#!/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