forked from sampsyo/bril
-
Notifications
You must be signed in to change notification settings - Fork 0
/
infer.py
153 lines (134 loc) · 5.12 KB
/
infer.py
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
148
149
150
151
152
153
"""Type inference for Bril
"""
import json
import sys
import copy
ARITHMETIC_OPS = ["add", "mul", "sub", "div"]
COMPARISON_OPS = ["eq", "lt", "gt", "le", "ge"]
LOGIC_OPS = ["not", "and", "or"]
def type_var(gamma, var, expected_type, i):
if var in gamma and gamma[var] != expected_type:
raise Exception(
'(stmt {}) Expected "{}" to have type "{}" but found "{}"'.format(
i,
var,
expected_type,
gamma[var]
))
gamma[var] = expected_type
# Runtime is O(n^2) where n is the number of instructions, because of
# main {
# jmp l2;
# l1:
# a = id b;
# b = id c;
# c = id d;
# ...
# y = id z;
# ret;
# l2:
# z = const 0;
# }
def infer_types_func(func):
gamma = {}
typed_func = copy.deepcopy(func)
# Keep track of whether or not any type was inferred.
# If so, then we need to try type checking again, because for `id` ops,
# e.g. "x = id y", we need to know the type of `y` first, which may not
# happen until later in the program because we're going sequentially
# through the instructions
old_gamma_size = -1
while len(gamma) != old_gamma_size:
old_gamma_size = len(gamma)
for i, instr in enumerate(typed_func["instrs"]):
# Continue if we have a label
if "op" not in instr:
continue
# Handle constants
if instr["op"] == "const":
if instr["value"] is True or instr["value"] is False:
type_var(gamma, instr["dest"], "bool", i)
else:
type_var(gamma, instr["dest"], "int", i)
# Handle effect operations
if instr["op"] == "ret" or instr["op"] == "jmp":
continue
# Handle value operations
elif instr["op"] in ARITHMETIC_OPS:
for arg in instr["args"]:
type_var(gamma, arg, "int", i)
type_var(gamma, instr["dest"], "int", i)
elif instr["op"] in COMPARISON_OPS:
for arg in instr["args"]:
type_var(gamma, arg, "int", i)
type_var(gamma, instr["dest"], "bool", i)
elif instr["op"] in LOGIC_OPS:
for arg in instr["args"]:
type_var(gamma, arg, "bool", i)
type_var(gamma, instr["dest"], "bool", i)
elif instr["op"] == "br":
type_var(gamma, instr["args"][0], "bool", i)
# Handle misc. operations
elif instr["op"] == "print" or instr["op"] == "nop":
continue
elif instr["op"] == "id" and instr["args"][0] in gamma:
type_var(gamma, instr["dest"], gamma[instr["args"][0]], i)
# Set the type for this instruction to be whatever we've inferred
if "dest" in instr and instr["dest"] in gamma:
typed_func["instrs"][i]["type"] = gamma[instr["dest"]]
# end for over instructions
# end while
return typed_func
def infer_types(bril):
typed_bril = {"functions": []}
for f in bril["functions"]:
typed_function = infer_types_func(f)
typed_bril["functions"].append(typed_function)
return typed_bril
def analyze_vars(typed_func):
labels = set()
gamma = dict()
for instr in typed_func["instrs"]:
if "label" in instr:
labels.add(instr["label"])
else:
if "dest" in instr and "type" in instr:
gamma[instr["dest"]] = instr["type"]
return gamma, labels
def typecheck_label(label, gamma):
if label in gamma:
raise Exception(
'Expected "{}" to be a label, but it was a'
' variable of type "{}"'
.format(label, gamma[label])
)
def typecheck_func(original_func, typed_func):
gamma, labels = analyze_vars(typed_func)
for instr in original_func["instrs"]:
if "label" in instr and instr["label"] in gamma:
raise Exception(
'Expected "{}" to be a label, but it was a'
' variable of type "{}"'
.format(instr["label"], gamma[instr["label"]])
)
if "op" in instr:
if "dest" in instr and "type" in instr and instr["type"] != gamma[instr["dest"]]:
raise Exception(
'Expected "{}" to have type, but it was explicitly'
' typed as "{}"'
.format(gamma[instr["dest"]], instr["type"])
)
elif instr["op"] == "jmp":
typecheck_label(instr["labels"][0], gamma)
elif instr["op"] == "br":
typecheck_label(instr["labels"][0], gamma)
typecheck_label(instr["labels"][1], gamma)
def typecheck(original_bril, typed_bril):
for i in range(len(original_bril["functions"])):
typecheck_func(original_bril["functions"][i], typed_bril["functions"][i])
if __name__ == '__main__':
bril = json.load(sys.stdin)
typed_bril = infer_types(bril)
if '-t' in sys.argv:
typecheck(bril, typed_bril)
json.dump(typed_bril, sys.stdout, indent=2, sort_keys=True)