summaryrefslogtreecommitdiff
path: root/LinkedNode.py
diff options
context:
space:
mode:
Diffstat (limited to 'LinkedNode.py')
-rw-r--r--LinkedNode.py179
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))