summaryrefslogtreecommitdiff
path: root/Variables.py
blob: 338cfdfd28b907f4464641fcc5269f505542762c (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
#!/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"

class Variables(object):
    def __init__(self, parent_variables, defined_names, function=None):
        """A scope for holding variable names
        
        Keywords arguments:
        parent_variables -- the parent Variables object. If None, it's a global
        variable scope
        defined_names -- A list of defined variables to which additional
        variables might be appended
        function -- The function object used for allocating memory. If not set,
        the variables object will ask the parent_variables object for it
        """
        self.parent_variables = parent_variables
        self.function = function
        # if there is a parent_variables object, it must be a function
        if self.parent_variables:
            # key: name, value: address n relative to BP (n >= 1)
            self.local_vars = {}
            self.param_vars = []
            if not self.function:
                self.function = self.parent_variables.function
        else:
            # key: name of var, value: label of var
            self.global_vars = {}
        self.defined_names = defined_names
    def uniqName(self, name):
        """Returns an unique global variable name for assembly"""
        uniq_name = name
        i = 0
        while uniq_name in self.defined_names:
            uniq_name = name + "_" + str(i)
            i += 1
        return uniq_name
    def getAddress(self, name):
        """Gets the address for a variable as a tuple containing a register and
        displacement

        To get the address of the variable, add the register value and
        displacement
        """
        # first try the local scope
        if self.function:
            if name in self.local_vars:
                # XXX don't hardcode R5
                return ("R5", str(self.local_vars[name]))
            try:
                return ("R5", str(-self.param_vars.index(name)))
            except ValueError:
                pass
        else:
            # lookup in global vars
            if name in self.global_vars:
                return ("GB", self.global_vars[name])
        # lookup in the parent
        if self.parent_variables:
            return self.parent_variables.getAddress(name)
        raise RuntimeError("Use of undefined variable '{}'".format(name))
    def declName(self, name, size=1, is_param=False):
        """Declares a variable in the nearest scope

        Keyword arguments:
        name -- The symbolic name of the variable
        size -- The size of the memory to be allocated in words (default 1)
        is_param -- Whether the name is a function parameter or not
        """
        already_defined = False
        if self.function:
            already_defined = name in self.local_vars or name in self.param_vars
        else:
            already_defined = name in self.global_vars
        if already_defined:
            raise RuntimeError("Redeclaration of variable '{}'".format(name))

        if self.function:
            # parameters do not need a special allocation because the callee
            # pushes it into the stack
            if is_param:
                self.param_vars.append(name)
            else:
                self.local_vars[name] = self.function.allocStack(size)
        elif is_param:
            raise RuntimeError("Parameter '{}' declared in global context".format(name))
        else:
            # global variables are prefixed "var_"
            var_name = "var_" + name
            self.global_vars[name] = var_name
            self.defined_names[var_name] = size