def block(lst, withResult, prepend=None): if len(lst) == 0: if withResult: return ast.Const(None) else: return ast.Pass() def stm(x): if hasResult(x): dis = ast.Discard(topy(x)) dis.lineno = getmeta(x, 'lineno') return dis else: return topy(x) if withResult: stms = [stm(s) for s in lst[:-1]] last = lst[-1] lastpy = topy(last) if hasResult(last): # Insert a pass so pycodegen emits a SET_LINENO p = ast.Pass() p.lineno = getmeta(last, 'lineno') statements = stms + [p, lastpy] else: statements = stms + [lastpy, ast.Const(None)] else: statements = [stm(s) for s in lst] if prepend: return ast.Stmt(prepend + statements) else: return ast.Stmt(statements)
def bind(self, expr, name): """ Generates code for binding a name to a value in the rule's locals dict. """ return ast.Stmt([ ast.Assign([ast.Subscript(ast.Name('__locals'), 'OP_ASSIGN', [ast.Const(name)])], expr), ast.Subscript(ast.Name('__locals'), 'OP_APPLY', [ast.Const(name)])])
def apply(self, ruleName, codeName='', *exprs): """ Create a call to self.apply(ruleName, *args). """ args = [self.compilePythonExpr(codeName, arg) for arg in exprs] if ruleName == "super": return ast.CallFunc(ast.Getattr(ast.Name("self"), "superApply"), [ast.Const(codeName)] + args, None, None) return ast.CallFunc(ast.Getattr(ast.Name("self"), "apply"), [ast.Const(ruleName)] + args, None, None)
def visitConst(self, node): if isinstance(node.value, str): try: # If the string is ASCII, return a `str` object node.value.decode('ascii') except ValueError: # Otherwise return a `unicode` object return ast.Const(node.value.decode('utf-8')) return node
def _return(self, val): if val is None: pyval = ast.Const(None) else: assertResult(val, "return") pyval = topy(val) return ast.Return(pyval)
def compileDeflang(self, name, parentx, body): assert isinstance(name, Symbol) if parentx: assertResult(parentx, "derive language from") parent = topy(parentx) else: parent = logixglobal('langlang') funcname = "#lang:%s" % name body = len(body) > 0 and block(body, False).nodes or [] funcbody = ast.Stmt( body + [ast.CallFunc(ast.Getattr(ast.Getattr(ast.Name(str(name)), '__impl__'), 'addDeflangLocals'), [ast.CallFunc(ast.Name('locals'), [])])]) res = ast.Stmt([ ast.Assign([compilePlace(Symbol(name))], ast.CallFunc(logixglobal('defLanguage'), [ast.Const(str(name)), parent, ast.CallFunc(GlobalName("globals"), [])])), astFunction(funcname, tuple(), tuple(), 0, None, funcbody), ast.CallFunc(ast.Name(funcname), []), ast.AssName(funcname, 'OP_DELETE')]) lineno = getmeta(self, 'lineno') for n in res.nodes: n.lineno = lineno res.lineno = lineno return res
def visitAssert(self, node): end = Label() self.visit(node.test) self.emit('jumpif', end) self.visit(node.fail or ast.Const(None)) self.emit('assertfail') self.emit(end)
def Symbol(symbol): if symbol == data.true: return ast.Const(True) elif symbol == data.false: return ast.Const(False) elif symbol == data.none: return ast.Const(None) elif symbol.namespace == "": return ast.Name(symbol.name) else: debug() raise CompileError, "can't compile symbol with namespace: %s" % symbol
def flist(obj): for x in obj: assertResult(x, 'use in flist') return ast.CallFunc(ast.Getattr(logixglobal('flist'), 'new'), [ast.List(map(topy, obj.elems)), ast.Dict([(ast.Const(n), topy(v)) for n,v in obj.items()])])
def compileGetOp(op): lang = op.__language__ opmodname = op.__language__.__module__ if opmodname == modulename: langexpr = GlobalName(lang.__impl__.name) else: langexpr = ast.Getattr(ast.Subscript(logixglobal('lmodules'), 'OP_APPLY', [ast.Const(opmodname)]), lang.__impl__.name) return ast.Subscript( ast.Getattr(ast.Getattr(langexpr, '__impl__'), 'operators'), 'OP_APPLY', [ast.Const(op.__syntax__.token)])
def compileGetops(doc): fromlang = doc[0] targetlang = doc.get("lang") ops = [op.strip() for op in doc[1:]] if targetlang is None: raise CompileError("No target language for getops" "(getops in non-exec parse?)") assertResult(fromlang, "get operators from") lineno = getattr(doc, 'lineno', None) lastdot = targetlang.rfind('.') if lastdot == -1: def targetlangexpr(): return GlobalName(targetlang) else: assert 0, ("PROBE: Why are we getting ops into an imported language?") langname = targetlang[lastdot+1:] langmod = targetlang[:lastdot] def targetlangexpr(): return ast.Getattr(ast.Subscript(logixglobal('lmodules'), 'OP_APPLY', [ast.Const(langmod)]), langname) if len(ops) == 0: # get all operators res = ast.CallFunc(ast.Getattr(ast.Getattr(targetlangexpr(), '__impl__'), 'addAllOperators'), [topy(fromlang)]) else: stmts = [ast.CallFunc(ast.Getattr(ast.Getattr(targetlangexpr(), '__impl__'), 'addOp'), [ast.CallFunc(ast.Getattr(ast.Getattr(topy(fromlang), "__impl__"), "getOp"), [ast.Const(op), ast.Const(False)])]) for op in ops] for s in stmts: s.lineno = lineno res = ast.Stmt(stmts) res.lineno = lineno return res
def visitAugAssign(self, node): if isinstance(node.node, ast.Name) and ( not self.locals or node.node.name not in flatten(self.locals)): name = node.node.name node.node = ast.Subscript(ast.Name('data'), 'OP_APPLY', [ast.Const(name)]) node.expr = self.visit(node.expr) return ast.If( [(ast.Compare(ast.Const(name), [('in', ast.Name('data'))]), ast.Stmt([node]))], ast.Stmt([ ast.Raise( ast.CallFunc(ast.Name('UndefinedError'), [ast.Const(name)]), None, None) ])) else: return ASTTransformer.visitAugAssign(self, node)
def exactly(self, expr): """ Create a call to self.exactly(expr). """ return ast.CallFunc(ast.Getattr(ast.Name("self"), "exactly"), [ast.Const(expr)], None, None)
def visitName(self, node): # If the name refers to a local inside a lambda, list comprehension, or # generator expression, leave it alone if node.name not in flatten(self.locals): # Otherwise, translate the name ref into a context lookup func_args = [ast.Name('data'), ast.Const(node.name)] node = ast.CallFunc(ast.Name('_lookup_name'), func_args) return node
def visitPower(self, node, *args): walk(node.left, self) walk(node.right, self) cnode = ast.CallFunc(ast.Name('pow'), [node.left, node.right], None, None) node.left = cnode # Little hack: instead of trying to turn node into a CallFunc, we just do pow(left, right)**1 node.right = ast.Const(1)
def block(lst, withResult, prepend=None): if len(lst) == 0: if withResult: return ast.Const(None) else: return ast.Pass() def stm(x): if hasResult(x): dis = ast.Discard(topy(x)) dis.lineno = getattr(x, 'lineno', None) return dis else: return topy(x) if withResult: stms = [stm(s) for s in lst[:-1]] last = lst[-1] lastpy = topy(last) if hasResult(last): # Insert a pass so pycodegen emits a SET_LINENO p = ast.Pass() p.lineno = getattr(last, "lineno", None) statements = stms + [p, lastpy] else: statements = stms + [lastpy, ast.Const(None)] else: statements = [stm(s) for s in lst] # HACK: No lineno info was emitted for "del a[0]" statements # (insert a Pass to fix that) s2 = [] for s in statements: if isinstance(s, ast.Subscript) and s.flags == "OP_DELETE" and hasattr(s, "lineno"): p = ast.Pass() p.lineno = s.lineno s2.append(p) s2.append(s) if prepend: return ast.Stmt(prepend + s2) else: return ast.Stmt(s2)
def sequence(self, exprs): """ Creates a sequence of exprs, returning the value of the last one. """ if len(exprs) > 0: stmtExprs = [ast.Discard(e) for e in exprs[:-1]] + [exprs[-1]] return ast.Stmt(stmtExprs) else: return ast.Const(None)
def atom_string(self, nodelist): k = '' for node in nodelist: k = k + eval(node[1]) n = ast.Const(k) if self.__template_type and self.__template_type[-1] == "html": # change "foo" to _q_htmltext("foo") n = ast.CallFunc(ast.Name(MARKUP_MANGLED_CLASS), [n]) return n
def quote(obj, depth=1): assert depth > 0 if isinstance(obj, rootops.quote): depth += 1 elif isinstance(obj, rootops.escape): extra = obj.__operands__.get('extra', []) escapelevel = 1 + len(extra) if escapelevel > depth: raise CompileError("invalid quote escape") elif escapelevel == depth: if obj.__operands__.hasField('splice'): raise CompileError("Can't splice here") elif obj.__operands__.hasField("localmodule"): return topy(localModuleQuote()) else: assertResult(obj[0], "insert into quote") return topy(obj[0]) if isinstance(type(obj), OperatorType): # {{{ generate code to build the operator cls = obj.__class__ if cls.__module__ == '%s.rootops' % logixModuleName: classx = ast.Getattr(logixglobal('rootops'), cls.__name__) else: optoken = obj.__class__.__syntax__.token classx = compileGetOp(obj) operands = macros.gensym("operands") return ast.Stmt([ast.Assign([compilePlace(operands)], quotedArgs(obj.__operands__, depth)), ast.CallFunc(classx, [], ast.Getattr(topy(operands), "elems"), ast.Getattr(topy(operands), "fields"))]) # }}} elif isinstance(obj, flist): # {{{ generate code to build the flist return quotedArgs(obj, depth) # }}} elif isinstance(obj, Symbol): # {{{ generate code to build the symbol return ast.CallFunc(logixglobal('Symbol'), [ast.Const(obj.asStr())]) # }}} elif isinstance(obj, (tuple, list, dict)): # Q: Is this ok? assert 0, "didn't expect one of those to be quoted" else: return topy(obj)
def visitListCompFor(self, node): self.set_lineno(node) start = self.newBlock() anchor = self.newBlock() self.visit(node.list) self.visit(ast.Const(0)) self.emit('SET_LINENO', node.lineno) self.nextBlock(start) self.emit('FOR_LOOP', anchor) self.visit(node.assign) return start, anchor
def compileGetOp(symbol): lang = language.getLanguage(symbol.namespace) if lang is None: debug() raise CompileError, "not an operator symbol: %s" % symbol opmodname = lang.__module__ if opmodname == modulename: langexpr = GlobalName(lang.__impl__.name) else: langexpr = ast.Getattr(ast.Subscript(logixglobal('lmodules'), 'OP_APPLY', [ast.Const(opmodname)]), lang.__impl__.name) return ast.Subscript( ast.Getattr(ast.Getattr(langexpr, '__impl__'), 'operators'), 'OP_APPLY', [ast.Const(symbol.name)])
def _generate(node): if node.type == node.TERM: return ast.Compare(ast.Const(node.value), [('in', ast.Name('text'))]) elif node.type == node.AND: return ast.And([_generate(node.left), _generate(node.right)]) elif node.type == node.OR: return ast.Or([_generate(node.left), _generate(node.right)]) elif node.type == node.NOT: return ast.Not(_generate(node.left)) elif node.type == node.ATTR: raise NotImplementedError
def function(self, name, expr): """ Create a function of one argument with the given name returning the given expr. @param name: The function name. @param expr: The AST to insert into the function. """ fexpr = ast.Stmt([ast.Assign([ast.AssName('__locals', 'OP_ASSIGN')], ast.Dict([(ast.Const('self'), ast.Name('self'))])), ast.Assign([ast.Subscript(ast.Getattr( ast.Name('self'), 'locals'), 'OP_ASSIGN', [ast.Const( name.split('_',1)[1])])], ast.Name('__locals')), expr]) f = ast.Lambda(['self'], [], 0, fexpr) f.filename = self.name return f
def compilePythonExpr(self, name, expr): """ Compile an embedded Python expression. @param name: The current rule name. @param expr: The Python expression to compile. """ c = python_compile(expr, "<grammar rule %s>" % (name,), "eval") return ast.Stmt([ ast.CallFunc(ast.Name('eval'), [ast.Const(c), ast.Getattr(ast.Name("self"), "globals"), ast.Name('__locals')])])
def _def(self, name, body, args=None): flags, argnames, defaults = funcArgs(args) if len(body) > 0: bod = block(body, True) stmts = bod.nodes if len(stmts) > 1 and isinstance(stmts[0], ast.Pass): first = 1 else: first = 0 stmt1 = stmts[first] if isinstance(stmt1, ast.Const) and isinstance(stmt1.value, str): doc = stmt1.value del stmts[:first+1] if len(stmts) == 0: bod = ast.Const(None) else: doc = None else: bod = ast.Const(None) doc = None return astFunction(str(name), argnames, defaults, flags, doc, ast.Return(bod))
def compileCodeObjects(filename, codeobjs): if len(codeobjs) == 0: stmts = [] else: stmts = [ast.For(ast.AssName('[--codeobj--]', 'OP_ASSIGN'), ast.Const(codeobjs), ast.Stmt([ast.Exec(ast.Name('[--codeobj--]'), None, None)]), None), ast.AssName('[--codeobj--]', 'OP_DELETE')] module = ast.Module(None, ast.Stmt(stmts)) compiler.misc.set_filename(filename, module) return pycodegen.ModuleCodeGenerator(module).getCode()
def _if(self, tests, else_=None): for test in tests: assertResult(test['test'], "use as if test") tests = [(el['test'], el['body']) for el in tests] pytests = [] for t,b in tests: pyt = topy(t) pyt.lineno = getattr(t, 'lineno', None) pytests.append( (pyt, block(b, True)) ) # HACK: For line numbers. Workaround for a test with no metadata # (e.g. just a symbol) # TODO: make this work for elifs (it doesn't) if pytests[0][0].lineno is None: pytests[0][0].lineno = getattr(self, 'lineno', None) if else_: else_ = block(else_, True) return ast.If(pytests, else_ or ast.Const(None))
def visitFor(self, node): start = self.newBlock() anchor = self.newBlock() after = self.newBlock() self.loops.push(start) self.set_lineno(node) self.emit('SETUP_LOOP', after) self.visit(node.list) self.visit(ast.Const(0)) self.nextBlock(start) self.set_lineno(node) self.emit('FOR_LOOP', anchor) self.visit(node.assign) self.visit(node.body) self.emit('JUMP_ABSOLUTE', start) self.nextBlock(anchor) self.emit('POP_BLOCK') if node.else_: self.visit(node.else_) self.loops.pop() self.nextBlock(after)
def compileGetops(self, fromlang, *ops, **kw): targetlang = kw.get("lang") if targetlang is None: raise CompileError("No target language for getops" "(getops in non-exec parse?)") assertResult(fromlang, "get operators from") lineno = getmeta(self, 'lineno') lastdot = targetlang.rfind('.') if lastdot == -1: targetlangexpr = GlobalName(targetlang) else: assert 0, ("PROBE: Why are we getting ops into an imported language?") langname = targetlang[lastdot+1:] langmod = targetlang[:lastdot] targetlangexpr = ast.Getattr(ast.Subscript(logixglobal('lmodules'), 'OP_APPLY', [ast.Const(langmod)]), langname) if len(ops) == 0: # get all operators res = ast.CallFunc(ast.Getattr(ast.Getattr(targetlangexpr, '__impl__'), 'addAllOperators'), [topy(fromlang)]) else: stmts = [ast.CallFunc(ast.Getattr(ast.Getattr(targetlangexpr, '__impl__'), 'addOp'), [compileGetOp(op)]) for op in ops] for s in stmts: s.lineno = lineno res = ast.Stmt(stmts) res.lineno = lineno return res
def _if(self, test, body, elifs=None, _else=None): assertResult(test, "use as if test") if elifs: for el in elifs: assertResult(el['test'], "use as elif test") tests = [(test, body)] + [(el['test'], el['body']) for el in elifs] else: tests = [(test, body)] pytests = [] for t,b in tests: pyt = topy(t) pyt.lineno = getmeta(t, 'lineno') pytests.append( (pyt, block(b, True)) ) # HACK: For line numbers. Workaround for a test with no metadata # (e.g. just a symbol), but this doesn't work for elifs if pytests[0][0].lineno is None: pytests[0][0].lineno = getmeta(self, 'lineno') if _else: _else = block(_else, True) return ast.If(pytests, _else or ast.Const(None))