From 7c750f79455e4b9d44c3d7f8f7ffe1d831f098cb Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Tue, 29 Nov 2011 14:23:47 +0000 Subject: Variables in separate scopes are now stored in different variable names when declared as such --- pp2cc.py | 125 ++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 33 deletions(-) diff --git a/pp2cc.py b/pp2cc.py index 742faaf..046ecf8 100755 --- a/pp2cc.py +++ b/pp2cc.py @@ -166,28 +166,104 @@ class Asm(object): """ return self.binary_op("LOAD", register, register, label) + " ; NOOP" +class Variables(object): + def __init__(self, defined_names, parent_variables): + """A scope for holding variable names + + Keywords arguments: + defined_names -- A list of defined variables to which additional + variables might be appended + parent_variables -- the parent Variables object. If None, it's a global + variable scope + """ + self.local_vars = {} + self.defined_names = defined_names + self.parent_variables = parent_variables + def uniqName(self, name): + """Returns an unique variable name""" + uniq_name = name + i = 0 + while uniq_name in self.defined_names: + uniq_name = name + "_" + str(i) + i += 1 + return uniq_name + def getName(self, name): + """Gets the name of a declared variable as it appears in the @DATA + section""" + if name in self.local_vars: + return self.local_vars[name] + if self.parent_variables: + return self.parent_variables.getName(name) + raise RuntimeError("Use of undefined variable '{}'".format(name)) + def declName(self, name): + """Declares a variable in the nearest scope and returns the label + name""" + if name in self.local_vars: + raise RuntimeError("Redeclaration of variable '{}'".format(name)) + # global variables are prefixed "var_", locals with "varl_" + var_name = "varl_" if self.parent_variables else "var_" + var_name += name + self.local_vars[name] = self.uniqName(var_name) + self.defined_names.append(var_name) + return var_name + class LinkedNode(object): """Stores nodes with a reference to the parent""" - def __init__(self, node, parent=None, level_increment=False): + def __init__(self, node, parent=None, level_increment=False, + defined_names=None): + """Holds properties for a node + + Keyword arguments: + node -- a Node object which is an object from the c_ast class + parent -- a parent LinkedNode object + level_increment -- True if the indentation level needs to be + incremented, False otherwise + defined_names -- a list of names which will be used in the @DATA + section. If not set, it'll be looked up in the parent + """ self.node = node + if parent: + assert isinstance(parent, LinkedNode), "parent is not a LinkedNode!" self.parent = parent self.function = None self.break_label = None self.continue_label = None self.type = type(node).__name__ self.level = 0 - # inherit level from parent + self.variables = None + parent_variables = None + # inherit level and variables from parent if parent: self.level = parent.level + self.variables = parent_variables = parent.variables + if not defined_names: + defined_names = parent.variables.defined_names + + if self.type == "Compound" or self.type == "FileAST": + # the node appears to have an own variable scope + if defined_names is None: + raise RuntimeError("No object found for storing variables") + self.variables = Variables(defined_names, parent_variables) + if not self.variables: + raise RuntimeError("No variables object found") if level_increment: self.incrementLevel() + def getScopeNode(self): + """Get the nearest node which introduces a new scope. + + If there is no node found an exception is raised because it expects at + least a global scope""" + if self.local_vars is not None: + return self + if self.parent: + return self.parent.getScopeNode() + raise RuntimeError("No global variable scope was found") def incrementLevel(self): self.level += 1 def getFunctionNode(self): if self.type == "FuncDef": return self if self.parent: - assert isinstance(self.parent, LinkedNode), "parent is not a LinkedNode!" return self.parent.getFunctionNode() return None def setFunction(self, function): @@ -200,14 +276,14 @@ class LinkedNode(object): def setBreak(self, break_label): """Marks this node as a loop or switch by setting the label for break - Keywords list: + Keywords arguments: break_label -- The label to continue when using the break keyword """ self.break_label = break_label def setContinue(self, continue_label): """Marks this node as a loop by setting the label for continue - Keywords list: + Keywords arguments: continue_label -- The label to continue when using the continue keyword """ self.continue_label = continue_label @@ -217,7 +293,6 @@ class LinkedNode(object): if self.break_label is not None: return self if self.parent: - assert isinstance(self.parent, LinkedNode), "parent is not a LinkedNode!" return self.parent.getBreakNode() return None def getContinueNode(self): @@ -225,7 +300,6 @@ class LinkedNode(object): if self.continue_label is not None: return self if self.parent: - assert isinstance(self.parent, LinkedNode), "parent is not a LinkedNode!" return self.parent.getContinueNode() return None @@ -283,10 +357,10 @@ class Parse(object): } self.binary_ops.update(self.comparison_ops) - # global variable names to be defined in @DATA - self.globalVars = [] + # local and global variable names to be defined in @DATA + self.varNames = [] self.functions = {} - # holds instructions for initializing the globalVars + # holds instructions for initializing the global variables self.globalInit = [] # holds the miscellaneous instructions for @CODE self.codeSegment = [] @@ -321,7 +395,7 @@ class Parse(object): self.labels.add(labelname) def compile(self): """Loops through the nodes in an AST syntax tree and generates ASM""" - root_node = LinkedNode(self.node) + root_node = LinkedNode(self.node, defined_names=self.varNames) for thing in self.node.ext: if not isinstance(thing, c_ast.Decl) and not isinstance(thing, c_ast.FuncDef): linked_node = LinkedNode(thing, parent=root_node) @@ -334,9 +408,9 @@ class Parse(object): """Retrieves the ASM source. You need to compile it first""" output = [] output.append("@DATA") - for varName in self.globalVars: + for varName in self.varNames: padding = " " * (16 - len(varName) - 1) - output.append(self.nameToVar(varName) + padding + " DS 1") + output.append(varName + padding + " DS 1") output.append("") output.append("@CODE") # initialization of global variables @@ -351,13 +425,6 @@ class Parse(object): output.append("@END") output.append("") return os.linesep.join(output) - def nameToVar(self, name): - """Returns the variable name which will be used in assembly - - Keyword list: - name -- This name will be returned with the var_ prefix - """ - return "var_" + name def parseFuncDef(self, linked_node): node = linked_node.node linked_node.incrementLevel() @@ -746,10 +813,9 @@ class Parse(object): raise RuntimeError("Unsupported constant type: " + node.type) return [self.asm.binary_op("LOAD", register, value)] def parseID(self, linked_node, register="R0"): - name = linked_node.node.name - if not name in self.globalVars: - raise RuntimeError("Use of undefined variable '{}'".format(name)) - var_name = self.nameToVar(name) + """Returns the name for a name""" + # XXX this is not made for functions + var_name = linked_node.variables.getName(linked_node.node.name) return [self.asm.binary_op("LOAD", register, "[GB+" + var_name + "]")] def parseIf(self, linked_node): linked_node.incrementLevel() @@ -941,10 +1007,7 @@ class Parse(object): lines.append(self.asm.binary_op("LOAD", register, "[" + register + "]")) elif linked_node.type == "ID": - name = linked_node.node.name - if not name in self.globalVars: - raise RuntimeError("Use of undefined variable '{}'".format(name)) - var_name = self.nameToVar(name) + var_name = linked_node.variables.getName(linked_node.node.name) lines.append(self.asm.binary_op("LOAD", register, "GB")) lines.append(self.asm.binary_op("ADD", register, var_name)) else: @@ -952,14 +1015,10 @@ class Parse(object): return lines def parseDecl(self, linked_node): lines = [] - varname = linked_node.node.name - if varname in self.globalVars: - raise RuntimeError("Redefinition of variable '{}'".format(varname)) - self.globalVars.append(varname) + var_name = linked_node.variables.declName(linked_node.node.name) # if the declaration also has a definition if linked_node.node.init: - var_name = self.nameToVar(varname) init_val = self.parseExpression(linked_node.node.init, linked_node) result_reg = self.registers.find_register(init_val) init_val.append(self.asm.binary_op("STOR", result_reg, "[GB+" + var_name + "]")) -- cgit v1.2.1