def __init__ (self, varalloc, validate=False): P0Flattener.__init__(self, varalloc) self.validate = validate
def flatten (self, node): """Takes an AST as input, and then "flattens" the tree into a list of statements.""" if isinstance(node, (Or,And)): # we only need to handle two operands to the "or" operator if self.validate and len(node.nodes) > 2: raise Exception("Only two operands supported in P1 for 'or' operator") lhsvar, lhsstmtlist = self.flatten(node.nodes[0]) rhsvar, rhsstmtlist = self.flatten(node.nodes[1]) varname = self.varalloc.get_next_var() result = Or([lhsvar,rhsvar]) if isinstance(node,Or) else And([lhsvar,rhsvar]) return (Name(varname), lhsstmtlist + rhsstmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], result)]) elif isinstance(node, Let): # first, flatten rhs (rhsvar, rhsstmtlist) = self.flatten(node.rhs) # now make sure that the variable specified by Let is assigned the result letassignstmt = [Assign([AssName(node.var.name, 'OP_ASSIGN')], rhsvar)] # now flatten the body and return # NOTE: there should not be ANY Let nodes after a flatten (bodyvar, bodystmtlist) = self.flatten(node.body) return (bodyvar, rhsstmtlist + letassignstmt + bodystmtlist) elif isinstance(node, (ProjectTo,InjectFrom)): (var, stmtlist) = self.flatten(node.arg) varname = self.varalloc.get_next_var() result = ProjectTo(node.typ, var) if isinstance(node,ProjectTo) else InjectFrom(node.typ, var) return (Name(varname), stmtlist + [Assign([AssName(varname,'OP_ASSIGN')], result)]) elif isinstance(node, GetTag): (var, stmtlist) = self.flatten(node.arg) varname = self.varalloc.get_next_var() return (Name(varname), stmtlist + [Assign([AssName(varname,'OP_ASSIGN')], GetTag(var))]) elif isinstance(node, List): stmts = [self.flatten(x) for x in node.nodes] varlist = [x for x,y in stmts] # convert the list of lists into a single list of statements stmtlist = reduce(lambda x,y: x+y, [y for x,y in stmts if y != []], []) varname = self.varalloc.get_next_var() return (Name(varname), stmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], List(varlist))]) elif isinstance(node, Dict): keys = [self.flatten(x[0]) for x in node.items] keyvarlist = [x for x,y in keys] keystmtlist = reduce(lambda x,y: x+y, [y for x,y in keys if y != []], []) values = [self.flatten(x[1]) for x in node.items] valuevarlist = [x for x,y in values] valuestmtlist = reduce(lambda x,y: x+y, [y for x,y in values if y != []], []) # keyvaluelist becomes a list of tuples, where each tuple is a key,value corresponding to the # temp variables for the key/value keyvaluelist = map(lambda x: (keyvarlist[x],valuevarlist[x]), range(0,len(keyvarlist))) varname = self.varalloc.get_next_var() return (Name(varname), keystmtlist + valuestmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], Dict(keyvaluelist))]) elif isinstance(node, IfExp): # Go ahead and flatten all expressions, including the test expression, as well as the # "then" and "else" expressions. vartes, test = self.flatten(node.test) vart, then = self.flatten(node.then) vare, else_ = self.flatten(node.else_) # Allocate a variable name, which will hold the result of the IfExp varname = self.varalloc.get_next_var() #test = [Assign([AssName(vartes, 'OP_ASSIGN')], varname)]+test1 # update the "then" and "else_" set of statements to include an # assignment to the allocated variable then = then + [Assign([AssName(varname, 'OP_ASSIGN')], vart)] else_ = else_ + [Assign([AssName(varname, 'OP_ASSIGN')], vare)] # We don't want to blindly execute both branches of the if, so instead we # encapsulate the flattened statements for the "then" and "else_" clauses in an If node. # The If node is then returned as a statement. This is expanded later into labels and # jumps. The test expression is always evaluated, so we have to include the corresponding # flattened statements. # NOTE: The If node has two attributes: "tests" and "else_". The tests attribute is # a list of tuples, where the first element in the tuple is the test expression and the # second element in the tuple is a Stmt object. Each tuple in the list corresponds to # an "if" or "elif" clause. The else_ attribute is a Stmt object corresponding to the # "else" clause. return (Name(varname), test + [If([(vartes, Stmt(then))], Stmt(else_))]) elif isinstance(node, Not): var, stmtlist = self.flatten(node.expr) tempvar = self.varalloc.get_next_var() return (Name(tempvar), stmtlist + [Assign([AssName(tempvar,'OP_ASSIGN')], var), Not(Name(tempvar))]) elif isinstance(node, Compare): # Only need to handle binary comparison operators. So if len(node.ops) > 1, its a syntax error. # For example, a == b == c is valid python, but invalid P1 if self.validate and len(node.ops) > 1: raise Exception('Only two operands supported in P1 for comparison operators') if self.validate and node.ops[0][0] not in ['==','!=','is']: raise Exception("'%s' is not a valid comparison operator in P1" % node.ops[0][0]) lhsvar, lhsstmtlist = self.flatten(node.expr) oper, rhs = node.ops[0] rhsvar, rhsstmtlist = self.flatten(rhs) varname = self.varalloc.get_next_var() return (Name(varname), lhsstmtlist + rhsstmtlist + [Assign([AssName(varname,'OP_ASSIGN')], Compare(lhsvar, [(oper, rhsvar)]))]) # overridden from p0flattener.py to handle arguments elif isinstance(node, CallFunc): # arguments can be arbitrary expressions, so we need to flatten those too. # this is a list of tuples: [(var1,stmtlist1), (var2,stmtlist2), ...] varstmtlist = [self.flatten(x) for x in node.args] #print varstmtlist # generate a temporary to store the result varname = self.varalloc.get_next_var() # convert the list of tuples to just a list of the variables; ditto for statements varlist = [x[0] for x in varstmtlist] #print varlist stmtlist = reduce(lambda x,y: x+y, [x[1] for x in varstmtlist], []) #print stmtlist # return a CallFunc with the variables substituted in return (Name(varname), stmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], CallFunc(node.node, varlist))]) elif isinstance(node, Subscript): # We only need to handle one subscript per the grammar, e.g, a[1,2] is invalid P1 # (a[1,2] is the only case where you get len(node.subs) > 1) if self.validate and len(node.subs) > 1: raise Exception('Only one subscript index supported in P1') exprvar, exprstmtlist = self.flatten(node.expr) subvar, substmtlist = self.flatten(node.subs[0]) varname = self.varalloc.get_next_var() return (Name(varname), exprstmtlist + substmtlist + [Assign([AssName(varname,'OP_ASSIGN')], Subscript(exprvar, node.flags, subvar))]) else: return P0Flattener.flatten(self, node)
def __init__(self, varalloc, validate=False): P0Flattener.__init__(self, varalloc) self.validate = validate
def flatten(self, node): """Takes an AST as input, and then "flattens" the tree into a list of statements.""" if isinstance(node, (Or, And)): # we only need to handle two operands to the "or" operator if self.validate and len(node.nodes) > 2: raise Exception( "Only two operands supported in P1 for 'or' operator") lhsvar, lhsstmtlist = self.flatten(node.nodes[0]) rhsvar, rhsstmtlist = self.flatten(node.nodes[1]) varname = self.varalloc.get_next_var() result = Or([lhsvar, rhsvar]) if isinstance(node, Or) else And( [lhsvar, rhsvar]) return (Name(varname), lhsstmtlist + rhsstmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], result)]) elif isinstance(node, Let): # first, flatten rhs (rhsvar, rhsstmtlist) = self.flatten(node.rhs) # now make sure that the variable specified by Let is assigned the result letassignstmt = [ Assign([AssName(node.var.name, 'OP_ASSIGN')], rhsvar) ] # now flatten the body and return # NOTE: there should not be ANY Let nodes after a flatten (bodyvar, bodystmtlist) = self.flatten(node.body) return (bodyvar, rhsstmtlist + letassignstmt + bodystmtlist) elif isinstance(node, (ProjectTo, InjectFrom)): (var, stmtlist) = self.flatten(node.arg) varname = self.varalloc.get_next_var() result = ProjectTo(node.typ, var) if isinstance( node, ProjectTo) else InjectFrom(node.typ, var) return (Name(varname), stmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], result)]) elif isinstance(node, GetTag): (var, stmtlist) = self.flatten(node.arg) varname = self.varalloc.get_next_var() return (Name(varname), stmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], GetTag(var))]) elif isinstance(node, List): stmts = [self.flatten(x) for x in node.nodes] varlist = [x for x, y in stmts] # convert the list of lists into a single list of statements stmtlist = reduce(lambda x, y: x + y, [y for x, y in stmts if y != []], []) varname = self.varalloc.get_next_var() return (Name(varname), stmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], List(varlist))]) elif isinstance(node, Dict): keys = [self.flatten(x[0]) for x in node.items] keyvarlist = [x for x, y in keys] keystmtlist = reduce(lambda x, y: x + y, [y for x, y in keys if y != []], []) values = [self.flatten(x[1]) for x in node.items] valuevarlist = [x for x, y in values] valuestmtlist = reduce(lambda x, y: x + y, [y for x, y in values if y != []], []) # keyvaluelist becomes a list of tuples, where each tuple is a key,value corresponding to the # temp variables for the key/value keyvaluelist = map(lambda x: (keyvarlist[x], valuevarlist[x]), range(0, len(keyvarlist))) varname = self.varalloc.get_next_var() return ( Name(varname), keystmtlist + valuestmtlist + [Assign([AssName(varname, 'OP_ASSIGN')], Dict(keyvaluelist))]) elif isinstance(node, IfExp): # Go ahead and flatten all expressions, including the test expression, as well as the # "then" and "else" expressions. vartes, test = self.flatten(node.test) vart, then = self.flatten(node.then) vare, else_ = self.flatten(node.else_) # Allocate a variable name, which will hold the result of the IfExp varname = self.varalloc.get_next_var() #test = [Assign([AssName(vartes, 'OP_ASSIGN')], varname)]+test1 # update the "then" and "else_" set of statements to include an # assignment to the allocated variable then = then + [Assign([AssName(varname, 'OP_ASSIGN')], vart)] else_ = else_ + [Assign([AssName(varname, 'OP_ASSIGN')], vare)] # We don't want to blindly execute both branches of the if, so instead we # encapsulate the flattened statements for the "then" and "else_" clauses in an If node. # The If node is then returned as a statement. This is expanded later into labels and # jumps. The test expression is always evaluated, so we have to include the corresponding # flattened statements. # NOTE: The If node has two attributes: "tests" and "else_". The tests attribute is # a list of tuples, where the first element in the tuple is the test expression and the # second element in the tuple is a Stmt object. Each tuple in the list corresponds to # an "if" or "elif" clause. The else_ attribute is a Stmt object corresponding to the # "else" clause. return (Name(varname), test + [If([(vartes, Stmt(then))], Stmt(else_))]) elif isinstance(node, Not): var, stmtlist = self.flatten(node.expr) tempvar = self.varalloc.get_next_var() return (Name(tempvar), stmtlist + [ Assign([AssName(tempvar, 'OP_ASSIGN')], var), Not(Name(tempvar)) ]) elif isinstance(node, Compare): # Only need to handle binary comparison operators. So if len(node.ops) > 1, its a syntax error. # For example, a == b == c is valid python, but invalid P1 if self.validate and len(node.ops) > 1: raise Exception( 'Only two operands supported in P1 for comparison operators' ) if self.validate and node.ops[0][0] not in ['==', '!=', 'is']: raise Exception( "'%s' is not a valid comparison operator in P1" % node.ops[0][0]) lhsvar, lhsstmtlist = self.flatten(node.expr) oper, rhs = node.ops[0] rhsvar, rhsstmtlist = self.flatten(rhs) varname = self.varalloc.get_next_var() return (Name(varname), lhsstmtlist + rhsstmtlist + [ Assign([AssName(varname, 'OP_ASSIGN')], Compare(lhsvar, [(oper, rhsvar)])) ]) # overridden from p0flattener.py to handle arguments elif isinstance(node, CallFunc): # arguments can be arbitrary expressions, so we need to flatten those too. # this is a list of tuples: [(var1,stmtlist1), (var2,stmtlist2), ...] varstmtlist = [self.flatten(x) for x in node.args] #print varstmtlist # generate a temporary to store the result varname = self.varalloc.get_next_var() # convert the list of tuples to just a list of the variables; ditto for statements varlist = [x[0] for x in varstmtlist] #print varlist stmtlist = reduce(lambda x, y: x + y, [x[1] for x in varstmtlist], []) #print stmtlist # return a CallFunc with the variables substituted in return (Name(varname), stmtlist + [ Assign([AssName(varname, 'OP_ASSIGN')], CallFunc(node.node, varlist)) ]) elif isinstance(node, Subscript): # We only need to handle one subscript per the grammar, e.g, a[1,2] is invalid P1 # (a[1,2] is the only case where you get len(node.subs) > 1) if self.validate and len(node.subs) > 1: raise Exception('Only one subscript index supported in P1') exprvar, exprstmtlist = self.flatten(node.expr) subvar, substmtlist = self.flatten(node.subs[0]) varname = self.varalloc.get_next_var() return (Name(varname), exprstmtlist + substmtlist + [ Assign([AssName(varname, 'OP_ASSIGN')], Subscript(exprvar, node.flags, subvar)) ]) else: return P0Flattener.flatten(self, node)
from comp_util import * from p0parser import P0Parser from p0flattener import P0Flattener from p0insselector import P0InstructionSelector if len(sys.argv) < 2: sys.exit(1) # configure logging logging.config.fileConfig('logging.cfg') testcases = sys.argv[1:] for testcase in testcases: parser = P0Parser() parser.build() #ast = compiler.parseFile(testcase) ast = parser.parseFile(testcase) varalloc = VariableAllocator() p0flattener = P0Flattener(varalloc) stmtlist = p0flattener.flatten(ast) instruction_selector = P0InstructionSelector(varalloc) program = instruction_selector.visit(stmtlist) regallocator = P0RegAllocator(program, varalloc) print regallocator.substitute() #import cProfile as profile #import pstats #output_file = 'profile.out' #profile.run('regallocator.substitute()', output_file) #p = pstats.Stats(output_file) #print "name: " #print p.sort_stats('name') #print "all stats: " #p.print_stats() #print "cumulative (top 10): "