summaryrefslogtreecommitdiff
path: root/AsmParser.py
diff options
context:
space:
mode:
Diffstat (limited to 'AsmParser.py')
-rw-r--r--AsmParser.py147
1 files changed, 147 insertions, 0 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