#!/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" class LinkedNode(object): """Stores nodes with a reference to the parent""" def __init__(self, node, parent=None, level_increment=False): """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 """ 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 = [] # inherit level and variables from parent if parent: self.level = parent.level self.variables = parent.variables if level_increment: self.incrementLevel() # labels are limited to function contexts if self.type == "FuncDef": self.goto_labels = {} def setVariablesObj(self, variables_obj): """Sets a new variables scope object for this node""" self.variables = variables_obj 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 needNewScope(self): """Returns True if a new name scope is necessary and False otherwise""" if self.type == "Compound": if self.parent.type not in ("For", "FuncDef"): return True elif self.type in ("FileAST", "For", "FuncDef"): return True return False 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 "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 and a stack allocation function """ self.function = function function.setLinkedNode(self) self.variables.setFunction(function) def getLocation(self): if hasattr(self.node, "coord"): return str(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, label_asm): """Sets the label for this node Keyword arguments: label_name -- The label name as can be used in C label_asm -- The label 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)) 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))