summaryrefslogtreecommitdiff
path: root/AsmLine.py
blob: 90163218cc78a9752a2b3d740498bcd5dedcb822 (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
#!/usr/bin/env python
"""Compiles C into assembly for the practicum processor (PP2)

Copyright (C) 2011-2014 Peter Wu <lekensteyn@gmail.com>
Licensed under the MIT license <http://opensource.org/licenses/MIT>.
"""

__author__ = "Peter Wu"
__copyright__ = "Copyright (C) 2011-2014 Peter Wu"
__credits__ = ["Peter Wu"]
__license__ = "MIT"
__version__ = "1.0"
__maintainer__ = "Peter Wu"
__email__ = "lekensteyn@gmail.com"

import re
from Asm import Asm
from Registers import Registers
from NamedId import NamedId

class AsmLine(object):
    asm = Asm()
    registers = Registers()
    re_whitespace = re.compile("\s+")
    def __init__(self, line, id_dict=None):
        # for storing NamedId objects
        self.id_dict = id_dict
        self.label = ""
        self.register = ""
        self.operand = ""
        line = line.split(";", 1)[0].strip()

        if ":" in line:
            self.label, line = line.split(":", 1)
            self.label = self.label.strip()
            line = line.strip()
            if self.asm.is_identifier(self.label):
                # for easier label name manipulation
                self.getNamedId(self.label)
            else:
                self.label = ""
                line = label + ":" + line

        parts = self.re_whitespace.split(line, 1)
        self.instruction = parts[0]

        if self.instruction in Asm.operators_binary:
            # a label for sure
            reg, operand = self.re_whitespace.split(parts[1], 1)
            self.register = reg
            self.setOperand(operand)
        elif self.instruction in Asm.operators_branch:
            # skip validation of reg for speed
            self.register = parts[1]
        elif self.instruction in Asm.operators_unary:
            self.setOperand(parts[1])
        elif self.instruction in Asm.operators_misc_reg:
            self.register = parts[1]
        elif self.instruction in Asm.operators_misc_noreg:
            # no args
            pass
        elif self.instruction == "CONS":
            # pseudo instruction
            self.setOperand(parts[1])
        else:
            raise RuntimeError("Unknown instruction '{}'".format(self.instruction))
    def setOperand(self, str):
        """Sets the operand for this object either as a string or an identifier
        object
        """
        if self.id_dict is None:
            self.operand = str
        elif self.registers.is_register(str):
            # register
            self.operand = [str]
        else:
            if str.startswith("["):
                # split by [ ] and +, grouping multiple occurences
                self.operand = re.split("([\[\]+]+)", str)
            else:
                # value
                self.operand = [str]
            for i, part in enumerate(self.operand):
                # skip empty elements, registers and [ ] +
                if (part and not self.registers.is_register(part) and
                    part[0] not in "[]+"):
                    # turn named items in an object for easier manipulation of
                    # the names later
                    # if performance is an issue, you may want to skip the name
                    # validity check on the cost of NamedIds pollution
                    if self.asm.is_identifier(part):
                        self.operand[i] = self.getNamedId(part)
    def getNamedId(self, name):
        """Returns an NamedId object for keeping a track of all names at once
        """
        # if there is no dictionary to store names, just return the name
        if self.id_dict is None:
            return name
        if name not in self.id_dict:
            self.id_dict[name] = NamedId(name)
        return self.id_dict[name]
    def __str__(self):
        """Returns a line of assembly"""
        line = self.instruction.ljust(4)
        if self.register:
            line += " " + self.register
        if self.operand:
            # join all operand parts together
            line += " " + "".join(str(elm) for elm in self.operand)
        return self.asm.format_line(line, self.label)