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 quotedArgs(operands, depth): """ Generate code from an flist of operand expressions, possibly containing splices. Returns an expression that constructs an flist. """ parts = [] for x in operands.elems: if isinstance(x, rootops.escape): extra = x.__operands__.get('extra', []) escapelevel = 1 + len(extra) if escapelevel > depth: raise CompileError("invalid quote escape") escape = escapelevel == depth else: escape = False if escape: if x.__operands__.hasField("splice"): assertResult(x[0], "insert into quote") parts.append( ('s', topy(x[0])) ) # 's' == splice elif x.__operands__.hasField("localmodule"): parts.append( ('v',topy(localModuleQuote())) ) else: assertResult(x[0], "insert into quote") parts.append( ('v', topy(x[0])) ) # 'v' == plain value else: parts.append( ('v', quote(x, depth)) ) # 'v' == plain value # {{{ expr = reduce parts to a single expression # If there is just a single splice, then that is the expression # otherwise generate: val = ??; val.extend(??); val.extend(??)... def frontSection(parts): vals = list(itools.takewhile(lambda (tag, exp): tag == 'v', parts)) if len(vals) == 0: return parts[0][1], parts[1:] else: return ast.List([v[1] for v in vals]), parts[len(vals):] if len(parts) == 0: expr = ast.List([]) else: first, rest = frontSection(parts) if len(rest) == 0: expr = first else: # Generate: # val = ...; if not hasattr(val, 'extend'): val = list(val) val = macros.gensym("val") statements = [ ast.Assign([compilePlace(val)], first), ast.If([(ast.CallFunc(GlobalName('isinstance'), [topy(val), logixglobal("flist")]), ast.Assign([compilePlace(val)], ast.CallFunc(ast.Getattr(topy(val), "copy"), [])))], #else ast.Assign([compilePlace(val)], ast.CallFunc(GlobalName('list'), [topy(val)])))] while len(rest) > 0: ex, rest = frontSection(rest) statements.append(ast.Discard(ast.CallFunc(ast.Getattr(topy(val), "extend"), [ex]))) statements.append(topy(val)) expr = ast.Stmt(statements) # }}} for v in operands.fields.values(): assertResult(v, "use as operand") keywords = ast.Dict([(ast.Const(n), quote(v, depth)) for n, v in operands.items()]) if isinstance(expr, ast.List): return ast.CallFunc(ast.Getattr(logixglobal("flist"), 'new'), [expr, keywords]) else: return ast.Add([expr, ast.CallFunc(ast.Getattr(logixglobal("flist"), 'new'), [ast.List([]), keywords])])
def quotedDoc(doc, depth): """ Generate code to build a Doc like `doc` with quote-escapes replaced by runtime values. """ # Each element of contentParts is either: # an AST - meaning a spliced value - the resulting content # should be `extend`ed by that value # a list of ASTs - a list of regular elements, the resulting # content should be `extend`ed by an ast.List # with these elements # {{{ contentParts = contentParts = [[]] for x in doc.content(): if isDoc(x, rootops.escape): escapelevel = 1 + len(x.get('extra', [])) if escapelevel > depth: raise CompileError("more quote escapes than quotes") escape = escapelevel == depth else: escape = False if escape: if x.hasProperty("splice"): assertResult(x[0], "insert into quote") contentParts.append(topy(x[0])) contentParts.append([]) elif x.hasProperty("localmodule"): contentParts[-1].append(topy(localModuleQuote())) else: assertResult(x[0], "insert into quote") contentParts[-1].append(topy(x[0])) else: contentParts[-1].append(quote(x, depth)) # }}} # These properties are added to the result doc *after* # any spliced in docs, so they overwrite any spliced properties for v in doc.propertyValues(): assertResult(v, "use as operand") properties = ast.Dict([(compileSymbol(n), quote(v, depth)) for n, v in doc.properties()]) # assert isinstance(contentParts[0], list) if contentParts == [[]]: return ast.CallFunc(logixglobal("Doc"), [compileSymbol(doc.tag), properties]) else: if len(contentParts[0]) > 0: docArg = ast.List(contentParts[0]) rest = contentParts[1:] elif len(contentParts) > 0: docArg = contentParts[1] rest = contentParts[2:] if len(rest) == 0: return ast.CallFunc(logixglobal("Doc"), [compileSymbol(doc.tag), docArg, properties]) else: val = macros.gensym("val") stmts = [ast.Assign([compilePlace(val)], ast.CallFunc(logixglobal("Doc"), [compileSymbol(doc.tag), docArg]))] for part in rest: if isinstance(part, list): if len(part) == 0: continue ext = ast.List(part) else: ext = part stmts.append(ast.Discard(ast.CallFunc(ast.Getattr(topy(val), "extend"), [ext]))) stmts.append(ast.Discard(ast.CallFunc(ast.Getattr(topy(val), "extend"), [properties]))) stmts.append(topy(val)) return ast.Stmt(stmts)