Refactoring: move modules around, avoid late import in TypeChecker.
--HG--
rename : src/castile/compiler.py => src/castile/backends/javascript.py
rename : src/castile/types.py => src/castile/checker.py
catseye
12 years ago
0 | from castile.types import Void | |
1 | ||
2 | OPS = { | |
3 | 'and': '&&', | |
4 | 'or': '||', | |
5 | '==': '===', | |
6 | } | |
7 | ||
8 | ||
9 | class Compiler(object): | |
10 | def __init__(self, out): | |
11 | self.out = out | |
12 | ||
13 | def commas(self, asts, sep=','): | |
14 | if asts: | |
15 | for child in asts[:-1]: | |
16 | self.compile(child) | |
17 | self.out.write(sep) | |
18 | self.compile(asts[-1]) | |
19 | ||
20 | def compile(self, ast): | |
21 | if ast.type == 'Program': | |
22 | self.out.write("""\ | |
23 | /* AUTOMATICALLY GENERATED EDIT AT OWN RISK */ | |
24 | ||
25 | var print = function(s) { console.log(s); } | |
26 | var len = function(s) { return s.length; } | |
27 | var concat = function(s1,s2) { return s1 + s2; } | |
28 | var substr = function(s,p,k) { return s.substr(p, k); } | |
29 | ||
30 | var repr = function(o) { | |
31 | if (typeof o === "string") { | |
32 | return "'" + o + "'"; | |
33 | } else if (o === true) { | |
34 | return "True"; | |
35 | } else if (o === false) { | |
36 | return "False"; | |
37 | } else if (o === undefined) { | |
38 | return ""; | |
39 | } else if (typeof o === "object") { | |
40 | var s = "("; | |
41 | for (var i = 0; i < o.length; i++) { | |
42 | s += repr(o[i]); | |
43 | if (i != o.length - 1) { s += ', '; } | |
44 | } | |
45 | s += ")"; | |
46 | return s; | |
47 | } else { | |
48 | return o; | |
49 | } | |
50 | } | |
51 | ||
52 | """) | |
53 | for child in ast.children: | |
54 | self.compile(child) | |
55 | self.out.write("""\ | |
56 | ||
57 | var result = main(); | |
58 | print(repr(result)); | |
59 | """) | |
60 | elif ast.type == 'Defn': | |
61 | self.out.write('%s = ' % ast.value) | |
62 | self.compile(ast.children[0]) | |
63 | self.out.write(';\n') | |
64 | elif ast.type in ('StructDefn', 'Forward'): | |
65 | pass | |
66 | elif ast.type == 'FunLit': | |
67 | self.out.write('function(') | |
68 | self.compile(ast.children[0]) | |
69 | self.out.write(')\n') | |
70 | self.compile(ast.children[1]) | |
71 | elif ast.type == 'Args': | |
72 | self.commas(ast.children) | |
73 | elif ast.type == 'Arg': | |
74 | self.out.write(ast.value) | |
75 | elif ast.type == 'Block': | |
76 | # typechecker assigned ast.t the type of this block | |
77 | # typechecker assigned value 'function body' if it is a fn bd | |
78 | self.out.write('{') | |
79 | if not ast.children: | |
80 | if ast.value == 'function body': | |
81 | self.out.write('return undefined;') | |
82 | elif ast.t == Void(): | |
83 | for child in ast.children: | |
84 | self.compile(child) | |
85 | self.out.write(';\n') | |
86 | if ast.value == 'function body': | |
87 | self.out.write('return undefined;') | |
88 | elif ast.children[-1].type == 'If': | |
89 | for child in ast.children: | |
90 | self.compile(child) | |
91 | self.out.write(';\n') | |
92 | if ast.value == 'function body': | |
93 | self.out.write('return result;') | |
94 | else: | |
95 | for child in ast.children[:-1]: | |
96 | self.compile(child) | |
97 | self.out.write(';\n') | |
98 | if ast.value == 'function body': | |
99 | self.out.write('return ') | |
100 | self.compile(ast.children[-1]) | |
101 | self.out.write(';\n') | |
102 | else: | |
103 | self.out.write('result = ') | |
104 | self.compile(ast.children[-1]) | |
105 | self.out.write(';\n') | |
106 | self.out.write('}') | |
107 | elif ast.type == 'VarDecl': | |
108 | self.out.write('var %s = ' % ast.value) | |
109 | self.compile(ast.children[0]) | |
110 | elif ast.type == 'While': | |
111 | self.out.write('while (') | |
112 | self.compile(ast.children[0]) | |
113 | self.out.write(')') | |
114 | self.compile(ast.children[1]) | |
115 | elif ast.type == 'Op': | |
116 | self.out.write('(') | |
117 | self.compile(ast.children[0]) | |
118 | self.out.write(' %s ' % OPS.get(ast.value, ast.value)) | |
119 | self.compile(ast.children[1]) | |
120 | self.out.write(')') | |
121 | elif ast.type == 'VarRef': | |
122 | self.out.write(ast.value) | |
123 | elif ast.type == 'FunCall': | |
124 | self.compile(ast.children[0]) | |
125 | self.out.write('(') | |
126 | self.commas(ast.children[1:]) | |
127 | self.out.write(')') | |
128 | elif ast.type == 'If': | |
129 | if ast.children[1].t == Void(): | |
130 | self.out.write('if(') | |
131 | self.compile(ast.children[0]) | |
132 | self.out.write(')') | |
133 | if len(ast.children) == 3: # if-else | |
134 | self.compile(ast.children[1]) | |
135 | self.out.write(' else ') | |
136 | self.compile(ast.children[2]) | |
137 | else: # just-if | |
138 | self.compile(ast.children[1]) | |
139 | else: | |
140 | # this assumes you return the result from the current | |
141 | # function, but this assumption is safe, because if you | |
142 | # didn't, it wouldn't typecheck. | |
143 | self.out.write('{ var result; if(') | |
144 | self.compile(ast.children[0]) | |
145 | self.out.write(')') | |
146 | self.compile(ast.children[1]) | |
147 | self.out.write(' else ') | |
148 | self.compile(ast.children[2]) | |
149 | self.out.write('return result; }') | |
150 | elif ast.type == 'Return': | |
151 | self.out.write('return ') | |
152 | self.compile(ast.children[0]) | |
153 | elif ast.type == 'Do': | |
154 | self.compile(ast.children[0]) | |
155 | elif ast.type == 'Not': | |
156 | self.out.write('!(') | |
157 | self.compile(ast.children[0]) | |
158 | self.out.write(')') | |
159 | elif ast.type == 'None': | |
160 | self.out.write('null') | |
161 | elif ast.type == 'IntLit': | |
162 | self.out.write(str(ast.value)) | |
163 | elif ast.type == 'StrLit': | |
164 | self.out.write("'%s'" % ast.value) | |
165 | elif ast.type == 'Assignment': | |
166 | self.compile(ast.children[0]) | |
167 | self.out.write(' = ') | |
168 | self.compile(ast.children[1]) | |
169 | elif ast.type == 'Make': | |
170 | self.out.write('[') | |
171 | self.commas(ast.children[1:]) | |
172 | self.out.write(']') | |
173 | elif ast.type == 'Index': | |
174 | self.compile(ast.children[0]) | |
175 | # ast.value was converted from a field name to an index by | |
176 | # the typechecker. | |
177 | self.out.write('[%d]' % ast.value) | |
178 | elif ast.type == 'Cast': | |
179 | # ast.value was added by the typechecker. | |
180 | self.out.write("['%s'," % ast.value) | |
181 | self.compile(ast.children[0]) | |
182 | self.out.write(']') | |
183 | elif ast.type == 'TypeCase': | |
184 | self.out.write('if (') | |
185 | self.compile(ast.children[0]) | |
186 | # ast.value was added by the typechecker. | |
187 | self.out.write("[0] == '%s')" % ast.value) | |
188 | self.out.write('{ var save=') | |
189 | self.compile(ast.children[0]) | |
190 | self.out.write('; ') | |
191 | self.compile(ast.children[0]) | |
192 | self.out.write('=') | |
193 | self.compile(ast.children[0]) | |
194 | self.out.write('[1]; ') | |
195 | self.compile(ast.children[2]) | |
196 | self.compile(ast.children[0]) | |
197 | self.out.write(' =save; }') | |
198 | else: | |
199 | raise NotImplementedError(repr(ast)) |
0 | from castile.builtins import BUILTINS | |
1 | from castile.context import ScopedContext | |
2 | from castile.types import * | |
3 | ||
4 | ||
5 | class TypeChecker(object): | |
6 | def __init__(self): | |
7 | global_context = {} | |
8 | for (name, (value, type)) in BUILTINS.iteritems(): | |
9 | global_context[name] = type | |
10 | self.context = ScopedContext(global_context) | |
11 | self.context = ScopedContext({}, self.context) | |
12 | ||
13 | self.forwards = {} | |
14 | self.structs = {} # struct name -> StructDefinition | |
15 | self.struct_fields = {} # struct name -> dict of field name -> pos | |
16 | self.assignable = {} | |
17 | self.return_type = None | |
18 | self.verbose = False | |
19 | ||
20 | def set(self, name, type): | |
21 | self.context[name] = type | |
22 | if self.verbose: | |
23 | print '%s: %s' % (name, type) | |
24 | return type | |
25 | ||
26 | def assert_eq(self, t1, t2): | |
27 | if t1 == t2: | |
28 | return | |
29 | raise SyntaxError("type mismatch: %s != %s" % (t1, t2)) | |
30 | ||
31 | def is_assignable(self, ast): | |
32 | assert ast.type == 'VarRef' | |
33 | name = ast.value | |
34 | return name in self.assignable | |
35 | ||
36 | def collect_structs(self, ast): | |
37 | for child in ast.children: | |
38 | if child.type == 'StructDefn': | |
39 | self.collect_struct(child) | |
40 | ||
41 | def collect_struct(self, ast): | |
42 | name = ast.value | |
43 | if name in self.structs: | |
44 | raise SyntaxError('duplicate struct %s' % name) | |
45 | struct_fields = {} | |
46 | self.struct_fields[name] = struct_fields | |
47 | te = [] | |
48 | i = 0 | |
49 | for child in ast.children: | |
50 | assert child.type == 'FieldDefn' | |
51 | field_name = child.value | |
52 | if field_name in struct_fields: | |
53 | raise SyntaxError('already-defined field %s' % field_name) | |
54 | struct_fields[field_name] = i | |
55 | i += 1 | |
56 | te.append(self.type_of(child.children[0])) | |
57 | self.structs[name] = StructDefinition(ast.value, te) | |
58 | ||
59 | # context is modified as side-effect of traversal | |
60 | def type_of(self, ast): | |
61 | if ast.type == 'Op': | |
62 | if ast.value in ('and', 'or'): | |
63 | self.assert_eq(self.type_of(ast.children[0]), Boolean()) | |
64 | self.assert_eq(self.type_of(ast.children[1]), Boolean()) | |
65 | return Boolean() | |
66 | elif ast.value in ('+', '-', '*', '/'): | |
67 | type1 = self.type_of(ast.children[0]) | |
68 | type2 = self.type_of(ast.children[1]) | |
69 | self.assert_eq(type1, type2) | |
70 | self.assert_eq(type1, Integer()) | |
71 | return Integer() | |
72 | elif ast.value in ('==', '!=', '>', '>=', '<', '<='): | |
73 | type1 = self.type_of(ast.children[0]) | |
74 | type2 = self.type_of(ast.children[1]) | |
75 | self.assert_eq(type1, type2) | |
76 | return Boolean() | |
77 | elif ast.type == 'Not': | |
78 | type1 = self.type_of(ast.children[0]) | |
79 | self.assert_eq(type1, Boolean()) | |
80 | return Boolean() | |
81 | elif ast.type == 'IntLit': | |
82 | return Integer() | |
83 | elif ast.type == 'StrLit': | |
84 | return String() | |
85 | elif ast.type == 'FunLit': | |
86 | # TODO: should not be able to see anything but globals in here... | |
87 | # unless it's a closure. Oh, joy. | |
88 | self.context = ScopedContext({}, self.context) | |
89 | self.return_type = None | |
90 | arg_types = self.type_of(ast.children[0]) # args | |
91 | return_type = self.type_of(ast.children[1]) # body | |
92 | if self.return_type is not None: | |
93 | self.assert_eq(return_type, self.return_type) | |
94 | self.context = self.context.parent | |
95 | self.return_type = None | |
96 | # modify AST for compiler's benefit | |
97 | ast.children[1].value = 'function body' | |
98 | return Function(arg_types, return_type) | |
99 | elif ast.type == 'Args': | |
100 | types = [] | |
101 | for child in ast.children: | |
102 | types.append(self.type_of(child)) | |
103 | return types | |
104 | elif ast.type == 'Arg': | |
105 | return self.set(ast.value, self.type_of(ast.children[0])) | |
106 | elif ast.type == 'Type': | |
107 | map = { | |
108 | 'integer': Integer(), | |
109 | 'boolean': Boolean(), | |
110 | 'string': String(), | |
111 | 'void': Void(), | |
112 | } | |
113 | return map[ast.value] | |
114 | elif ast.type == 'FunType': | |
115 | return_type = self.type_of(ast.children[0]) | |
116 | return Function([self.type_of(c) for c in ast.children[1:]], | |
117 | return_type) | |
118 | elif ast.type == 'UnionType': | |
119 | return Union([self.type_of(c) for c in ast.children]) | |
120 | elif ast.type == 'StructType': | |
121 | return Struct(ast.value) | |
122 | elif ast.type == 'VarDecl': | |
123 | name = ast.value | |
124 | if name in self.context: | |
125 | raise SyntaxError('declaration of %s shadows previous' % name) | |
126 | self.assignable[name] = True | |
127 | self.set(name, self.type_of(ast.children[0])) | |
128 | return Void() | |
129 | elif ast.type == 'VarRef': | |
130 | return self.context[ast.value] | |
131 | elif ast.type == 'None': | |
132 | return Void() | |
133 | elif ast.type == 'FunCall': | |
134 | t1 = self.type_of(ast.children[0]) | |
135 | assert isinstance(t1, Function), \ | |
136 | '%r is not a function' % t1 | |
137 | if len(t1.arg_types) != len(ast.children) - 1: | |
138 | raise SyntaxError("argument mismatch") | |
139 | i = 0 | |
140 | for child in ast.children[1:]: | |
141 | self.assert_eq(self.type_of(child), t1.arg_types[i]) | |
142 | i += 1 | |
143 | return t1.return_type | |
144 | elif ast.type == 'Do': | |
145 | t1 = self.type_of(ast.children[0]) | |
146 | return Void() | |
147 | elif ast.type == 'Return': | |
148 | t1 = self.type_of(ast.children[0]) | |
149 | if self.return_type is None: | |
150 | self.return_type = t1 | |
151 | else: | |
152 | self.assert_eq(t1, self.return_type) | |
153 | return Void() | |
154 | elif ast.type == 'If': | |
155 | t1 = self.type_of(ast.children[0]) | |
156 | assert t1 == Boolean() | |
157 | t2 = self.type_of(ast.children[1]) | |
158 | if len(ast.children) == 3: | |
159 | t3 = self.type_of(ast.children[2]) | |
160 | self.assert_eq(t2, t3) | |
161 | return t2 | |
162 | else: | |
163 | return Void() | |
164 | elif ast.type == 'While': | |
165 | t1 = self.type_of(ast.children[0]) | |
166 | assert t1 == Boolean() | |
167 | t2 = self.type_of(ast.children[1]) | |
168 | return Void() | |
169 | elif ast.type == 'Block': | |
170 | self.context = ScopedContext({}, self.context) | |
171 | if len(ast.children) == 0: | |
172 | ast.t = Void() | |
173 | return Void() | |
174 | for child in ast.children[:-1]: | |
175 | self.assert_eq(self.type_of(child), Void()) | |
176 | last = self.type_of(ast.children[-1]) | |
177 | self.context = self.context.parent | |
178 | # modify the AST for the compiler's benefit | |
179 | ast.t = last | |
180 | return ast.t | |
181 | elif ast.type == 'Assignment': | |
182 | t1 = self.type_of(ast.children[0]) | |
183 | if not self.is_assignable(ast.children[0]): | |
184 | raise SyntaxError('cannot assign to non-local') | |
185 | t2 = self.type_of(ast.children[1]) | |
186 | self.assert_eq(t1, t2) | |
187 | return Void() | |
188 | elif ast.type == 'Make': | |
189 | t = self.type_of(ast.children[0]) | |
190 | if t.name not in self.structs: | |
191 | raise SyntaxError("undefined struct %s" % t.name) | |
192 | struct_defn = self.structs[t.name] | |
193 | if len(struct_defn.content_types) != len(ast.children) - 1: | |
194 | raise SyntaxError("argument mismatch") | |
195 | i = 0 | |
196 | for defn in ast.children[1:]: | |
197 | t1 = self.type_of(defn) | |
198 | self.assert_eq(t1, struct_defn.content_types[i]) | |
199 | i += 1 | |
200 | return t | |
201 | elif ast.type == 'Index': | |
202 | t = self.type_of(ast.children[0]) | |
203 | field_name = ast.value | |
204 | struct_fields = self.struct_fields[t.name] | |
205 | if field_name not in struct_fields: | |
206 | raise SyntaxError("undefined field") | |
207 | index = struct_fields[field_name] | |
208 | # we modify the AST for the evaluator's benefit. | |
209 | ast.value = index | |
210 | # we look up the type from the StructDefinition | |
211 | return self.structs[t.name].content_types[index] | |
212 | elif ast.type == 'TypeCase': | |
213 | t1 = self.type_of(ast.children[0]) | |
214 | t2 = self.type_of(ast.children[1]) | |
215 | ||
216 | if not isinstance(t1, Union): | |
217 | raise SyntaxError('bad typecase, %s not a union' % t1) | |
218 | if not t1.contains(t2): | |
219 | raise SyntaxError('bad typecase, %s not in %s' % (t2, t1)) | |
220 | # modify the AST for the evaluator's benefit | |
221 | ast.value = str(t2) | |
222 | ||
223 | # typecheck t3 with variable in children[0] having type t2 | |
224 | assert ast.children[0].type == 'VarRef' | |
225 | self.context = ScopedContext({}, self.context) | |
226 | self.context[ast.children[0].value] = t2 | |
227 | t3 = self.type_of(ast.children[2]) | |
228 | self.context = self.context.parent | |
229 | return t3 | |
230 | elif ast.type == 'Program': | |
231 | for defn in ast.children: | |
232 | t1 = self.type_of(defn) | |
233 | return Void() | |
234 | elif ast.type == 'Defn': | |
235 | # reset assignable | |
236 | self.assignable = {} | |
237 | t = self.type_of(ast.children[0]) | |
238 | if ast.value in self.forwards: | |
239 | self.assert_eq(self.forwards[ast.value], t) | |
240 | del self.forwards[ast.value] | |
241 | else: | |
242 | self.set(ast.value, t) | |
243 | if ast.value == 'main': | |
244 | # any return type is fine, for now, so, | |
245 | # we compare it against itself | |
246 | rt = t.return_type | |
247 | self.assert_eq(t, Function([], rt)) | |
248 | return t | |
249 | elif ast.type == 'Forward': | |
250 | t = self.type_of(ast.children[0]) | |
251 | self.forwards[ast.value] = t | |
252 | return self.set(ast.value, t) | |
253 | elif ast.type == 'StructDefn': | |
254 | pass | |
255 | elif ast.type == 'Cast': | |
256 | t1 = self.type_of(ast.children[0]) | |
257 | t2 = self.type_of(ast.children[1]) | |
258 | if not isinstance(t2, Union): | |
259 | raise SyntaxError('bad cast, not a union: %s' % t2) | |
260 | if not t2.contains(t1): | |
261 | raise SyntaxError('bad cast, %s does not include %s' % | |
262 | (t2, t1)) | |
263 | # we modify the ast here for the evaluator's benefit. | |
264 | ast.value = str(t1) | |
265 | return t2 | |
266 | else: | |
267 | raise NotImplementedError(repr(ast)) |
0 | from castile.types import Void | |
1 | ||
2 | OPS = { | |
3 | 'and': '&&', | |
4 | 'or': '||', | |
5 | '==': '===', | |
6 | } | |
7 | ||
8 | ||
9 | class Compiler(object): | |
10 | def __init__(self, out): | |
11 | self.out = out | |
12 | ||
13 | def commas(self, asts, sep=','): | |
14 | if asts: | |
15 | for child in asts[:-1]: | |
16 | self.compile(child) | |
17 | self.out.write(sep) | |
18 | self.compile(asts[-1]) | |
19 | ||
20 | def compile(self, ast): | |
21 | if ast.type == 'Program': | |
22 | self.out.write("""\ | |
23 | /* AUTOMATICALLY GENERATED EDIT AT OWN RISK */ | |
24 | ||
25 | var print = function(s) { console.log(s); } | |
26 | var len = function(s) { return s.length; } | |
27 | var concat = function(s1,s2) { return s1 + s2; } | |
28 | var substr = function(s,p,k) { return s.substr(p, k); } | |
29 | ||
30 | var repr = function(o) { | |
31 | if (typeof o === "string") { | |
32 | return "'" + o + "'"; | |
33 | } else if (o === true) { | |
34 | return "True"; | |
35 | } else if (o === false) { | |
36 | return "False"; | |
37 | } else if (o === undefined) { | |
38 | return ""; | |
39 | } else if (typeof o === "object") { | |
40 | var s = "("; | |
41 | for (var i = 0; i < o.length; i++) { | |
42 | s += repr(o[i]); | |
43 | if (i != o.length - 1) { s += ', '; } | |
44 | } | |
45 | s += ")"; | |
46 | return s; | |
47 | } else { | |
48 | return o; | |
49 | } | |
50 | } | |
51 | ||
52 | """) | |
53 | for child in ast.children: | |
54 | self.compile(child) | |
55 | self.out.write("""\ | |
56 | ||
57 | var result = main(); | |
58 | print(repr(result)); | |
59 | """) | |
60 | elif ast.type == 'Defn': | |
61 | self.out.write('%s = ' % ast.value) | |
62 | self.compile(ast.children[0]) | |
63 | self.out.write(';\n') | |
64 | elif ast.type in ('StructDefn', 'Forward'): | |
65 | pass | |
66 | elif ast.type == 'FunLit': | |
67 | self.out.write('function(') | |
68 | self.compile(ast.children[0]) | |
69 | self.out.write(')\n') | |
70 | self.compile(ast.children[1]) | |
71 | elif ast.type == 'Args': | |
72 | self.commas(ast.children) | |
73 | elif ast.type == 'Arg': | |
74 | self.out.write(ast.value) | |
75 | elif ast.type == 'Block': | |
76 | # typechecker assigned ast.t the type of this block | |
77 | # typechecker assigned value 'function body' if it is a fn bd | |
78 | self.out.write('{') | |
79 | if not ast.children: | |
80 | if ast.value == 'function body': | |
81 | self.out.write('return undefined;') | |
82 | elif ast.t == Void(): | |
83 | for child in ast.children: | |
84 | self.compile(child) | |
85 | self.out.write(';\n') | |
86 | if ast.value == 'function body': | |
87 | self.out.write('return undefined;') | |
88 | elif ast.children[-1].type == 'If': | |
89 | for child in ast.children: | |
90 | self.compile(child) | |
91 | self.out.write(';\n') | |
92 | if ast.value == 'function body': | |
93 | self.out.write('return result;') | |
94 | else: | |
95 | for child in ast.children[:-1]: | |
96 | self.compile(child) | |
97 | self.out.write(';\n') | |
98 | if ast.value == 'function body': | |
99 | self.out.write('return ') | |
100 | self.compile(ast.children[-1]) | |
101 | self.out.write(';\n') | |
102 | else: | |
103 | self.out.write('result = ') | |
104 | self.compile(ast.children[-1]) | |
105 | self.out.write(';\n') | |
106 | self.out.write('}') | |
107 | elif ast.type == 'VarDecl': | |
108 | self.out.write('var %s = ' % ast.value) | |
109 | self.compile(ast.children[0]) | |
110 | elif ast.type == 'While': | |
111 | self.out.write('while (') | |
112 | self.compile(ast.children[0]) | |
113 | self.out.write(')') | |
114 | self.compile(ast.children[1]) | |
115 | elif ast.type == 'Op': | |
116 | self.out.write('(') | |
117 | self.compile(ast.children[0]) | |
118 | self.out.write(' %s ' % OPS.get(ast.value, ast.value)) | |
119 | self.compile(ast.children[1]) | |
120 | self.out.write(')') | |
121 | elif ast.type == 'VarRef': | |
122 | self.out.write(ast.value) | |
123 | elif ast.type == 'FunCall': | |
124 | self.compile(ast.children[0]) | |
125 | self.out.write('(') | |
126 | self.commas(ast.children[1:]) | |
127 | self.out.write(')') | |
128 | elif ast.type == 'If': | |
129 | if ast.children[1].t == Void(): | |
130 | self.out.write('if(') | |
131 | self.compile(ast.children[0]) | |
132 | self.out.write(')') | |
133 | if len(ast.children) == 3: # if-else | |
134 | self.compile(ast.children[1]) | |
135 | self.out.write(' else ') | |
136 | self.compile(ast.children[2]) | |
137 | else: # just-if | |
138 | self.compile(ast.children[1]) | |
139 | else: | |
140 | # this assumes you return the result from the current | |
141 | # function, but this assumption is safe, because if you | |
142 | # didn't, it wouldn't typecheck. | |
143 | self.out.write('{ var result; if(') | |
144 | self.compile(ast.children[0]) | |
145 | self.out.write(')') | |
146 | self.compile(ast.children[1]) | |
147 | self.out.write(' else ') | |
148 | self.compile(ast.children[2]) | |
149 | self.out.write('return result; }') | |
150 | elif ast.type == 'Return': | |
151 | self.out.write('return ') | |
152 | self.compile(ast.children[0]) | |
153 | elif ast.type == 'Do': | |
154 | self.compile(ast.children[0]) | |
155 | elif ast.type == 'Not': | |
156 | self.out.write('!(') | |
157 | self.compile(ast.children[0]) | |
158 | self.out.write(')') | |
159 | elif ast.type == 'None': | |
160 | self.out.write('null') | |
161 | elif ast.type == 'IntLit': | |
162 | self.out.write(str(ast.value)) | |
163 | elif ast.type == 'StrLit': | |
164 | self.out.write("'%s'" % ast.value) | |
165 | elif ast.type == 'Assignment': | |
166 | self.compile(ast.children[0]) | |
167 | self.out.write(' = ') | |
168 | self.compile(ast.children[1]) | |
169 | elif ast.type == 'Make': | |
170 | self.out.write('[') | |
171 | self.commas(ast.children[1:]) | |
172 | self.out.write(']') | |
173 | elif ast.type == 'Index': | |
174 | self.compile(ast.children[0]) | |
175 | # ast.value was converted from a field name to an index by | |
176 | # the typechecker. | |
177 | self.out.write('[%d]' % ast.value) | |
178 | elif ast.type == 'Cast': | |
179 | # ast.value was added by the typechecker. | |
180 | self.out.write("['%s'," % ast.value) | |
181 | self.compile(ast.children[0]) | |
182 | self.out.write(']') | |
183 | elif ast.type == 'TypeCase': | |
184 | self.out.write('if (') | |
185 | self.compile(ast.children[0]) | |
186 | # ast.value was added by the typechecker. | |
187 | self.out.write("[0] == '%s')" % ast.value) | |
188 | self.out.write('{ var save=') | |
189 | self.compile(ast.children[0]) | |
190 | self.out.write('; ') | |
191 | self.compile(ast.children[0]) | |
192 | self.out.write('=') | |
193 | self.compile(ast.children[0]) | |
194 | self.out.write('[1]; ') | |
195 | self.compile(ast.children[2]) | |
196 | self.compile(ast.children[0]) | |
197 | self.out.write(' =save; }') | |
198 | else: | |
199 | raise NotImplementedError(repr(ast)) |
9 | 9 | |
10 | 10 | from castile.parser import Parser |
11 | 11 | from castile.eval import Program |
12 | from castile.types import TypeChecker | |
13 | from castile.compiler import Compiler | |
12 | from castile.checker import TypeChecker | |
13 | from castile.backends.javascript import Compiler | |
14 | 14 | |
15 | 15 | |
16 | 16 | def main(argv): |
0 | from castile.context import ScopedContext | |
1 | ||
2 | ||
3 | 0 | class Type(object): |
4 | 1 | # Note: type equality relies on (str), so we can't directly |
5 | 2 | # compare infinite types. But with structs, we shouldn't have to. |
69 | 66 | h += ', '.join([str(t) for t in self.content_types]) |
70 | 67 | h += ')' |
71 | 68 | return h |
72 | ||
73 | ||
74 | class TypeChecker(object): | |
75 | def __init__(self): | |
76 | from castile.builtins import BUILTINS | |
77 | global_context = {} | |
78 | for (name, (value, type)) in BUILTINS.iteritems(): | |
79 | global_context[name] = type | |
80 | self.context = ScopedContext(global_context) | |
81 | self.context = ScopedContext({}, self.context) | |
82 | ||
83 | self.forwards = {} | |
84 | self.structs = {} # struct name -> StructDefinition | |
85 | self.struct_fields = {} # struct name -> dict of field name -> pos | |
86 | self.assignable = {} | |
87 | self.return_type = None | |
88 | self.verbose = False | |
89 | ||
90 | def set(self, name, type): | |
91 | self.context[name] = type | |
92 | if self.verbose: | |
93 | print '%s: %s' % (name, type) | |
94 | return type | |
95 | ||
96 | def assert_eq(self, t1, t2): | |
97 | if t1 == t2: | |
98 | return | |
99 | raise SyntaxError("type mismatch: %s != %s" % (t1, t2)) | |
100 | ||
101 | def is_assignable(self, ast): | |
102 | assert ast.type == 'VarRef' | |
103 | name = ast.value | |
104 | return name in self.assignable | |
105 | ||
106 | def collect_structs(self, ast): | |
107 | for child in ast.children: | |
108 | if child.type == 'StructDefn': | |
109 | self.collect_struct(child) | |
110 | ||
111 | def collect_struct(self, ast): | |
112 | name = ast.value | |
113 | if name in self.structs: | |
114 | raise SyntaxError('duplicate struct %s' % name) | |
115 | struct_fields = {} | |
116 | self.struct_fields[name] = struct_fields | |
117 | te = [] | |
118 | i = 0 | |
119 | for child in ast.children: | |
120 | assert child.type == 'FieldDefn' | |
121 | field_name = child.value | |
122 | if field_name in struct_fields: | |
123 | raise SyntaxError('already-defined field %s' % field_name) | |
124 | struct_fields[field_name] = i | |
125 | i += 1 | |
126 | te.append(self.type_of(child.children[0])) | |
127 | self.structs[name] = StructDefinition(ast.value, te) | |
128 | ||
129 | # context is modified as side-effect of traversal | |
130 | def type_of(self, ast): | |
131 | if ast.type == 'Op': | |
132 | if ast.value in ('and', 'or'): | |
133 | self.assert_eq(self.type_of(ast.children[0]), Boolean()) | |
134 | self.assert_eq(self.type_of(ast.children[1]), Boolean()) | |
135 | return Boolean() | |
136 | elif ast.value in ('+', '-', '*', '/'): | |
137 | type1 = self.type_of(ast.children[0]) | |
138 | type2 = self.type_of(ast.children[1]) | |
139 | self.assert_eq(type1, type2) | |
140 | self.assert_eq(type1, Integer()) | |
141 | return Integer() | |
142 | elif ast.value in ('==', '!=', '>', '>=', '<', '<='): | |
143 | type1 = self.type_of(ast.children[0]) | |
144 | type2 = self.type_of(ast.children[1]) | |
145 | self.assert_eq(type1, type2) | |
146 | return Boolean() | |
147 | elif ast.type == 'Not': | |
148 | type1 = self.type_of(ast.children[0]) | |
149 | self.assert_eq(type1, Boolean()) | |
150 | return Boolean() | |
151 | elif ast.type == 'IntLit': | |
152 | return Integer() | |
153 | elif ast.type == 'StrLit': | |
154 | return String() | |
155 | elif ast.type == 'FunLit': | |
156 | # TODO: should not be able to see anything but globals in here... | |
157 | # unless it's a closure. Oh, joy. | |
158 | self.context = ScopedContext({}, self.context) | |
159 | self.return_type = None | |
160 | arg_types = self.type_of(ast.children[0]) # args | |
161 | return_type = self.type_of(ast.children[1]) # body | |
162 | if self.return_type is not None: | |
163 | self.assert_eq(return_type, self.return_type) | |
164 | self.context = self.context.parent | |
165 | self.return_type = None | |
166 | # modify AST for compiler's benefit | |
167 | ast.children[1].value = 'function body' | |
168 | return Function(arg_types, return_type) | |
169 | elif ast.type == 'Args': | |
170 | types = [] | |
171 | for child in ast.children: | |
172 | types.append(self.type_of(child)) | |
173 | return types | |
174 | elif ast.type == 'Arg': | |
175 | return self.set(ast.value, self.type_of(ast.children[0])) | |
176 | elif ast.type == 'Type': | |
177 | map = { | |
178 | 'integer': Integer(), | |
179 | 'boolean': Boolean(), | |
180 | 'string': String(), | |
181 | 'void': Void(), | |
182 | } | |
183 | return map[ast.value] | |
184 | elif ast.type == 'FunType': | |
185 | return_type = self.type_of(ast.children[0]) | |
186 | return Function([self.type_of(c) for c in ast.children[1:]], | |
187 | return_type) | |
188 | elif ast.type == 'UnionType': | |
189 | return Union([self.type_of(c) for c in ast.children]) | |
190 | elif ast.type == 'StructType': | |
191 | return Struct(ast.value) | |
192 | elif ast.type == 'VarDecl': | |
193 | name = ast.value | |
194 | if name in self.context: | |
195 | raise SyntaxError('declaration of %s shadows previous' % name) | |
196 | self.assignable[name] = True | |
197 | self.set(name, self.type_of(ast.children[0])) | |
198 | return Void() | |
199 | elif ast.type == 'VarRef': | |
200 | return self.context[ast.value] | |
201 | elif ast.type == 'None': | |
202 | return Void() | |
203 | elif ast.type == 'FunCall': | |
204 | t1 = self.type_of(ast.children[0]) | |
205 | assert isinstance(t1, Function), \ | |
206 | '%r is not a function' % t1 | |
207 | if len(t1.arg_types) != len(ast.children) - 1: | |
208 | raise SyntaxError("argument mismatch") | |
209 | i = 0 | |
210 | for child in ast.children[1:]: | |
211 | self.assert_eq(self.type_of(child), t1.arg_types[i]) | |
212 | i += 1 | |
213 | return t1.return_type | |
214 | elif ast.type == 'Do': | |
215 | t1 = self.type_of(ast.children[0]) | |
216 | return Void() | |
217 | elif ast.type == 'Return': | |
218 | t1 = self.type_of(ast.children[0]) | |
219 | if self.return_type is None: | |
220 | self.return_type = t1 | |
221 | else: | |
222 | self.assert_eq(t1, self.return_type) | |
223 | return Void() | |
224 | elif ast.type == 'If': | |
225 | t1 = self.type_of(ast.children[0]) | |
226 | assert t1 == Boolean() | |
227 | t2 = self.type_of(ast.children[1]) | |
228 | if len(ast.children) == 3: | |
229 | t3 = self.type_of(ast.children[2]) | |
230 | self.assert_eq(t2, t3) | |
231 | return t2 | |
232 | else: | |
233 | return Void() | |
234 | elif ast.type == 'While': | |
235 | t1 = self.type_of(ast.children[0]) | |
236 | assert t1 == Boolean() | |
237 | t2 = self.type_of(ast.children[1]) | |
238 | return Void() | |
239 | elif ast.type == 'Block': | |
240 | self.context = ScopedContext({}, self.context) | |
241 | if len(ast.children) == 0: | |
242 | ast.t = Void() | |
243 | return Void() | |
244 | for child in ast.children[:-1]: | |
245 | self.assert_eq(self.type_of(child), Void()) | |
246 | last = self.type_of(ast.children[-1]) | |
247 | self.context = self.context.parent | |
248 | # modify the AST for the compiler's benefit | |
249 | ast.t = last | |
250 | return ast.t | |
251 | elif ast.type == 'Assignment': | |
252 | t1 = self.type_of(ast.children[0]) | |
253 | if not self.is_assignable(ast.children[0]): | |
254 | raise SyntaxError('cannot assign to non-local') | |
255 | t2 = self.type_of(ast.children[1]) | |
256 | self.assert_eq(t1, t2) | |
257 | return Void() | |
258 | elif ast.type == 'Make': | |
259 | t = self.type_of(ast.children[0]) | |
260 | if t.name not in self.structs: | |
261 | raise SyntaxError("undefined struct %s" % t.name) | |
262 | struct_defn = self.structs[t.name] | |
263 | if len(struct_defn.content_types) != len(ast.children) - 1: | |
264 | raise SyntaxError("argument mismatch") | |
265 | i = 0 | |
266 | for defn in ast.children[1:]: | |
267 | t1 = self.type_of(defn) | |
268 | self.assert_eq(t1, struct_defn.content_types[i]) | |
269 | i += 1 | |
270 | return t | |
271 | elif ast.type == 'Index': | |
272 | t = self.type_of(ast.children[0]) | |
273 | field_name = ast.value | |
274 | struct_fields = self.struct_fields[t.name] | |
275 | if field_name not in struct_fields: | |
276 | raise SyntaxError("undefined field") | |
277 | index = struct_fields[field_name] | |
278 | # we modify the AST for the evaluator's benefit. | |
279 | ast.value = index | |
280 | # we look up the type from the StructDefinition | |
281 | return self.structs[t.name].content_types[index] | |
282 | elif ast.type == 'TypeCase': | |
283 | t1 = self.type_of(ast.children[0]) | |
284 | t2 = self.type_of(ast.children[1]) | |
285 | ||
286 | if not isinstance(t1, Union): | |
287 | raise SyntaxError('bad typecase, %s not a union' % t1) | |
288 | if not t1.contains(t2): | |
289 | raise SyntaxError('bad typecase, %s not in %s' % (t2, t1)) | |
290 | # modify the AST for the evaluator's benefit | |
291 | ast.value = str(t2) | |
292 | ||
293 | # typecheck t3 with variable in children[0] having type t2 | |
294 | assert ast.children[0].type == 'VarRef' | |
295 | self.context = ScopedContext({}, self.context) | |
296 | self.context[ast.children[0].value] = t2 | |
297 | t3 = self.type_of(ast.children[2]) | |
298 | self.context = self.context.parent | |
299 | return t3 | |
300 | elif ast.type == 'Program': | |
301 | for defn in ast.children: | |
302 | t1 = self.type_of(defn) | |
303 | return Void() | |
304 | elif ast.type == 'Defn': | |
305 | # reset assignable | |
306 | self.assignable = {} | |
307 | t = self.type_of(ast.children[0]) | |
308 | if ast.value in self.forwards: | |
309 | self.assert_eq(self.forwards[ast.value], t) | |
310 | del self.forwards[ast.value] | |
311 | else: | |
312 | self.set(ast.value, t) | |
313 | if ast.value == 'main': | |
314 | # any return type is fine, for now, so, | |
315 | # we compare it against itself | |
316 | rt = t.return_type | |
317 | self.assert_eq(t, Function([], rt)) | |
318 | return t | |
319 | elif ast.type == 'Forward': | |
320 | t = self.type_of(ast.children[0]) | |
321 | self.forwards[ast.value] = t | |
322 | return self.set(ast.value, t) | |
323 | elif ast.type == 'StructDefn': | |
324 | pass | |
325 | elif ast.type == 'Cast': | |
326 | t1 = self.type_of(ast.children[0]) | |
327 | t2 = self.type_of(ast.children[1]) | |
328 | if not isinstance(t2, Union): | |
329 | raise SyntaxError('bad cast, not a union: %s' % t2) | |
330 | if not t2.contains(t1): | |
331 | raise SyntaxError('bad cast, %s does not include %s' % | |
332 | (t2, t1)) | |
333 | # we modify the ast here for the evaluator's benefit. | |
334 | ast.value = str(t1) | |
335 | return t2 | |
336 | else: | |
337 | raise NotImplementedError(repr(ast)) |