summaryrefslogtreecommitdiff
path: root/AsmParser.py
blob: 40ad3842886739b9caab96798bdef37c7cdeacac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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