summaryrefslogtreecommitdiff
path: root/pp2cc.py
diff options
context:
space:
mode:
authorPeter Wu <lekensteyn@gmail.com>2011-11-29 14:23:47 +0000
committerPeter Wu <lekensteyn@gmail.com>2011-11-29 14:23:47 +0000
commit7c750f79455e4b9d44c3d7f8f7ffe1d831f098cb (patch)
tree909871df91942a260f38ae3730880b7de8902b98 /pp2cc.py
parent82396a5e3f6020345bbbabbc03295679bc0621fb (diff)
downloadpp2cc-7c750f79455e4b9d44c3d7f8f7ffe1d831f098cb.tar.gz
Variables in separate scopes are now stored in different variable names when declared as such
Diffstat (limited to 'pp2cc.py')
-rwxr-xr-xpp2cc.py125
1 files 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 + "]"))