diff options
-rw-r--r-- | AsmParser.py | 147 | ||||
-rwxr-xr-x | pp2cc.py | 29 |
2 files changed, 175 insertions, 1 deletions
diff --git a/AsmParser.py b/AsmParser.py new file mode 100644 index 0000000..40ad384 --- /dev/null +++ b/AsmParser.py @@ -0,0 +1,147 @@ +#!/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 Asm import Asm +import re + +class AsmParser(object): + def __init__(self, filename, parent=None): + if parent: + self.parent = parent + self.defined_names = parent.defined_names + else: + self.data = [] + self.code = [] + # defined labels for the assembly files. key: label, value: mapped + # label (to avoid name clashes with the other code) + self.labels = {} + # defined words/storage + self.defined_names = [] + + # valid values: None, DATA and CODE + self.in_section = None + # key: name, value: list of line numbers + self.constants = {} + + self.re_whitespace = re.compile("\s+") + self.asm = Asm() + + file = open(filename, "rU") + while True: + line = file.readline() + # line is empty if EoF, otherwise a string including newline + if line: + self.parseLine(line.split(";", 1)[0].strip()) + else: + break + file.close() + def parseLine(self, line): + """Processes the a line from assembly""" + if line.startswith("@"): + cmd, opts = re.split("\s+", line + " ", 1) + cmd = cmd[1:] + opts = opts.strip() + + if cmd == "CODE": + self.in_section = "CODE" + elif cmd == "DATA": + self.in_section = "DATA" + elif cmd == "END": + self.in_section = None + elif cmd == "INCLUDE": + if opts.count('"', 1, -1) == 0 and opts.count('"') == 2: + raise RuntimeError('Expected the format @INCLUDE "file", ' + "found '{}' instead".format(line)) + filename = opts[1:-1] + if not filename.endswith(".asm"): + filename += ".asm" + AsmParser(filename, self) + elif cmd in ("STACKSIZE", "STACK"): + # ignore + pass + else: + raise RuntimeError("Unrecognized command '{}'".format(cmd)) + elif self.in_section in ("DATA", "CODE"): + match = re.split(self.re_whitespace, line, 2) + if len(match) == 2: + name, what, data = match + if what == "EQU": + self.setConstant(name, data) + line = "" + elif what in ("DS", "DW"): + if self.in_section == "CODE": + raise RuntimeError("DS or DW found in @CODE section") + self.addName(name) + self.addData(line) + if line: + if self.in_section == "DATA": + raise RuntimeError("Found non definition data in @DATA") + label = self.asm.get_label(line) + self.addLabel(label) + self.addCode(line) + else: + # ignore other lines + pass + def setConstant(self, name, value): + """Defines a constant for the current assembly file + """ + if name in self.constants: + raise RuntimeError("Redefinition of constant '{}'".format(name)) + self.constants[name] = self.evaluateConstant(data) + def addCode(self, line): + """Add a line to the @CODE section""" + if self.parent: + self.parent.addCode(line) + else: + self.code.append(line) + def addData(self, line): + """Add a line to the @DATA section""" + if self.parent: + self.parent.addData(line) + else: + self.data.append(line) + def evaluateConstant(self, expression): + """Evaluates a constant expression in an EQU""" + if not expression.isdigit(): + raise RuntimeError("I am lazy and do not support values other than" + " digits in an EQU") + return expression + def addLabel(self, label): + """Adds a label to the list of labels""" + if self.parent: + self.parent.addLabel(label) + elif label in self.labels: + raise RuntimeError("Label '{}' is already defined".format(label)) + else: + self.labels[label] = label + def addName(self, name): + """Adds a name to the list of define words/ storage""" + if self.parent: + self.parent.addName(name) + elif name in self.defined_names: + raise RuntimeError("Name '{}' is already defined".format(name)) + else: + self.defined_names.append(name) + def mapLabel(self, label, new_label): + """Renames a label""" + if not name in self.labels: + raise RuntimeError("Attempt to rename an undefined '{}' to '{}'" + .format(label, new_label)) + self.labels[label] = new_label @@ -17,6 +17,7 @@ from Registers import Registers from LinkedNode import LinkedNode from Function import Function from Variables import Variables, GlobalVariables +from AsmParser import AsmParser __author__ = "Peter Wu" __copyright__ = "Copyright 2011, Peter Wu" @@ -91,7 +92,21 @@ class Parse(object): self.registers = Registers() def loadFile(self, filename, use_cpp=True, cpp_args=[]): """Loads a file and parses the contents of it""" - self.node = parse_file(filename, use_cpp=use_cpp, cpp_args=cpp_args); + # reset state + self.asm_node = self.node = None + + ext = os.path.splitext(filename)[1].lower() + if ext: + ext = ext[1:] + if ext == "i": + # C source code which should not be preprocessed + self.node = parse_file(filename, use_cpp=False) + elif ext in ("c", "h"): + self.node = parse_file(filename, use_cpp=use_cpp, cpp_args=cpp_args) + elif ext == "asm": + self.asm_node = AsmParser(filename) + else: + self.logger.error("Unrecognized file extension '{}'".format(ext)) def show(self): self.node.show(showcoord=True) def uniqLbl(self, labelname): @@ -111,6 +126,15 @@ class Parse(object): self.logger.error("Redefinition of label '{}'".format(labelname)) self.labels.add(labelname) def compile(self): + """Processes a loaded file and adds it to the assembly output""" + if self.node is not None: + self.compileAST() + elif self.asm_node is not None: + self.compileASM() + else: + self.logger.error("No AST node nor assembly lines found to be" + " processed") + def compileAST(self): """Loops through the nodes in an AST syntax tree and generates ASM""" root_node = LinkedNode(self.node) variables = GlobalVariables(self.varNames) @@ -123,6 +147,9 @@ class Parse(object): " node, but also found " + str(thing), linked_node=linked_node) self.codeSegment += self.parseStatement(thing, root_node) + def compileASM(self): + """Processes lines of assembly and merge it into the output""" + pass def getSource(self): """Retrieves the ASM source. You need to compile it first""" output = [] |