diff options
Diffstat (limited to 'LinkedNode.py')
-rw-r--r-- | LinkedNode.py | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/LinkedNode.py b/LinkedNode.py new file mode 100644 index 0000000..864f708 --- /dev/null +++ b/LinkedNode.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python +"""Compiles C into assembly for the practicum processor (PP2) + +All rights reserved, you may not redistribute or use this program without prior +permission from Peter Wu or Xander Houtman. Use of this program is entirely +your own risk. In no circumstances can the authors of this program be held +responsible for any damage including, but not limited to, financial damage or +data loss. Modification of this program is not allowed without prior +permission. The generated output (assembly and messages) are not subject to +this license. +""" + +__author__ = "Peter Wu" +__copyright__ = "Copyright 2011, Peter Wu" +__credits__ = ["Peter Wu"] +__license__ = "Proprietary" +__version__ = "1.0" +__maintainer__ = "Peter Wu" +__email__ = "uwretep@gmail.com" + +from Variables import Variables + +class LinkedNode(object): + """Stores nodes with a reference to the parent""" + 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 + self.variables = None + # for supporting post increment and post decrement + self.post_lines = [] + 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 + + # for is added for the init part (C99) + if self.type in ("Compound", "FileAST", "For"): + # 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) + # Identifiers which are in use (think of variables and labels) + self.defined_names = defined_names + if not self.variables: + raise RuntimeError("No variables object found") + if level_increment: + self.incrementLevel() + # labels are limited to function contexts + if self.type == "FuncDef": + self.goto_labels = {} + def handle_post_lines(self, lines): + """Add post-increment lines to the lines list and clear the queue""" + lines += self.post_lines + self.post_lines = [] + 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 isTypeStatement(self): + """Returns True if the node is a statement type""" + return self.type in ("Compound", "If", "Return", "DoWhile", "While", + "For", "Decl", "FuncDef", "Break", "Continue", + "EmptyStatement", "Switch", "DeclList", + "FuncDecl", "ArrayDecl", "Case", + "Default", "EllipsisParam",# (int a, ...) + "Enum", # enum type + "Enumerator", # enum value + "EnumeratorList", # list of enum values + "FuncDecl", "Goto", "Label", "ParamList", "PtrDecl", "Struct", + "TypeDecl", "Typedef", "Union") + def getStatementNode(self): + """Returns the nearest LinkedNode which is a statement node type""" + if self.isTypeStatement(): + return self + if self.parent: + return self.parent.getStatementNode() + return None + def incrementLevel(self): + self.level += 1 + def getFunctionNode(self): + """Returns the nearest LinkedNode which is a function definition node + type""" + if self.type == "FuncDef": + return self + if self.parent: + return self.parent.getFunctionNode() + return None + def setFunction(self, function): + """Sets the function object containing label information""" + self.function = function + def getLocation(self): + if hasattr(self.node, "coord"): + return self.node.coord + return "Unknown" + def setBreak(self, break_label): + """Marks this node as a loop or switch by setting the label for break + + 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 arguments: + continue_label -- The label to continue when using the continue keyword + """ + self.continue_label = continue_label + def getBreakNode(self): + """Returns the label to the end of the nearest switch statement or for + loop""" + if self.break_label is not None: + return self + if self.parent: + return self.parent.getBreakNode() + return None + def getContinueNode(self): + """Returns the label to the label to continue a loop""" + if self.continue_label is not None: + return self + if self.parent: + return self.parent.getContinueNode() + return None + def setLabel(self, label_name): + """Sets the label for this node and return the label name as it appears + in assembly + """ + if self.parent: + function = self.parent.getFunctionNode() + if not self.parent or not function: + raise RuntimeError("Labels are only allowed in functions") + if label_name in function.goto_labels: + raise RuntimeError("Duplicate label '{}'".format(label_name)) + + label_asm = "lbl_" + label_name + i = 0 + while label_asm in self.defined_names: + label_asm = "lbl_" + label_name + str(i) + i += 1 + + function.goto_labels[label_name] = label_asm + return label_asm + def lookupLabel(self, label_name): + """Returns the label name as it appears in assembly for label_name""" + # get the nearest function for this node + if self.parent: + function = self.parent.getFunctionNode() + if not self.parent or not function: + raise RuntimeError("Labels are only allowed in functions") + if label_name in function.goto_labels: + return function.goto_labels[label_name] + raise RuntimeError("Label '{}' used but not defined".format(name)) |