def __init__(self, *expressions, **kwargs): super(Debug, self).__init__() self.expr = Plus() for e in expressions: if not isinstance(e, Expression): e = Literal(e) self.expr.append(e) if kwargs.get('nl', True): self.expr.append(Literal('\n'))
def expr(obj): from js2esi.node.expression import Expression from js2esi.node.literal import Literal if obj is None: return None if isinstance(obj, Expression): return obj return Literal(obj)
def __js__(self, ctxt): # TODO: i think this can be made into a single "printv()" call... # however, it should inspect the number & size of the output # to see if it should resort to the repetetive invocation... # note: the reason to call __js__() instead of js() is that js() # will inject an artificial 'Block' into the node hierarchy. from js2esi.node.expression import FunctionCall from js2esi.node.literal import Literal if len(self.statements) == 1 and not self.raw: return Block( FunctionCall(self.vars and 'printv' or 'print', self.statements[0])).__js__(ctxt) sts = [ FunctionCall(self.raw and 'printraw' or 'print', e) for e in self.statements ] if self.vars: sts.insert(0, FunctionCall('printraw', Literal('<esi:vars>'))) sts.append(FunctionCall('printraw', Literal('</esi:vars>'))) return Block(*sts).__js__(ctxt)
def outdent(): return IfDebug( Assign( 'node_indent', Function('substr', Var('node_indent'), Literal(0), Literal(-2))))
def indent(): return IfDebug( Assign('node_indent', Plus(Var('node_indent'), Literal(' '))))
def __init__(self): super(DebugBlock, self).__init__() self.init = Block(Assign('node_debug', ''), Assign('node_indent', '')) self.term = Function('set_response_code', Literal(444), Var('node_debug'))
def optimize(self, level=7): ''' Optimize this node node (and all of it\'s children). The ``level`` parameter indicates how aggressively to optimize the tree - with the trade-off being time-to-finish. Currently, the following levels exist: 3+: collapse all literals, e.g. 3+4 becomes 7 5+: resolve inline functions 6+: (TODO) inline hardcoded variable values (but keep declarations) 7+: (TODO) examine all functions for inline-ability, but keep definitions around (in case this script gets included via "eval") 8: (TODO) remove auto-inlined functions and unused variables 9: (TODO) rename functions and variables to be shorter ''' from js2esi.node import util from js2esi.node.function import FunctionDefinition from js2esi.node.expression import FunctionCall, Operator, Not from js2esi.node.literal import Literal ret = self if level < 5: # un-inline all inline functions for fdef in util.allchildren(ret, FunctionDefinition): fdef.inline = False else: inlines = {} for fdef in util.allchildren(ret, FunctionDefinition, lambda f: f.inline): inlines[fdef.name] = fdef # tbd: see below for comments on better ways of detecting recursively # inlined functions... instead of states, i could use a stack of # inlining functions. # TODO: this process seems *much* more processor intensive that it needs to # be... need to review exactly why this is necessary... # process: # - first iteratively resolve inlines within inlined function definitions # (this is to create completely self-contained inlined function defs) # note that this loop is so that at each step, inlining only happens # for function calls to function definitions that do not, in turn, also # inline. this is for two reasons: a) prevent recursive loops, and b) # keep the inlining process simple since i don't need to worry about # recursive inlining. # - then resolve inlines everywhere else count = 0 changed = True while changed: count += 1 changed = False if count > 1000: raise StructureError( 'resolving inlined functions appears to have entered an infinite loop' ) for fdef in inlines.values(): # print >>sys.stderr,'check-inline:',fdef for fcall in util.allchildren( fdef, FunctionCall, lambda fc: fc.name in inlines): ok2inline = True subfdef = inlines[fcall.name] for subfcall in util.allchildren( subfdef, FunctionCall, lambda fc: fc.name in inlines): ok2inline = False # print >>sys.stderr,'!ok2inline: %s => %s' % (fdef.name, subfdef.name) break if not ok2inline: continue subfdef.inlineInto(fcall) changed = True if changed: ret = _resolveProxies(ret) # TODO: do i need to rebuild the inlines dictionary?... # check that all inlined functions are self-contained... for fdef in inlines.values(): for fcall in util.allchildren(fdef, FunctionCall, lambda fc: fc.name in inlines): raise StructureError( 'recursive inlined function %s() detected' % (fdef.name, )) while True: # tbd: do i need an infinite loop check? changed = False for call in util.allchildren(ret, FunctionCall, lambda fc: fc.name in inlines): inlines[call.name].inlineInto(call) changed = True ret = _resolveProxies(ret) break if not changed: break if level >= 3: # collapse literals # tbd: instead of just limiting the loops to 1000, should # i detect thrashing?... ie.: # initial state A. # first loop: state A changes to state B. # second loop: state B changes to state A. # and we are now in an infinite loop... # instead of limiting this to 1000 loops, should i # detect equivalent states?... count = 0 changed = True while changed: count += 1 if count > 1000: raise StructureError( 'collapsing literals appears to have entered an infinite loop' ) changed = False for op in util.allchildren(ret, Operator): if isinstance(op, Not): # TODO: !(Literal(boolean)) can be optimized... continue # TODO: handle other operators, such as bitwise?... # TODO: better strategy: do a "try" with python... if it succeeds, # use that, otherwise just leave it as is... note that then # i could combine types, for example ('-' * 6) ==> '------' handlers = { '+': lambda a, b: a + b, '-': lambda a, b: a - b, '*': lambda a, b: a * b, '/': lambda a, b: a / b, '%': lambda a, b: a % b, } if op.op not in handlers: continue lits = None for arg in op.args: if not isinstance(arg, Literal): lits = None break if lits is None: lits = arg.type else: if lits != arg.type: lits = None break if lits is None: continue # tbd: i should really use map/reduce here... val = op.args[0].value for arg in op.args[1:]: val = handlers[op.op](val, arg.value) op._proxy = Literal( str(val)[-2:] == '.0' and int(val) or val) changed = True ret = _resolveProxies(ret) return ret
def __js__(self, ctxt): ctxt.write(ctxt.indent + 'require(') Literal(self.src).js(ctxt) if self.force: ctxt.write(', force=true') ctxt.write(');\n')