#!/usr/bin/env python """Compiles C into assembly for the practicum processor (PP2) Copyright (C) 2011-2014 Peter Wu Licensed under the MIT license . """ __author__ = "Peter Wu" __copyright__ = "Copyright (C) 2011-2014 Peter Wu" __credits__ = ["Peter Wu"] __license__ = "MIT" __version__ = "1.0" __maintainer__ = "Peter Wu" __email__ = "lekensteyn@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))