Merge pull request #3 from catseye/develop-0.5
Develop 0.5
Chris Pressey authored 3 years ago
GitHub committed 3 years ago
0 | History of Castile | |
1 | ================== | |
2 | ||
3 | Castile 0.5 | |
4 | ----------- | |
5 | ||
6 | ### Distribution | |
7 | ||
8 | * Added HISTORY.md file. | |
9 | ||
10 | ### Language | |
11 | ||
12 | * Scoped structs can be declared with the `for (...)` clause | |
13 | after the struct. A value of a scope struct can only be | |
14 | `make`d, and the fields of such a value can only be accessed, | |
15 | lexically inside one of the definitions named in the `for`. | |
16 | * Structs cannot be tested for equality with the `==` and `!=` | |
17 | operators. Instead the programmer should write a function | |
18 | that compares structs for equality, if desired. | |
19 | * Values of union type can be tested for equality, but only if | |
20 | none of the types involved in the union are structs. | |
21 | ||
22 | ### Implementation | |
23 | ||
24 | * Lexical scanner has been split off from parser code, into | |
25 | its own module. A performance bug (using O(n^2) space | |
26 | instead of O(n)) during scanning has also been fixed. | |
27 | * Line numbers are recorded in the AST when parsing, and | |
28 | reported on type errors when type errors occur. | |
29 | * Requesting the AST be dumped, will also dump the AST with | |
30 | type assignments, if an error occurs during type checking. | |
31 | * Established an abstract base class for compiler backends. | |
32 | * Fixed a bug where tagged values were being tagged again | |
33 | during a cast from a union type to another union type. | |
34 | * ArgumentParser is used instead of OptionParser to parse | |
35 | command-line arguments. `--version` added, `--test` (and | |
36 | remaining doctests in source modules) removed. | |
37 | ||
38 | Castile 0.4 | |
39 | ----------- | |
40 | ||
41 | ### Distribution | |
42 | ||
43 | * Re-focused project: Castile is a simple imperative language | |
44 | with union types. | |
45 | * Released under a 3-clause BSD license. | |
46 | ||
47 | ### Language | |
48 | ||
49 | * `struct`s cannot be compared for order, it is a static error. | |
50 | * A union type is allowed to be promoted to a bigger union type, | |
51 | or to itself. | |
52 | ||
53 | ### Implementation | |
54 | ||
55 | * Completed the C-generating backend of the compiler: it passes all tests now. | |
56 | * Implemented `str` builtin, equality testing of `struct`s in all backends. | |
57 | * Improved pretty-printing of code in C and Ruby backends. | |
58 | * Implemented `ne` in stackmac implementation. | |
59 | ||
60 | Castile 0.3 revision 2021.0625 | |
61 | ------------------------------ | |
62 | ||
63 | * Updated implementation to run under both Python 2 and Python 3. | |
64 | * Refactored test suite, making it more idiomatic Falderal. | |
65 | ||
66 | Castile 0.3 revision 2016.0331 | |
67 | ------------------------------ | |
68 | ||
69 | * Fixed generated Ruby code that worked in Ruby 1.8 but fails in Ruby 1.9. | |
70 | ||
71 | Castile 0.3 revision 2015.0101 | |
72 | ------------------------------ | |
73 | ||
74 | * Stopped using deprecated Falderal variable names in test suite. | |
75 | ||
76 | Castile 0.3 | |
77 | ----------- | |
78 | ||
79 | * Treatment of local variables became more Python-like. | |
80 | * Beginnings of a C backend in compiler. | |
81 | ||
82 | Castile 0.2 | |
83 | ----------- | |
84 | ||
85 | * Heavy development of the language, with many changes. | |
86 | * Added JavaScript, Ruby, and stackmac (stack-machine) backends to compiler. | |
87 | ||
88 | Castile 0.1 | |
89 | ----------- | |
90 | ||
91 | Initial release of Castile, an unremarkable language with an unremarkable | |
92 | compiler/interpreter in Python. |
0 | 0 | Castile |
1 | 1 | ======= |
2 | 2 | |
3 | Version 0.4 | _Entry_ [@ catseye.tc](https://catseye.tc/node/Castile) | |
3 | Version 0.5 | _Entry_ [@ catseye.tc](https://catseye.tc/node/Castile) | |
4 | 4 | | _See also:_ [Eightebed](https://github.com/catseye/Eightebed#readme) |
5 | 5 | ∘ [Dieter](https://github.com/catseye/Dieter#readme) |
6 | 6 |
1 | 1 | ---- |
2 | 2 | |
3 | 3 | ### Implementation ### |
4 | ||
5 | Line number reporting for Context errors and Syntax errors. | |
4 | 6 | |
5 | 7 | Name mangling for compilers (prepend with `_` most likely.) |
6 | 8 | |
10 | 12 | |
11 | 13 | Implement `int`, `chr`, `ord` for Ruby, JavaScript, stackmac, C. |
12 | 14 | |
13 | Struct equality is not properly deep in JavaScript or C. | |
14 | ||
15 | 15 | Better indentation in the JavaScript backend. |
16 | 16 | |
17 | 17 | TaggedValue -> just a tuple. |
20 | 20 | and void types in unions of (void, X) should only be one value. |
21 | 21 | (structs are still boxed though) |
22 | 22 | |
23 | AST nodes should have source line numbers, it would be really nice. | |
24 | ||
25 | 23 | Implement garbage collection of some sort in the C backend. Either that |
26 | 24 | or implement some kind of resource-awareness in the language itself. |
27 | 25 | |
28 | 26 | Other backends (Python? Java? CIL? Scheme?) |
29 | 27 | |
28 | Test framework: collect the backend-independent tests into a single | |
29 | file, and only test it once. Run all the *other* tests on every | |
30 | backend. | |
31 | ||
30 | 32 | ### Design ### |
31 | 33 | |
32 | 34 | Don't output final value. Command-line arguments passed to `main`. (`sysmain`?) |
33 | 35 | |
34 | Convenience: | |
36 | Automatic type promotion (upcasting), e.g. using an integer where | |
37 | integer|string is expected (as e.g. a function argument) is fine, | |
38 | an `as integer|string` should be automatically inserted. | |
35 | 39 | |
36 | * Should we have automatic promotion (value tagging?) | |
37 | Since it causes an operation, I think it should be explicit, but the | |
38 | explicit syntax could be more lightweight. | |
39 | * Lua-esque `:` operator: `a:b(c)` -> `a.b(a, c)` | |
40 | Lua-esque `:` operator: `a:b(c)` -> `a.b(a, c)` | |
40 | 41 | |
41 | 42 | Type promotion with higher precedence? So that it can be used at toplevel. |
42 | 43 |
4 | 4 | |
5 | 5 | Program ::= {Defn [";"]}. |
6 | 6 | Defn ::= "fun" ident "(" [Arg {"," Arg}] ")" Body |
7 | | "struct" ident "{" {ident ":" TExpr [";"]} "}" | |
7 | | "struct" ident "{" {ident ":" TExpr [";"]} "}" ["for" "(" [ident {"," ident}] ")"] | |
8 | 8 | | ident (":" TExpr0 | "=" Literal). |
9 | 9 | Arg ::= ident [":" TExpr1]. |
10 | 10 | Body ::= "{" {Stmt [";"]} "}". |
0 | /* | |
1 | * Implementation of an associative map in Castile. | |
2 | * | |
3 | * The map is implemented as an association list, | |
4 | * but this fact is hidden from clients, as only | |
5 | * the operations have access to the internals | |
6 | * of the struct. | |
7 | */ | |
8 | ||
9 | struct assoc { | |
10 | key: string; | |
11 | value: string; | |
12 | next: assoc|void; | |
13 | } for (update, lookup, remove, render) | |
14 | ||
15 | fun empty() { | |
16 | return null as assoc|void | |
17 | } | |
18 | ||
19 | fun update(k: string, v: string, a: assoc|void) { | |
20 | make assoc(key:k, value:v, next:a as assoc|void) | |
21 | } | |
22 | ||
23 | lookup : assoc|void, string -> string|void | |
24 | fun lookup(a: assoc|void, k: string) { | |
25 | typecase a is void { | |
26 | return null as string|void | |
27 | } | |
28 | typecase a is assoc { | |
29 | if a.key == k { | |
30 | return a.value as string|void | |
31 | } | |
32 | return lookup(a.next, k) | |
33 | } | |
34 | } | |
35 | ||
36 | remove : assoc|void, string -> assoc|void | |
37 | fun remove(a: assoc|void, k: string) { | |
38 | typecase a is void { | |
39 | return a as assoc|void | |
40 | } | |
41 | typecase a is assoc { | |
42 | if a.key == k { | |
43 | return remove(a.next, k) | |
44 | } | |
45 | return make assoc(key:a.key, value:a.value, next:remove(a.next, k)) as assoc|void | |
46 | } | |
47 | } | |
48 | ||
49 | render : assoc|void -> string | |
50 | fun render(a: assoc|void) { | |
51 | typecase a is void { | |
52 | return "" | |
53 | } | |
54 | typecase a is assoc { | |
55 | return concat(a.value, concat(",", render(a.next))) | |
56 | } | |
57 | } | |
58 | ||
59 | fun main() { | |
60 | a = update("3", "third", empty()); | |
61 | a = update("2", "second", a as assoc|void); | |
62 | a = update("1", "first", a as assoc|void); | |
63 | print(render(a as assoc|void)) | |
64 | b = remove((a as assoc|void), "2"); | |
65 | print(render(b)) | |
66 | r = lookup(b, "2"); | |
67 | typecase r is void { print("NOT FOUND"); } | |
68 | typecase r is string { print(r); } | |
69 | } |
0 | struct list { | |
1 | value: string; | |
2 | next: list|void; | |
3 | } | |
4 | ||
5 | fun empty() { | |
6 | return null as list|void | |
7 | } | |
8 | ||
9 | fun cons(v: string, l: list|void) { | |
10 | make list(value:v, next:l) as list|void | |
11 | } | |
12 | ||
13 | equal_list : list|void, list|void -> boolean | |
14 | fun equal_list(a: list|void, b: list|void) { | |
15 | typecase a is void { | |
16 | typecase b is void { | |
17 | return true | |
18 | } | |
19 | } | |
20 | typecase a is list { | |
21 | typecase b is list { | |
22 | return a.value == b.value and equal_list(a.next, b.next) | |
23 | } | |
24 | } | |
25 | return false | |
26 | } | |
27 | ||
28 | length : list|void -> integer | |
29 | fun length(l: list|void) { | |
30 | typecase l is void { return 0 } | |
31 | typecase l is list { return 1 + length(l.next) } | |
32 | } | |
33 | ||
34 | main = fun() { | |
35 | l1 = cons("first", cons("second", cons("third", empty()))); | |
36 | l2 = cons("first", cons("second", cons("third", empty()))); | |
37 | l3 = cons("first", cons("second", empty())); | |
38 | ||
39 | print(str(length(l1 as list|void))); | |
40 | ||
41 | if (equal_list(l1, l2) and not equal_list(l2, l3)) { | |
42 | print("Yep, story checks out") | |
43 | } | |
44 | } |
0 | struct person { name: string }; | |
1 | fun foo(a, b: integer|string) { | |
2 | r = a; | |
3 | typecase b is integer { | |
4 | r = r + b; | |
5 | }; | |
6 | typecase b is person { | |
7 | r = r + len(b); | |
8 | }; | |
9 | r | |
10 | } | |
11 | main = fun() { | |
12 | a = 0; | |
13 | a = foo(a, 333 as integer|string); | |
14 | a = foo(a, "hiya" as integer|string); | |
15 | a /* should output 337 */ | |
16 | } |
8 | 8 | r |
9 | 9 | } |
10 | 10 | main = fun() { |
11 | a = 0; | |
11 | 12 | a = foo(a, 333 as integer|string); |
12 | 13 | a = foo(a, "hiya" as integer|string); |
13 | 14 | a /* should output 337 */ |
0 | 0 | class AST(object): |
1 | def __init__(self, tag, children=None, value=None, type=None, aux=None): | |
1 | def __init__(self, tag, children=None, value=None, type=None, aux=None, line=None): | |
2 | 2 | self.tag = tag |
3 | self.line = line | |
3 | 4 | self.value = value |
4 | 5 | # typechecker may populate this. parser will not. |
5 | 6 | self.type = type |
0 | """Abstract base class for Castile compiler backends, | |
1 | especially source-to-source.""" | |
2 | ||
3 | class BaseCompiler(object): | |
4 | def __init__(self, out): | |
5 | self.out = out | |
6 | self.indent = 0 | |
7 | ||
8 | def commas(self, asts, sep=','): | |
9 | if asts: | |
10 | for child in asts[:-1]: | |
11 | self.compile(child) | |
12 | self.out.write(sep) | |
13 | self.compile(asts[-1]) | |
14 | ||
15 | def write(self, x): | |
16 | self.out.write(x) | |
17 | ||
18 | def write_indent(self, x): | |
19 | self.out.write(' ' * self.indent) | |
20 | self.out.write(x) |
0 | from castile.backends.base import BaseCompiler | |
0 | 1 | from castile.transformer import VarDeclTypeAssigner |
1 | 2 | from castile.types import ( |
2 | 3 | Integer, String, Void, Boolean, Function, Union, Struct |
8 | 9 | } |
9 | 10 | |
10 | 11 | PRELUDE = r""" |
11 | /* AUTOMATICALLY GENERATED -- EDIT AT OWN RISK */ | |
12 | /* AUTOMATICALLY GENERATED -- EDIT AT YOUR OWN RISK */ | |
12 | 13 | |
13 | 14 | #include <stdio.h> |
14 | 15 | #include <stdlib.h> |
72 | 73 | return !strcmp(tag, tv->tag); |
73 | 74 | } |
74 | 75 | |
76 | int equal_tagged_value(struct tagged_value *tv1, struct tagged_value *tv2) | |
77 | { | |
78 | return is_tag(tv1->tag, tv2) && tv1->value == tv2->value; | |
79 | } | |
80 | ||
75 | 81 | """ |
76 | 82 | |
77 | 83 | |
78 | class Compiler(object): | |
84 | class Compiler(BaseCompiler): | |
79 | 85 | def __init__(self, out): |
80 | self.out = out | |
86 | super(Compiler, self).__init__(out) | |
81 | 87 | self.main_type = None |
82 | self.indent = 0 | |
83 | 88 | self.typecasing = set() |
84 | ||
85 | def commas(self, asts, sep=','): | |
86 | if asts: | |
87 | for child in asts[:-1]: | |
88 | self.compile(child) | |
89 | self.out.write(sep) | |
90 | self.compile(asts[-1]) | |
91 | ||
92 | def write(self, x): | |
93 | self.out.write(x) | |
94 | ||
95 | def write_indent(self, x): | |
96 | self.out.write(' ' * self.indent) | |
97 | self.out.write(x) | |
98 | 89 | |
99 | 90 | # as used in local variable declarations |
100 | 91 | def c_type(self, type): |
177 | 168 | elif ast.tag == 'Forward': |
178 | 169 | self.write_indent('extern %s;\n' % self.c_decl(ast.children[0].type, ast.value)) |
179 | 170 | elif ast.tag == 'StructDefn': |
171 | field_defns = ast.children[0].children | |
180 | 172 | self.write_indent('struct %s {\n' % ast.value) |
181 | 173 | self.indent += 1 |
182 | for child in ast.children: | |
174 | for child in field_defns: | |
175 | assert child.tag == 'FieldDefn', child.tag | |
183 | 176 | self.compile(child) |
184 | 177 | self.indent -= 1 |
185 | 178 | self.write_indent('};\n\n') |
186 | 179 | self.write_indent('struct %s * make_%s(' % (ast.value, ast.value)) |
187 | 180 | |
188 | if ast.children: | |
189 | for child in ast.children[:-1]: | |
190 | assert child.tag == 'FieldDefn' | |
181 | if field_defns: | |
182 | for child in field_defns[:-1]: | |
183 | assert child.tag == 'FieldDefn', child.tag | |
191 | 184 | self.write('%s, ' % self.c_decl(child.children[0].type, child.value)) |
192 | child = ast.children[-1] | |
193 | assert child.tag == 'FieldDefn' | |
185 | child = field_defns[-1] | |
186 | assert child.tag == 'FieldDefn', child.tag | |
194 | 187 | self.write('%s' % self.c_decl(child.children[0].type, child.value)) |
195 | 188 | |
196 | 189 | self.write(') {\n') |
197 | 190 | self.indent += 1 |
198 | 191 | self.write_indent('struct %s *x = malloc(sizeof(struct %s));\n' % (ast.value, ast.value)) |
199 | 192 | |
200 | for child in ast.children: | |
201 | assert child.tag == 'FieldDefn' | |
193 | for child in field_defns: | |
194 | assert child.tag == 'FieldDefn', child.tag | |
202 | 195 | self.write_indent('x->%s = %s;\n' % (child.value, child.value)) |
203 | 196 | |
204 | 197 | self.write_indent('return x;\n') |
205 | self.indent -= 1 | |
206 | self.write_indent('}\n\n') | |
207 | ||
208 | self.write_indent('int equal_%s(struct %s * a, struct %s * b) {\n' % (ast.value, ast.value, ast.value)) | |
209 | ||
210 | self.indent += 1 | |
211 | for child in ast.children: | |
212 | assert child.tag == 'FieldDefn' | |
213 | # TODO does not handle structs within structs | |
214 | self.write_indent('if (a->%s != b->%s) return 0;\n' % (child.value, child.value)) | |
215 | ||
216 | self.write_indent('return 1;\n') | |
217 | 198 | self.indent -= 1 |
218 | 199 | self.write_indent('}\n\n') |
219 | 200 | |
256 | 237 | self.indent -= 1 |
257 | 238 | elif ast.tag == 'Op': |
258 | 239 | if ast.value == '==' and isinstance(ast.children[0].type, Struct): |
259 | self.write('equal_%s(' % ast.children[0].type.name) | |
240 | raise NotImplementedError('structs cannot be compared for equality') | |
241 | elif ast.value == '==' and isinstance(ast.children[0].type, Union): | |
242 | self.write('equal_tagged_value(') | |
260 | 243 | self.compile(ast.children[0]) |
261 | 244 | self.write(', ') |
262 | 245 | self.compile(ast.children[1]) |
342 | 325 | self.compile(ast.children[0]) |
343 | 326 | self.write('->%s' % ast.value) |
344 | 327 | elif ast.tag == 'TypeCast': |
345 | self.write('tag("%s",(void *)' % str(ast.children[0].type)) | |
346 | self.compile(ast.children[0]) | |
347 | self.write(')') | |
328 | # If the LHS is not already a union type, promote it to a tagged value. | |
329 | if isinstance(ast.children[0].type, Union): | |
330 | self.compile(ast.children[0]) | |
331 | else: | |
332 | self.write('tag("%s",(void *)' % str(ast.children[0].type)) | |
333 | self.compile(ast.children[0]) | |
334 | self.write(')') | |
348 | 335 | elif ast.tag == 'TypeCase': |
349 | 336 | self.write_indent('if (is_tag("%s",' % str(ast.children[1].type)) |
350 | 337 | self.compile(ast.children[0]) |
0 | from castile.types import Struct | |
0 | from castile.backends.base import BaseCompiler | |
1 | from castile.types import Struct, Union | |
1 | 2 | |
2 | 3 | OPS = { |
3 | 4 | 'and': '&&', |
5 | 6 | '==': '===', |
6 | 7 | } |
7 | 8 | |
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.tag == 'Program': | |
22 | self.out.write("""\ | |
23 | /* AUTOMATICALLY GENERATED -- EDIT AT OWN RISK */ | |
9 | PRELUDE = r""" | |
10 | /* AUTOMATICALLY GENERATED -- EDIT AT YOUR OWN RISK */ | |
24 | 11 | |
25 | 12 | /* |
26 | 13 | var stdin = process.openStdin(); |
55 | 42 | } |
56 | 43 | }; |
57 | 44 | |
58 | """) | |
59 | for child in ast.children: | |
60 | self.compile(child) | |
61 | self.out.write("""\ | |
45 | var equal_tagged_value = function(tv1, tv2) | |
46 | { | |
47 | return (tv1.tag === tv2.tag) && (tv1.value === tv2.value); | |
48 | } | |
49 | """ | |
50 | ||
51 | POSTLUDE = """\ | |
62 | 52 | |
63 | 53 | var result = main(); |
64 | 54 | if (result !== undefined && result !== null) |
65 | 55 | print(repr(result)); |
66 | """) | |
56 | """ | |
57 | ||
58 | ||
59 | class Compiler(BaseCompiler): | |
60 | ||
61 | def compile(self, ast): | |
62 | if ast.tag == 'Program': | |
63 | self.write(PRELUDE) | |
64 | for child in ast.children: | |
65 | self.compile(child) | |
66 | self.write(POSTLUDE) | |
67 | 67 | elif ast.tag == 'Defn': |
68 | self.out.write('var %s = ' % ast.value) | |
68 | self.write('var %s = ' % ast.value) | |
69 | 69 | self.compile(ast.children[0]) |
70 | self.out.write(';\n') | |
70 | self.write(';\n') | |
71 | 71 | elif ast.tag == 'Forward': |
72 | 72 | pass |
73 | 73 | elif ast.tag == 'StructDefn': |
74 | self.out.write('function equal_%s(a, b) {\n' % ast.value) | |
75 | for child in ast.children: | |
76 | assert child.tag == 'FieldDefn' | |
77 | # TODO does not handle structs within structs | |
78 | self.out.write('if (a.%s !== b.%s) return false;\n' % (child.value, child.value)) | |
79 | self.out.write('return true;\n') | |
80 | self.out.write('}\n\n') | |
74 | pass | |
81 | 75 | elif ast.tag == 'FunLit': |
82 | self.out.write('function(') | |
76 | self.write('function(') | |
83 | 77 | self.compile(ast.children[0]) |
84 | self.out.write(')\n') | |
78 | self.write(')\n') | |
85 | 79 | self.compile(ast.children[1]) |
86 | 80 | elif ast.tag == 'Args': |
87 | 81 | self.commas(ast.children) |
88 | 82 | elif ast.tag == 'Arg': |
89 | self.out.write(ast.value) | |
83 | self.write(ast.value) | |
90 | 84 | elif ast.tag == 'Body': |
91 | self.out.write('{') | |
85 | self.write('{') | |
92 | 86 | self.compile(ast.children[0]) |
93 | 87 | assert ast.children[1].tag == 'Block' |
94 | 88 | block = ast.children[1] |
95 | 89 | for child in block.children: |
96 | 90 | self.compile(child) |
97 | self.out.write(';\n') | |
98 | self.out.write('}') | |
91 | self.write(';\n') | |
92 | self.write('}') | |
99 | 93 | elif ast.tag == 'VarDecls': |
100 | 94 | for child in ast.children: |
101 | 95 | self.compile(child) |
102 | 96 | elif ast.tag == 'VarDecl': |
103 | self.out.write('var %s;\n' % ast.value) | |
97 | self.write('var %s;\n' % ast.value) | |
104 | 98 | elif ast.tag == 'Block': |
105 | self.out.write('{') | |
99 | self.write('{') | |
106 | 100 | for child in ast.children: |
107 | 101 | self.compile(child) |
108 | self.out.write(';\n') | |
109 | self.out.write('}') | |
102 | self.write(';\n') | |
103 | self.write('}') | |
110 | 104 | elif ast.tag == 'While': |
111 | self.out.write('while (') | |
105 | self.write('while (') | |
112 | 106 | self.compile(ast.children[0]) |
113 | self.out.write(')') | |
107 | self.write(')') | |
114 | 108 | self.compile(ast.children[1]) |
115 | 109 | elif ast.tag == 'Op': |
116 | 110 | if ast.value == '==' and isinstance(ast.children[0].type, Struct): |
117 | self.out.write('equal_%s(' % ast.children[0].type.name) | |
111 | raise NotImplementedError('structs cannot be compared for equality') | |
112 | elif ast.value == '==' and isinstance(ast.children[0].type, Union): | |
113 | self.write('equal_tagged_value(') | |
118 | 114 | self.compile(ast.children[0]) |
119 | self.out.write(', ') | |
115 | self.write(', ') | |
120 | 116 | self.compile(ast.children[1]) |
121 | self.out.write(')') | |
117 | self.write(')') | |
122 | 118 | else: |
123 | self.out.write('(') | |
119 | self.write('(') | |
124 | 120 | self.compile(ast.children[0]) |
125 | self.out.write(' %s ' % OPS.get(ast.value, ast.value)) | |
121 | self.write(' %s ' % OPS.get(ast.value, ast.value)) | |
126 | 122 | self.compile(ast.children[1]) |
127 | self.out.write(')') | |
123 | self.write(')') | |
128 | 124 | elif ast.tag == 'VarRef': |
129 | self.out.write(ast.value) | |
125 | self.write(ast.value) | |
130 | 126 | elif ast.tag == 'FunCall': |
131 | 127 | self.compile(ast.children[0]) |
132 | self.out.write('(') | |
128 | self.write('(') | |
133 | 129 | self.commas(ast.children[1:]) |
134 | self.out.write(')') | |
130 | self.write(')') | |
135 | 131 | elif ast.tag == 'If': |
136 | self.out.write('if(') | |
132 | self.write('if(') | |
137 | 133 | self.compile(ast.children[0]) |
138 | self.out.write(')') | |
134 | self.write(')') | |
139 | 135 | if len(ast.children) == 3: # if-else |
140 | 136 | self.compile(ast.children[1]) |
141 | self.out.write(' else ') | |
137 | self.write(' else ') | |
142 | 138 | self.compile(ast.children[2]) |
143 | 139 | else: # just-if |
144 | 140 | self.compile(ast.children[1]) |
145 | 141 | elif ast.tag == 'Return': |
146 | self.out.write('return ') | |
142 | self.write('return ') | |
147 | 143 | self.compile(ast.children[0]) |
148 | 144 | elif ast.tag == 'Break': |
149 | self.out.write('break') | |
145 | self.write('break') | |
150 | 146 | elif ast.tag == 'Not': |
151 | self.out.write('!(') | |
147 | self.write('!(') | |
152 | 148 | self.compile(ast.children[0]) |
153 | self.out.write(')') | |
149 | self.write(')') | |
154 | 150 | elif ast.tag == 'None': |
155 | self.out.write('null') | |
151 | self.write('null') | |
156 | 152 | elif ast.tag == 'IntLit': |
157 | self.out.write(str(ast.value)) | |
153 | self.write(str(ast.value)) | |
158 | 154 | elif ast.tag == 'StrLit': |
159 | self.out.write("'%s'" % ast.value) | |
155 | self.write("'%s'" % ast.value) | |
160 | 156 | elif ast.tag == 'BoolLit': |
161 | 157 | if ast.value: |
162 | self.out.write("true") | |
158 | self.write("true") | |
163 | 159 | else: |
164 | self.out.write("false") | |
160 | self.write("false") | |
165 | 161 | elif ast.tag == 'Assignment': |
166 | 162 | self.compile(ast.children[0]) |
167 | self.out.write(' = ') | |
163 | self.write(' = ') | |
168 | 164 | self.compile(ast.children[1]) |
169 | 165 | elif ast.tag == 'Make': |
170 | self.out.write('{') | |
166 | self.write('{') | |
171 | 167 | self.commas(ast.children[1:]) |
172 | self.out.write('}') | |
168 | self.write('}') | |
173 | 169 | elif ast.tag == 'FieldInit': |
174 | self.out.write("'%s':" % ast.value) | |
170 | self.write("'%s':" % ast.value) | |
175 | 171 | self.compile(ast.children[0]) |
176 | 172 | elif ast.tag == 'Index': |
177 | 173 | self.compile(ast.children[0]) |
178 | self.out.write('.%s' % ast.value) | |
174 | self.write('.%s' % ast.value) | |
179 | 175 | elif ast.tag == 'TypeCast': |
180 | self.out.write("['%s'," % str(ast.children[0].type)) | |
176 | # If the LHS is not already a union type, promote it to a tagged value. | |
177 | if isinstance(ast.children[0].type, Union): | |
178 | self.compile(ast.children[0]) | |
179 | else: | |
180 | self.write("['%s'," % str(ast.children[0].type)) | |
181 | self.compile(ast.children[0]) | |
182 | self.write(']') | |
183 | elif ast.tag == 'TypeCase': | |
184 | self.write('if (') | |
181 | 185 | self.compile(ast.children[0]) |
182 | self.out.write(']') | |
183 | elif ast.tag == 'TypeCase': | |
184 | self.out.write('if (') | |
186 | self.write("[0] == '%s')" % str(ast.children[1].type)) | |
187 | self.write('{ var save=') | |
185 | 188 | self.compile(ast.children[0]) |
186 | self.out.write("[0] == '%s')" % str(ast.children[1].type)) | |
187 | self.out.write('{ var save=') | |
189 | self.write('; ') | |
188 | 190 | self.compile(ast.children[0]) |
189 | self.out.write('; ') | |
191 | self.write('=') | |
190 | 192 | self.compile(ast.children[0]) |
191 | self.out.write('=') | |
192 | self.compile(ast.children[0]) | |
193 | self.out.write('[1]; ') | |
193 | self.write('[1]; ') | |
194 | 194 | self.compile(ast.children[2]) |
195 | 195 | self.compile(ast.children[0]) |
196 | self.out.write(' =save; }') | |
196 | self.write(' =save; }') | |
197 | 197 | else: |
198 | 198 | raise NotImplementedError(repr(ast)) |
0 | from castile.backends.base import BaseCompiler | |
1 | from castile.types import Union | |
2 | ||
3 | ||
0 | 4 | OPS = { |
1 | 5 | } |
2 | 6 | |
3 | 7 | PRELUDE = """\ |
4 | # AUTOMATICALLY GENERATED -- EDIT AT OWN RISK | |
8 | # AUTOMATICALLY GENERATED -- EDIT AT YOUR OWN RISK | |
5 | 9 | |
6 | 10 | input = lambda { |s| |
7 | 11 | print(s) |
63 | 67 | """ |
64 | 68 | |
65 | 69 | |
66 | class Compiler(object): | |
67 | def __init__(self, out): | |
68 | self.out = out | |
69 | self.indent = 0 | |
70 | ||
71 | def commas(self, asts, sep=','): | |
72 | if asts: | |
73 | for child in asts[:-1]: | |
74 | self.compile(child) | |
75 | self.out.write(sep) | |
76 | self.compile(asts[-1]) | |
77 | ||
78 | def write(self, x): | |
79 | self.out.write(x) | |
80 | ||
81 | def write_indent(self, x): | |
82 | self.out.write(' ' * self.indent) | |
83 | self.out.write(x) | |
84 | ||
70 | class Compiler(BaseCompiler): | |
85 | 71 | def mangle(self, ident): |
86 | 72 | if ident.startswith('next'): |
87 | 73 | return '{}_'.format(ident) |
200 | 186 | self.compile(ast.children[0]) |
201 | 187 | self.write('["%s"]' % ast.value) |
202 | 188 | elif ast.tag == 'TypeCast': |
203 | self.write("['%s'," % str(ast.children[0].type)) | |
204 | self.compile(ast.children[0]) | |
205 | self.write(']') | |
189 | # If the LHS is not already a union type, promote it to a tagged value. | |
190 | if isinstance(ast.children[0].type, Union): | |
191 | self.compile(ast.children[0]) | |
192 | else: | |
193 | self.write("['%s'," % str(ast.children[0].type)) | |
194 | self.compile(ast.children[0]) | |
195 | self.write(']') | |
206 | 196 | elif ast.tag == 'TypeCase': |
207 | 197 | self.write_indent('if (') |
208 | 198 | self.compile(ast.children[0]) |
209 | 199 | self.write("[0] == '%s')" % str(ast.children[1].type)) |
210 | self.write('then save=') | |
200 | self.write(' then\n') | |
201 | self.indent += 1 | |
202 | self.write_indent('save=') | |
211 | 203 | self.compile(ast.children[0]) |
212 | 204 | self.write('\n') |
205 | self.write_indent('') | |
213 | 206 | self.compile(ast.children[0]) |
214 | 207 | self.write('=') |
215 | 208 | self.compile(ast.children[0]) |
216 | 209 | self.write('[1]\n') |
217 | 210 | self.compile(ast.children[2]) |
218 | self.compile(ast.children[0]) | |
219 | self.write(' = save end') | |
211 | self.write_indent('') | |
212 | self.compile(ast.children[0]) | |
213 | self.write(' = save\n') | |
214 | self.indent -= 1 | |
215 | self.write_indent('end') | |
220 | 216 | else: |
221 | 217 | raise NotImplementedError(repr(ast)) |
88 | 88 | def compile(self, ast): |
89 | 89 | if ast.tag == 'Program': |
90 | 90 | self.out.write("""\ |
91 | ; AUTOMATICALLY GENERATED -- EDIT AT OWN RISK | |
91 | ; AUTOMATICALLY GENERATED -- EDIT AT YOUR OWN RISK | |
92 | 92 | |
93 | 93 | """) |
94 | 94 | for child in ast.children: |
237 | 237 | elif ast.tag == 'TypeCast': |
238 | 238 | self.compile(ast.children[0]) |
239 | 239 | t = str(ast.children[0].type) |
240 | self.out.write('; tag with "%s"\n' % t) | |
241 | if self.size_of(ast.children[0].type) == 0: | |
242 | # special case. there is nothing on the stack | |
243 | self.out.write('push 0\n') | |
244 | tag = self.get_tag(t) | |
245 | self.out.write('tag %d\n' % tag) | |
240 | # If the LHS is not already a union type, promote it to a tagged value. | |
241 | if not isinstance(ast.children[0].type, Union): | |
242 | self.out.write('; tag with "%s"\n' % t) | |
243 | if self.size_of(ast.children[0].type) == 0: | |
244 | # special case. there is nothing on the stack | |
245 | self.out.write('push 0\n') | |
246 | tag = self.get_tag(t) | |
247 | self.out.write('tag %d\n' % tag) | |
246 | 248 | elif ast.tag == 'TypeCase': |
247 | 249 | end_typecase = self.get_label('end_typecase') |
248 | 250 | self.compile(ast.children[0]) |
9 | 9 | |
10 | 10 | def __repr__(self): |
11 | 11 | return '(%r, %r)' % (self.tag, self.value) |
12 | ||
13 | def __eq__(self, other): | |
14 | return self.tag == other.tag and self.value == other.value | |
12 | 15 | |
13 | 16 | |
14 | 17 | def builtin_len(s): |
5 | 5 | |
6 | 6 | |
7 | 7 | class CastileTypeError(ValueError): |
8 | pass | |
8 | def __init__(self, ast, message, *args, **kwargs): | |
9 | message = 'line {}: {}'.format(ast.line, message) | |
10 | super(CastileTypeError, self).__init__(message, *args, **kwargs) | |
9 | 11 | |
10 | 12 | |
11 | 13 | class StructDefinition(object): |
12 | def __init__(self, name, field_names, content_types): | |
14 | def __init__(self, name, field_names, content_types, scope_idents): | |
13 | 15 | self.name = name |
14 | 16 | self.field_names = field_names # dict of name -> position |
15 | 17 | self.content_types = content_types # list of types in order |
18 | self.scope_idents = scope_idents # list of identifiers, or None | |
16 | 19 | |
17 | 20 | def field_names_in_order(self): |
18 | 21 | m = {} |
32 | 35 | self.context = ScopedContext(global_context, level='global') |
33 | 36 | self.toplevel_context = ScopedContext({}, self.context, level='toplevel') |
34 | 37 | self.context = self.toplevel_context |
38 | self.current_defn = None | |
35 | 39 | |
36 | 40 | self.forwards = {} |
37 | 41 | self.structs = {} # struct name -> StructDefinition |
46 | 50 | print('%s: %s' % (name, type)) |
47 | 51 | return type |
48 | 52 | |
49 | def assert_eq(self, t1, t2): | |
53 | def assert_eq(self, ast, t1, t2): | |
50 | 54 | if t1 == t2: |
51 | 55 | return |
52 | raise CastileTypeError("type mismatch: %s != %s" % (t1, t2)) | |
56 | raise CastileTypeError(ast, "type mismatch: %s != %s" % (t1, t2)) | |
53 | 57 | |
54 | 58 | def collect_structs(self, ast): |
55 | 59 | for child in ast.children: |
59 | 63 | def collect_struct(self, ast): |
60 | 64 | name = ast.value |
61 | 65 | if name in self.structs: |
62 | raise CastileTypeError('duplicate struct %s' % name) | |
66 | raise CastileTypeError(ast, 'duplicate struct %s' % name) | |
63 | 67 | struct_fields = {} |
64 | te = [] | |
68 | type_exprs = [] | |
65 | 69 | i = 0 |
66 | for child in ast.children: | |
67 | assert child.tag == 'FieldDefn' | |
70 | field_defns = ast.children[0].children | |
71 | scope_idents = None | |
72 | if len(ast.children) > 1: | |
73 | scope_idents = [a.value for a in ast.children[1].children] | |
74 | for child in field_defns: | |
75 | assert child.tag == 'FieldDefn', child.tag | |
68 | 76 | field_name = child.value |
69 | 77 | if field_name in struct_fields: |
70 | raise CastileTypeError('already-defined field %s' % field_name) | |
78 | raise CastileTypeError(child, 'already-defined field %s' % field_name) | |
71 | 79 | struct_fields[field_name] = i |
72 | 80 | i += 1 |
73 | te.append(self.type_of(child.children[0])) | |
74 | self.structs[name] = StructDefinition(ast.value, struct_fields, te) | |
81 | type_exprs.append(self.type_of(child.children[0])) | |
82 | self.structs[name] = StructDefinition(ast.value, struct_fields, type_exprs, scope_idents) | |
75 | 83 | |
76 | 84 | def resolve_structs(self, ast): |
77 | 85 | if isinstance(ast.type, Struct): |
78 | 86 | if ast.type.name not in self.structs: |
79 | raise CastileTypeError('undefined struct %s' % ast.type.name) | |
87 | raise CastileTypeError(ast, 'undefined struct %s' % ast.type.name) | |
80 | 88 | ast.type.defn = self.structs[ast.type.name] |
81 | 89 | for child in ast.children: |
82 | 90 | self.resolve_structs(child) |
85 | 93 | def type_of(self, ast): |
86 | 94 | if ast.tag == 'Op': |
87 | 95 | if ast.value in ('and', 'or'): |
88 | self.assert_eq(self.type_of(ast.children[0]), Boolean()) | |
89 | self.assert_eq(self.type_of(ast.children[1]), Boolean()) | |
96 | self.assert_eq(ast, self.type_of(ast.children[0]), Boolean()) | |
97 | self.assert_eq(ast, self.type_of(ast.children[1]), Boolean()) | |
90 | 98 | ast.type = Boolean() |
91 | 99 | elif ast.value in ('+', '-', '*', '/'): |
92 | 100 | type1 = self.type_of(ast.children[0]) |
93 | 101 | type2 = self.type_of(ast.children[1]) |
94 | self.assert_eq(type1, type2) | |
95 | self.assert_eq(type1, Integer()) | |
102 | self.assert_eq(ast, type1, type2) | |
103 | self.assert_eq(ast, type1, Integer()) | |
96 | 104 | ast.type = Integer() |
97 | 105 | elif ast.value in ('==', '!=', '>', '>=', '<', '<='): |
98 | 106 | type1 = self.type_of(ast.children[0]) |
99 | 107 | type2 = self.type_of(ast.children[1]) |
100 | self.assert_eq(type1, type2) | |
101 | if ast.value in ('>', '>=', '<', '<=') and isinstance(type1, Struct): | |
102 | raise CastileTypeError("structs cannot be compared for order") | |
108 | self.assert_eq(ast, type1, type2) | |
109 | if isinstance(type1, Struct): | |
110 | raise CastileTypeError(ast, "structs cannot be compared") | |
111 | if isinstance(type1, Union) and type1.contains_instance_of(Struct): | |
112 | raise CastileTypeError(ast, "unions containing structs cannot be compared") | |
103 | 113 | ast.type = Boolean() |
104 | 114 | elif ast.tag == 'Not': |
105 | 115 | type1 = self.type_of(ast.children[0]) |
106 | self.assert_eq(type1, Boolean()) | |
116 | self.assert_eq(ast, type1, Boolean()) | |
107 | 117 | ast.type = Boolean() |
108 | 118 | elif ast.tag == 'IntLit': |
109 | 119 | ast.type = Integer() |
113 | 123 | ast.type = Boolean() |
114 | 124 | elif ast.tag == 'FunLit': |
115 | 125 | save_context = self.context |
116 | self.context = ScopedContext({}, self.toplevel_context, | |
117 | level='argument') | |
126 | self.context = ScopedContext( | |
127 | {}, self.toplevel_context, level='argument' | |
128 | ) | |
118 | 129 | self.return_type = None |
119 | 130 | arg_types = self.type_of(ast.children[0]) # args |
120 | 131 | t = self.type_of(ast.children[1]) # body |
121 | self.assert_eq(t, Void()) | |
132 | self.assert_eq(ast, t, Void()) | |
122 | 133 | self.context = save_context |
123 | 134 | return_type = self.return_type |
124 | 135 | self.return_type = None |
143 | 154 | elif ast.tag == 'Body': |
144 | 155 | self.context = ScopedContext({}, self.context, |
145 | 156 | level='local') |
146 | self.assert_eq(self.type_of(ast.children[1]), Void()) | |
157 | self.assert_eq(ast, self.type_of(ast.children[1]), Void()) | |
147 | 158 | self.context = self.context.parent |
148 | 159 | ast.type = Void() |
149 | 160 | elif ast.tag == 'FunType': |
156 | 167 | for c in ast.children: |
157 | 168 | type_ = self.type_of(c) |
158 | 169 | if type_ in types: |
159 | raise CastileTypeError("bad union type") | |
170 | raise CastileTypeError(c, "bad union type") | |
160 | 171 | types.append(type_) |
161 | 172 | ast.type = Union(types) |
162 | 173 | elif ast.tag == 'StructType': |
171 | 182 | assert isinstance(t1, Function), \ |
172 | 183 | '%r is not a function' % t1 |
173 | 184 | if len(t1.arg_types) != len(ast.children) - 1: |
174 | raise CastileTypeError("argument mismatch") | |
185 | raise CastileTypeError(ast, "argument mismatch") | |
175 | 186 | i = 0 |
176 | 187 | for child in ast.children[1:]: |
177 | self.assert_eq(self.type_of(child), t1.arg_types[i]) | |
188 | self.assert_eq(ast, self.type_of(child), t1.arg_types[i]) | |
178 | 189 | i += 1 |
179 | 190 | ast.type = t1.return_type |
180 | 191 | elif ast.tag == 'Return': |
182 | 193 | if self.return_type is None: |
183 | 194 | self.return_type = t1 |
184 | 195 | else: |
185 | self.assert_eq(t1, self.return_type) | |
196 | self.assert_eq(ast, t1, self.return_type) | |
186 | 197 | ast.type = Void() |
187 | 198 | elif ast.tag == 'Break': |
188 | 199 | ast.type = Void() |
195 | 206 | if len(ast.children) == 3: |
196 | 207 | # TODO useless! is void. |
197 | 208 | t3 = self.type_of(ast.children[2]) |
198 | self.assert_eq(t2, t3) | |
209 | self.assert_eq(ast, t2, t3) | |
199 | 210 | ast.type = t2 |
200 | 211 | else: |
201 | 212 | ast.type = Void() |
204 | 215 | within_control = self.within_control |
205 | 216 | self.within_control = True |
206 | 217 | t1 = self.type_of(ast.children[0]) |
207 | self.assert_eq(t1, Boolean()) | |
218 | self.assert_eq(ast, t1, Boolean()) | |
208 | 219 | t2 = self.type_of(ast.children[1]) |
209 | 220 | ast.type = Void() |
210 | 221 | self.within_control = within_control |
211 | 222 | elif ast.tag == 'Block': |
212 | 223 | for child in ast.children: |
213 | self.assert_eq(self.type_of(child), Void()) | |
224 | self.assert_eq(ast, self.type_of(child), Void()) | |
214 | 225 | ast.type = Void() |
215 | 226 | elif ast.tag == 'Assignment': |
216 | 227 | t2 = self.type_of(ast.children[1]) |
218 | 229 | name = ast.children[0].value |
219 | 230 | if ast.aux == 'defining instance': |
220 | 231 | if self.within_control: |
221 | raise CastileTypeError('definition of %s within control block' % name) | |
232 | raise CastileTypeError(ast, 'definition of %s within control block' % name) | |
222 | 233 | if name in self.context: |
223 | raise CastileTypeError('definition of %s shadows previous' % name) | |
234 | raise CastileTypeError(ast, 'definition of %s shadows previous' % name) | |
224 | 235 | self.set(name, t2) |
225 | 236 | t1 = t2 |
226 | 237 | else: |
227 | 238 | if name not in self.context: |
228 | raise CastileTypeError('variable %s used before definition' % name) | |
239 | raise CastileTypeError(ast, 'variable %s used before definition' % name) | |
229 | 240 | t1 = self.type_of(ast.children[0]) |
230 | self.assert_eq(t1, t2) | |
241 | self.assert_eq(ast, t1, t2) | |
231 | 242 | # not quite useless now (typecase still likes this) |
232 | 243 | if self.context.level(ast.children[0].value) != 'local': |
233 | raise CastileTypeError('cannot assign to non-local') | |
244 | raise CastileTypeError(ast, 'cannot assign to non-local') | |
234 | 245 | ast.type = Void() |
235 | 246 | elif ast.tag == 'Make': |
236 | 247 | t = self.type_of(ast.children[0]) |
237 | 248 | if t.name not in self.structs: |
238 | raise CastileTypeError("undefined struct %s" % t.name) | |
249 | raise CastileTypeError(ast, "undefined struct %s" % t.name) | |
239 | 250 | struct_defn = self.structs[t.name] |
251 | if struct_defn.scope_idents is not None: | |
252 | if self.current_defn not in struct_defn.scope_idents: | |
253 | raise CastileTypeError(ast, "inaccessible struct %s for make: %s not in %s" % | |
254 | (t.name, self.current_defn, struct_defn.scope_idents) | |
255 | ) | |
240 | 256 | if len(struct_defn.content_types) != len(ast.children) - 1: |
241 | raise CastileTypeError("argument mismatch") | |
257 | raise CastileTypeError(ast, "argument mismatch; expected {}, got {} in {}".format( | |
258 | len(struct_defn.content_types), len(ast.children) - 1, ast | |
259 | )) | |
242 | 260 | i = 0 |
243 | 261 | for defn in ast.children[1:]: |
244 | 262 | name = defn.value |
245 | 263 | t1 = self.type_of(defn) |
246 | 264 | pos = struct_defn.field_names[name] |
247 | 265 | defn.aux = pos |
248 | self.assert_eq(t1, struct_defn.content_types[pos]) | |
266 | self.assert_eq(ast, t1, struct_defn.content_types[pos]) | |
249 | 267 | i += 1 |
250 | 268 | ast.type = t |
251 | 269 | elif ast.tag == 'FieldInit': |
252 | 270 | ast.type = self.type_of(ast.children[0]) |
253 | 271 | elif ast.tag == 'Index': |
254 | 272 | t = self.type_of(ast.children[0]) |
273 | struct_defn = self.structs[t.name] | |
274 | if struct_defn.scope_idents is not None: | |
275 | if self.current_defn not in struct_defn.scope_idents: | |
276 | raise CastileTypeError(ast, "inaccessible struct %s for access: %s not in %s" % | |
277 | (t.name, self.current_defn, struct_defn.scope_idents) | |
278 | ) | |
255 | 279 | field_name = ast.value |
256 | struct_fields = self.structs[t.name].field_names | |
280 | struct_fields = struct_defn.field_names | |
257 | 281 | if field_name not in struct_fields: |
258 | raise CastileTypeError("undefined field") | |
282 | raise CastileTypeError(ast, "undefined field") | |
259 | 283 | index = struct_fields[field_name] |
260 | 284 | # we make this value available to compiler backends |
261 | 285 | ast.aux = index |
262 | 286 | # we look up the type from the StructDefinition |
263 | ast.type = self.structs[t.name].content_types[index] | |
287 | ast.type = struct_defn.content_types[index] | |
264 | 288 | elif ast.tag == 'TypeCase': |
265 | 289 | t1 = self.type_of(ast.children[0]) |
266 | 290 | t2 = self.type_of(ast.children[1]) |
267 | 291 | if not isinstance(t1, Union): |
268 | raise CastileTypeError('bad typecase, %s not a union' % t1) | |
292 | raise CastileTypeError(ast, 'bad typecase, %s not a union' % t1) | |
269 | 293 | if not t1.contains(t2): |
270 | raise CastileTypeError('bad typecase, %s not in %s' % (t2, t1)) | |
294 | raise CastileTypeError(ast, 'bad typecase, %s not in %s' % (t2, t1)) | |
271 | 295 | # typecheck t3 with variable in children[0] having type t2 |
272 | 296 | assert ast.children[0].tag == 'VarRef' |
273 | 297 | within_control = self.within_control |
279 | 303 | self.within_control = within_control |
280 | 304 | elif ast.tag == 'Program': |
281 | 305 | for defn in ast.children: |
282 | self.assert_eq(self.type_of(defn), Void()) | |
306 | self.assert_eq(ast, self.type_of(defn), Void()) | |
283 | 307 | ast.type = Void() |
284 | 308 | self.resolve_structs(ast) |
285 | 309 | elif ast.tag == 'Defn': |
310 | self.current_defn = ast.value | |
286 | 311 | t = self.type_of(ast.children[0]) |
312 | self.current_defn = None | |
287 | 313 | if ast.value in self.forwards: |
288 | self.assert_eq(self.forwards[ast.value], t) | |
314 | self.assert_eq(ast, self.forwards[ast.value], t) | |
289 | 315 | del self.forwards[ast.value] |
290 | 316 | else: |
291 | 317 | self.set(ast.value, t) |
293 | 319 | # any return type is fine, for now, so, |
294 | 320 | # we compare it against itself |
295 | 321 | rt = t.return_type |
296 | self.assert_eq(t, Function([], rt)) | |
322 | self.assert_eq(ast, t, Function([], rt)) | |
297 | 323 | ast.type = Void() |
298 | 324 | elif ast.tag == 'Forward': |
299 | 325 | t = self.type_of(ast.children[0]) |
306 | 332 | value_t = self.type_of(ast.children[0]) |
307 | 333 | union_t = self.type_of(ast.children[1]) |
308 | 334 | if not isinstance(union_t, Union): |
309 | raise CastileTypeError('bad cast, not a union: %s' % union_t) | |
335 | raise CastileTypeError(ast, 'bad cast, not a union: %s' % union_t) | |
310 | 336 | if not union_t.contains(value_t): |
311 | 337 | raise CastileTypeError( |
312 | 'bad cast, %s does not include %s' % (union_t, value_t) | |
338 | ast, 'bad cast, %s does not include %s' % (union_t, value_t) | |
313 | 339 | ) |
314 | 340 | ast.type = union_t |
315 | 341 | else: |
2 | 2 | |
3 | 3 | |
4 | 4 | class ScopedContext(object): |
5 | """ | |
6 | >>> d = ScopedContext({ 'a': 2, 'b': 3 }) | |
7 | >>> e = ScopedContext({ 'c': 4 }, parent=d) | |
8 | >>> e['c'] | |
9 | 4 | |
10 | >>> e['b'] | |
11 | 3 | |
12 | >>> 'a' in e | |
13 | True | |
14 | >>> 'e' in e | |
15 | False | |
16 | ||
17 | """ | |
18 | 5 | def __init__(self, dict, parent=None, level=None): |
19 | 6 | self._dict = dict |
20 | 7 | self.parent = parent |
46 | 33 | |
47 | 34 | def __repr__(self): |
48 | 35 | return 'ScopedContext(%r,parent=%r)' % (self._dict, self.parent) |
49 | ||
50 | ||
51 | if __name__ == "__main__": | |
52 | import sys | |
53 | import doctest | |
54 | (fails, something) = doctest.testmod() | |
55 | if fails == 0: | |
56 | print("All tests passed.") | |
57 | sys.exit(0) | |
58 | else: | |
59 | sys.exit(1) |
141 | 141 | return v |
142 | 142 | elif ast.tag == 'TypeCast': |
143 | 143 | v = self.eval(ast.children[0]) |
144 | return TaggedValue(typeof(v), v) | |
144 | if not isinstance(v, TaggedValue): | |
145 | v = TaggedValue(typeof(v), v) | |
146 | return v | |
145 | 147 | elif ast.tag == 'TypeCase': |
146 | 148 | r = self.eval(ast.children[0]) |
147 | 149 | assert isinstance(r, TaggedValue) |
166 | 168 | return "Type(string:)" |
167 | 169 | elif isinstance(x, StructDict): |
168 | 170 | return x.name |
171 | elif isinstance(x, TaggedValue): | |
172 | return x.tag | |
169 | 173 | else: |
170 | return "wtf" | |
174 | raise NotImplementedError(x) | |
171 | 175 | |
172 | 176 | |
173 | 177 | class Program(object): |
0 | 0 | """castile {options} program-file.castile |
1 | 1 | |
2 | Interpreter/compiler for Castile, an unremarkable programming language. | |
2 | Interpreter/compiler for Castile, a programming language with union types. | |
3 | 3 | |
4 | 4 | """ |
5 | 5 | |
6 | 6 | import sys |
7 | 7 | |
8 | from optparse import OptionParser | |
8 | from argparse import ArgumentParser | |
9 | 9 | |
10 | 10 | from castile.parser import Parser |
11 | 11 | from castile.eval import Program |
15 | 15 | |
16 | 16 | |
17 | 17 | def main(argv): |
18 | optparser = OptionParser(__doc__.strip()) | |
19 | optparser.add_option("-a", "--show-ast", | |
20 | action="store_true", dest="show_ast", default=False, | |
21 | help="show parsed AST instead of evaluating") | |
22 | optparser.add_option("-c", "--compile-to", metavar='BACKEND', | |
23 | dest="compile_to", default=None, | |
24 | help="compile to given backend code instead " | |
25 | "of evaluating directly (available backends: " | |
26 | "javascript, ruby, stackmac)") | |
27 | optparser.add_option("-p", "--parse-only", | |
28 | action="store_true", dest="parse_only", | |
29 | default=False, | |
30 | help="parse the input program only and exit") | |
31 | optparser.add_option("-t", "--test", | |
32 | action="store_true", dest="test", default=False, | |
33 | help="run test cases and exit") | |
34 | optparser.add_option("-Y", "--no-typecheck", | |
35 | action="store_false", dest="typecheck", default=True, | |
36 | help="do not typecheck the program") | |
37 | (options, args) = optparser.parse_args(argv[1:]) | |
38 | if options.test: | |
39 | import doctest | |
40 | (fails, something) = doctest.testmod() | |
41 | if fails == 0: | |
42 | print("All tests passed.") | |
43 | sys.exit(0) | |
44 | else: | |
45 | sys.exit(1) | |
46 | with open(args[0], 'r') as f: | |
18 | argparser = ArgumentParser() | |
19 | ||
20 | argparser.add_argument('input_files', nargs='+', metavar='FILENAME', type=str, | |
21 | help='Source files containing the Castile program' | |
22 | ) | |
23 | argparser.add_argument("-a", "--show-ast", | |
24 | action="store_true", dest="show_ast", default=False, | |
25 | help="show parsed AST instead of evaluating" | |
26 | ) | |
27 | argparser.add_argument("-c", "--compile-to", metavar='BACKEND', | |
28 | dest="compile_to", default=None, | |
29 | help="compile to given backend code instead " | |
30 | "of evaluating directly (available backends: " | |
31 | "c, javascript, ruby, stackmac)" | |
32 | ) | |
33 | argparser.add_argument("-p", "--parse-only", | |
34 | action="store_true", dest="parse_only", | |
35 | default=False, | |
36 | help="parse the input program only and exit" | |
37 | ) | |
38 | argparser.add_argument("-Y", "--no-typecheck", | |
39 | action="store_false", dest="typecheck", default=True, | |
40 | help="do not typecheck the program" | |
41 | ) | |
42 | argparser.add_argument('--version', action='version', version="%(prog)s 0.5") | |
43 | ||
44 | options = argparser.parse_args(argv[1:]) | |
45 | ||
46 | with open(options.input_files[0], 'r') as f: | |
47 | 47 | p = Parser(f.read()) |
48 | 48 | ast = p.program() |
49 | 49 | if options.show_ast: |
54 | 54 | if options.typecheck: |
55 | 55 | t = TypeChecker() |
56 | 56 | t.collect_structs(ast) |
57 | t.type_of(ast) | |
57 | try: | |
58 | t.type_of(ast) | |
59 | except Exception: | |
60 | if options.show_ast: | |
61 | print(ast.pprint(0)) | |
62 | print("-----") | |
63 | raise | |
58 | 64 | if options.compile_to is not None: |
59 | 65 | x = FunctionLifter() |
60 | 66 | ast = x.lift_functions(ast) |
0 | import re | |
1 | ||
2 | 0 | from castile.ast import AST |
3 | ||
4 | ||
5 | class CastileSyntaxError(ValueError): | |
6 | pass | |
1 | from castile.scanner import Scanner, CastileSyntaxError | |
7 | 2 | |
8 | 3 | |
9 | 4 | class Parser(object): |
10 | 5 | """Parse a Castile program into an AST. |
11 | ||
12 | The parser includes the scanner as part of it. (Delegating to an external | |
13 | scanner is rather verbose ("self.scanner.expect(...)"; inheriting from a | |
14 | Scanner class, even if it's just a mixin, seems rather weird.) | |
15 | 6 | |
16 | 7 | The parser mainly just constructs the AST. It does few other analyses |
17 | 8 | or transformations itself. However, there are a few: |
23 | 14 | |
24 | 15 | """ |
25 | 16 | def __init__(self, text): |
26 | self.text = text | |
27 | self.token = None | |
28 | self.type = None | |
29 | self.scan() | |
30 | # for parser... | |
17 | self.scanner = Scanner(text) | |
31 | 18 | self.locals = None |
32 | 19 | |
33 | # ### SCANNER ### # | |
34 | ||
35 | def scan_pattern(self, pattern, type, token_group=1, rest_group=2): | |
36 | pattern = r'^(' + pattern + r')(.*?)$' | |
37 | match = re.match(pattern, self.text, re.DOTALL) | |
38 | if not match: | |
39 | return False | |
40 | else: | |
41 | self.type = type | |
42 | self.token = match.group(token_group) | |
43 | self.text = match.group(rest_group) | |
44 | # print(self.type, self.token) | |
45 | return True | |
46 | ||
47 | def scan(self): | |
48 | self.scan_pattern(r'[ \t\n\r]*', 'whitespace') | |
49 | while self.text.startswith('/*'): | |
50 | self.scan_pattern(r'\/\*.*?\*\/[ \t\n\r]*', 'comment') | |
51 | if not self.text: | |
52 | self.token = None | |
53 | self.type = 'EOF' | |
54 | return | |
55 | if self.scan_pattern(r'->', 'arrow'): | |
56 | return | |
57 | if self.scan_pattern(r'>=|>|<=|<|==|!=', 'relational operator'): | |
58 | return | |
59 | if self.scan_pattern(r'\+|\-', 'additive operator'): | |
60 | return | |
61 | if self.scan_pattern(r'\*|\/|\|', 'multiplicative operator'): | |
62 | return | |
63 | if self.scan_pattern(r'\.|\;|\,|\(|\)|\{|\}|\=', 'punctuation'): | |
64 | return | |
65 | if self.scan_pattern(r'string|integer|boolean|function|void|union', | |
66 | 'type name'): | |
67 | return | |
68 | if self.scan_pattern(r'and|or', 'boolean operator'): | |
69 | return | |
70 | if self.scan_pattern(r'(if|else|while|make|struct|' | |
71 | r'typecase|is|as|return|break|' | |
72 | r'true|false|null)(?!\w)', | |
73 | 'keyword', token_group=2, rest_group=3): | |
74 | return | |
75 | if self.scan_pattern(r'\d+', 'integer literal'): | |
76 | return | |
77 | if self.scan_pattern(r'\"(.*?)\"', 'string literal', | |
78 | token_group=2, rest_group=3): | |
79 | return | |
80 | if self.scan_pattern(r'[a-zA-Z_][a-zA-Z0-9_]*', 'identifier'): | |
81 | return | |
82 | if self.scan_pattern(r'.', 'unknown character'): | |
83 | return | |
84 | else: | |
85 | raise ValueError("this should never happen, " | |
86 | "self.text=(%s)" % self.text) | |
87 | ||
88 | def expect(self, token): | |
89 | if self.token == token: | |
90 | self.scan() | |
91 | else: | |
92 | raise CastileSyntaxError( | |
93 | "Expected '%s', but found '%s'" % (token, self.token) | |
94 | ) | |
95 | ||
96 | def expect_type(self, type): | |
97 | self.check_type(type) | |
98 | token = self.token | |
99 | self.scan() | |
100 | return token | |
101 | ||
102 | def on(self, token): | |
103 | return self.token == token | |
104 | ||
105 | def on_any(self, tokens): | |
106 | return self.token in tokens | |
107 | ||
108 | def on_type(self, type): | |
109 | return self.type == type | |
110 | ||
111 | def check_type(self, type): | |
112 | if not self.type == type: | |
113 | raise CastileSyntaxError( | |
114 | "Expected %s, but found %s ('%s')" % (type, self.type, self.token) | |
115 | ) | |
116 | ||
117 | def consume(self, token): | |
118 | if self.token == token: | |
119 | self.scan() | |
120 | return True | |
121 | else: | |
122 | return False | |
123 | ||
124 | def consume_type(self, type): | |
125 | if self.on_type(type): | |
126 | token = self.token | |
127 | self.scan() | |
128 | return token | |
129 | else: | |
130 | return None | |
131 | ||
132 | # ### PARSER ### # | |
20 | ### Delegate to scanner | |
21 | ||
22 | def consume(self, *args, **kwargs): | |
23 | return self.scanner.consume(*args, **kwargs) | |
24 | ||
25 | def consume_type(self, *args, **kwargs): | |
26 | return self.scanner.consume_type(*args, **kwargs) | |
27 | ||
28 | def expect(self, *args, **kwargs): | |
29 | return self.scanner.expect(*args, **kwargs) | |
30 | ||
31 | def expect_type(self, *args, **kwargs): | |
32 | return self.scanner.expect_type(*args, **kwargs) | |
33 | ||
34 | def on(self, *args, **kwargs): | |
35 | return self.scanner.on(*args, **kwargs) | |
36 | ||
37 | def on_any(self, *args, **kwargs): | |
38 | return self.scanner.on_any(*args, **kwargs) | |
39 | ||
40 | def on_type(self, *args, **kwargs): | |
41 | return self.scanner.on_type(*args, **kwargs) | |
42 | ||
43 | ### Delegate to AST | |
44 | ||
45 | def ast(self, *args, **kwargs): | |
46 | kwargs['line'] = self.scanner.line | |
47 | return AST(*args, **kwargs) | |
48 | ||
49 | ### Parser proper | |
133 | 50 | |
134 | 51 | def program(self): |
135 | 52 | defns = [] |
136 | 53 | while not self.on_type('EOF'): |
137 | 54 | defns.append(self.defn()) |
138 | 55 | self.consume(';') |
139 | return AST('Program', defns) | |
56 | return self.ast('Program', defns) | |
140 | 57 | |
141 | 58 | def defn(self): |
142 | 59 | if self.consume('fun'): |
149 | 66 | args.append(self.arg()) |
150 | 67 | self.expect(")") |
151 | 68 | body = self.body() |
152 | funlit = AST('FunLit', [AST('Args', args), body]) | |
153 | return AST('Defn', [funlit], value=id) | |
69 | funlit = self.ast('FunLit', [self.ast('Args', args), body]) | |
70 | return self.ast('Defn', [funlit], value=id) | |
154 | 71 | elif self.consume('struct'): |
155 | 72 | id = self.expect_type('identifier') |
156 | 73 | self.expect("{") |
159 | 76 | name = self.expect_type('identifier') |
160 | 77 | self.expect(':') |
161 | 78 | texpr = self.texpr0() |
162 | components.append(AST('FieldDefn', [texpr], value=name)) | |
79 | components.append(self.ast('FieldDefn', [texpr], value=name)) | |
163 | 80 | self.consume(';') |
164 | 81 | self.expect("}") |
165 | return AST('StructDefn', components, value=id) | |
82 | scope_children = [] | |
83 | if self.consume("for"): | |
84 | self.expect("(") | |
85 | idents = [] | |
86 | if not self.on(")"): | |
87 | idents.append(self.ast('Ident', value=self.expect_type('identifier'))) | |
88 | while self.consume(","): | |
89 | idents.append(self.ast('Ident', value=self.expect_type('identifier'))) | |
90 | self.expect(")") | |
91 | scope_children.append(self.ast('Idents', idents)) | |
92 | return self.ast('StructDefn', [self.ast('FieldDefns', components)] + scope_children, value=id) | |
166 | 93 | else: |
167 | 94 | id = self.expect_type('identifier') |
168 | 95 | if self.consume('='): |
169 | 96 | e = self.literal() |
170 | return AST('Defn', [e], value=id) | |
97 | return self.ast('Defn', [e], value=id) | |
171 | 98 | else: |
172 | 99 | self.expect(':') |
173 | 100 | e = self.texpr0() |
174 | return AST('Forward', [e], value=id) | |
101 | return self.ast('Forward', [e], value=id) | |
175 | 102 | |
176 | 103 | def arg(self): |
177 | 104 | id = self.expect_type('identifier') |
178 | te = AST('Type', value='integer') | |
105 | te = self.ast('Type', value='integer') | |
179 | 106 | if self.consume(':'): |
180 | 107 | te = self.texpr1() |
181 | return AST('Arg', [te], value=id) | |
108 | return self.ast('Arg', [te], value=id) | |
182 | 109 | |
183 | 110 | def texpr0(self): |
184 | 111 | ast = self.texpr1() |
185 | 112 | if self.consume('->'): |
186 | 113 | r = self.texpr1() |
187 | return AST('FunType', [r, ast]) | |
114 | return self.ast('FunType', [r, ast]) | |
188 | 115 | if self.on(','): |
189 | 116 | args = [ast] |
190 | 117 | while self.consume(','): |
191 | 118 | args.append(self.texpr1()) |
192 | 119 | self.expect('->') |
193 | 120 | r = self.texpr1() |
194 | return AST('FunType', [r] + args) | |
121 | return self.ast('FunType', [r] + args) | |
195 | 122 | return ast |
196 | 123 | |
197 | 124 | def texpr1(self): |
200 | 127 | args = [ast] |
201 | 128 | while self.consume('|'): |
202 | 129 | args.append(self.texpr2()) |
203 | ast = AST('UnionType', args) | |
130 | ast = self.ast('UnionType', args) | |
204 | 131 | return ast |
205 | 132 | |
206 | 133 | def texpr2(self): |
210 | 137 | return ast |
211 | 138 | elif self.on_type('identifier'): |
212 | 139 | id = self.consume_type('identifier') |
213 | return AST('StructType', [], value=id) | |
140 | return self.ast('StructType', [], value=id) | |
214 | 141 | tname = self.expect_type('type name') |
215 | return AST('Type', value=tname) | |
142 | return self.ast('Type', value=tname) | |
216 | 143 | |
217 | 144 | def block(self): |
218 | 145 | self.expect('{') |
221 | 148 | stmts.append(self.stmt()) |
222 | 149 | self.consume(';') |
223 | 150 | self.expect('}') |
224 | return AST('Block', stmts) | |
151 | return self.ast('Block', stmts) | |
225 | 152 | |
226 | 153 | STMT_TAGS = ('If', 'While', 'TypeCase', 'Return', 'Break') |
227 | 154 | |
239 | 166 | stmts.append(last) |
240 | 167 | self.consume(';') |
241 | 168 | if len(stmts) == 0: |
242 | stmts = [AST('Return', [AST('None')])] | |
169 | stmts = [self.ast('Return', [self.ast('None')])] | |
243 | 170 | elif last is not None and last.tag not in self.STMT_TAGS: |
244 | stmts[-1] = AST('Return', [stmts[-1]]) | |
171 | stmts[-1] = self.ast('Return', [stmts[-1]]) | |
245 | 172 | self.expect('}') |
246 | vardecls = AST( | |
173 | vardecls = self.ast( | |
247 | 174 | 'VarDecls', |
248 | [AST('VarDecl', value=name) for name in self.locals] | |
175 | [self.ast('VarDecl', value=name) for name in self.locals] | |
249 | 176 | ) |
250 | stmts = AST('Block', stmts) | |
177 | stmts = self.ast('Block', stmts) | |
251 | 178 | self.locals = save_locals |
252 | return AST('Body', [vardecls, stmts]) | |
179 | return self.ast('Body', [vardecls, stmts]) | |
253 | 180 | |
254 | 181 | def stmt(self): |
255 | 182 | if self.on('if'): |
257 | 184 | elif self.consume('while'): |
258 | 185 | t = self.expr0() |
259 | 186 | b = self.block() |
260 | return AST('While', [t, b]) | |
187 | return self.ast('While', [t, b]) | |
261 | 188 | elif self.consume('typecase'): |
262 | 189 | id = self.expect_type('identifier') |
263 | e = AST('VarRef', value=id) | |
190 | e = self.ast('VarRef', value=id) | |
264 | 191 | self.expect('is') |
265 | 192 | te = self.texpr0() |
266 | 193 | b = self.block() |
267 | return AST('TypeCase', [e, te, b], value=te.minirepr()) | |
194 | return self.ast('TypeCase', [e, te, b], value=te.minirepr()) | |
268 | 195 | elif self.consume('return'): |
269 | return AST('Return', [self.expr0()]) | |
196 | return self.ast('Return', [self.expr0()]) | |
270 | 197 | elif self.consume('break'): |
271 | return AST('Break') | |
198 | return self.ast('Break') | |
272 | 199 | else: |
273 | 200 | return self.expr0() |
274 | 201 | |
282 | 209 | b2 = self.ifstmt() |
283 | 210 | else: |
284 | 211 | b2 = self.block() |
285 | return AST('If', [t, b1, b2]) | |
286 | return AST('If', [t, b1]) | |
212 | return self.ast('If', [t, b1, b2]) | |
213 | return self.ast('If', [t, b1]) | |
287 | 214 | |
288 | 215 | def expr0(self): |
289 | 216 | e = self.expr1() |
290 | 217 | while self.on_type('boolean operator'): |
291 | 218 | op = self.expect_type('boolean operator') |
292 | 219 | e2 = self.expr1() |
293 | e = AST('Op', [e, e2], value=op) | |
220 | e = self.ast('Op', [e, e2], value=op) | |
294 | 221 | if self.consume('as'): |
295 | 222 | union_te = self.texpr0() |
296 | e = AST('TypeCast', [e, union_te]) | |
223 | e = self.ast('TypeCast', [e, union_te]) | |
297 | 224 | return e |
298 | 225 | |
299 | 226 | def expr1(self): |
301 | 228 | while self.on_type('relational operator'): |
302 | 229 | op = self.expect_type('relational operator') |
303 | 230 | e2 = self.expr2() |
304 | e = AST('Op', [e, e2], value=op) | |
231 | e = self.ast('Op', [e, e2], value=op) | |
305 | 232 | return e |
306 | 233 | |
307 | 234 | def expr2(self): |
309 | 236 | while self.on_type('additive operator'): |
310 | 237 | op = self.expect_type('additive operator') |
311 | 238 | e2 = self.expr3() |
312 | e = AST('Op', [e, e2], value=op) | |
239 | e = self.ast('Op', [e, e2], value=op) | |
313 | 240 | return e |
314 | 241 | |
315 | 242 | def expr3(self): |
317 | 244 | while self.on_type('multiplicative operator'): |
318 | 245 | op = self.expect_type('multiplicative operator') |
319 | 246 | e2 = self.expr4() |
320 | e = AST('Op', [e, e2], value=op) | |
247 | e = self.ast('Op', [e, e2], value=op) | |
321 | 248 | return e |
322 | 249 | |
323 | 250 | def expr4(self): |
331 | 258 | while self.consume(","): |
332 | 259 | args.append(self.expr0()) |
333 | 260 | self.expect(")") |
334 | e = AST('FunCall', [e] + args) | |
261 | e = self.ast('FunCall', [e] + args) | |
335 | 262 | elif self.consume('.'): |
336 | 263 | id = self.expect_type('identifier') |
337 | e = AST('Index', [e], value=id) | |
264 | e = self.ast('Index', [e], value=id) | |
338 | 265 | else: |
339 | 266 | done = True |
340 | 267 | return e |
345 | 272 | elif self.on_any(('-', 'fun', 'true', 'false', 'null')): |
346 | 273 | return self.literal() |
347 | 274 | elif self.consume('not'): |
348 | return AST('Not', [self.expr1()]) | |
275 | return self.ast('Not', [self.expr1()]) | |
349 | 276 | elif self.consume('make'): |
350 | 277 | # TODO I just accidentally any type. Is that bad? |
351 | 278 | texpr = self.texpr0() |
355 | 282 | id = self.expect_type('identifier') |
356 | 283 | self.expect(':') |
357 | 284 | e = self.expr0() |
358 | args.append(AST('FieldInit', [e], value=id)) | |
285 | args.append(self.ast('FieldInit', [e], value=id)) | |
359 | 286 | while self.consume(","): |
360 | 287 | id = self.expect_type('identifier') |
361 | 288 | self.expect(':') |
362 | 289 | e = self.expr0() |
363 | args.append(AST('FieldInit', [e], value=id)) | |
290 | args.append(self.ast('FieldInit', [e], value=id)) | |
364 | 291 | self.expect(")") |
365 | return AST('Make', [texpr] + args, value=texpr.minirepr()) | |
292 | return self.ast('Make', [texpr] + args, value=texpr.minirepr()) | |
366 | 293 | |
367 | 294 | elif self.consume('('): |
368 | 295 | e = self.expr0() |
370 | 297 | return e |
371 | 298 | else: |
372 | 299 | id = self.expect_type('identifier') |
373 | ast = AST('VarRef', value=id) | |
300 | ast = self.ast('VarRef', value=id) | |
374 | 301 | if self.consume('='): |
375 | 302 | e = self.expr0() |
376 | 303 | aux = None |
377 | 304 | if id not in self.locals: |
378 | 305 | self.locals.add(id) |
379 | 306 | aux = 'defining instance' |
380 | ast = AST('Assignment', [ast, e], aux=aux) | |
307 | ast = self.ast('Assignment', [ast, e], aux=aux) | |
381 | 308 | return ast |
382 | 309 | |
383 | 310 | def literal(self): |
384 | 311 | if self.on_type('string literal'): |
385 | 312 | v = self.consume_type('string literal') |
386 | return AST('StrLit', value=v) | |
313 | return self.ast('StrLit', value=v) | |
387 | 314 | elif self.on_type('integer literal'): |
388 | 315 | v = int(self.consume_type('integer literal')) |
389 | return AST('IntLit', value=v) | |
316 | return self.ast('IntLit', value=v) | |
390 | 317 | elif self.consume('-'): |
391 | 318 | v = 0 - int(self.expect_type('integer literal')) |
392 | return AST('IntLit', value=v) | |
319 | return self.ast('IntLit', value=v) | |
393 | 320 | elif self.consume('true'): |
394 | return AST('BoolLit', value=True) | |
321 | return self.ast('BoolLit', value=True) | |
395 | 322 | elif self.consume('false'): |
396 | return AST('BoolLit', value=False) | |
323 | return self.ast('BoolLit', value=False) | |
397 | 324 | elif self.consume('null'): |
398 | return AST('None') | |
325 | return self.ast('None') | |
399 | 326 | else: |
400 | 327 | self.expect('fun') |
401 | 328 | self.expect("(") |
406 | 333 | args.append(self.arg()) |
407 | 334 | self.expect(")") |
408 | 335 | body = self.body() |
409 | return AST('FunLit', [AST('Args', args), body]) | |
336 | return self.ast('FunLit', [self.ast('Args', args), body]) |
0 | import re | |
1 | ||
2 | ||
3 | class CastileSyntaxError(ValueError): | |
4 | pass | |
5 | ||
6 | ||
7 | class Scanner(object): | |
8 | ||
9 | def __init__(self, text): | |
10 | self.text = text | |
11 | self.token = None | |
12 | self.type = None | |
13 | self.pos = 0 | |
14 | self.line = 1 | |
15 | self.scan() | |
16 | ||
17 | def near_text(self, length=10): | |
18 | return self.text[self.pos:self.pos + length] | |
19 | ||
20 | def scan_pattern(self, pattern, type, token_group=1, rest_group=2): | |
21 | pattern = r'(' + pattern + r')' | |
22 | regexp = re.compile(pattern, flags=re.DOTALL) | |
23 | match = regexp.match(self.text, pos=self.pos) | |
24 | if not match: | |
25 | return False | |
26 | else: | |
27 | self.type = type | |
28 | self.token = match.group(token_group) | |
29 | self.pos += len(match.group(0)) | |
30 | self.line += self.token.count('\n') | |
31 | return True | |
32 | ||
33 | def scan(self): | |
34 | self.scan_pattern(r'[ \t\n\r]*', 'whitespace') | |
35 | while self.scan_pattern(r'\/\*.*?\*\/[ \t\n\r]*', 'comment'): | |
36 | self.scan_pattern(r'[ \t\n\r]*', 'whitespace') | |
37 | if self.pos >= len(self.text): | |
38 | self.token = None | |
39 | self.type = 'EOF' | |
40 | return | |
41 | if self.scan_pattern(r'->', 'arrow'): | |
42 | return | |
43 | if self.scan_pattern(r'>=|>|<=|<|==|!=', 'relational operator'): | |
44 | return | |
45 | if self.scan_pattern(r'\+|\-', 'additive operator'): | |
46 | return | |
47 | if self.scan_pattern(r'\*|\/|\|', 'multiplicative operator'): | |
48 | return | |
49 | if self.scan_pattern(r'\.|\;|\,|\(|\)|\{|\}|\=', 'punctuation'): | |
50 | return | |
51 | if self.scan_pattern(r'string|integer|boolean|function|void|union', | |
52 | 'type name'): | |
53 | return | |
54 | if self.scan_pattern(r'and|or', 'boolean operator'): | |
55 | return | |
56 | if self.scan_pattern(r'(if|else|while|make|struct|' | |
57 | r'typecase|is|as|return|break|' | |
58 | r'true|false|null)(?!\w)', | |
59 | 'keyword', token_group=2, rest_group=3): | |
60 | return | |
61 | if self.scan_pattern(r'\d+', 'integer literal'): | |
62 | return | |
63 | if self.scan_pattern(r'\"(.*?)\"', 'string literal', | |
64 | token_group=2, rest_group=3): | |
65 | return | |
66 | if self.scan_pattern(r'[a-zA-Z_][a-zA-Z0-9_]*', 'identifier'): | |
67 | return | |
68 | if self.scan_pattern(r'.', 'unknown character'): | |
69 | return | |
70 | else: | |
71 | raise ValueError("this should never happen, " | |
72 | "self.text=(%s)" % self.text) | |
73 | ||
74 | def expect(self, token): | |
75 | if self.token == token: | |
76 | self.scan() | |
77 | else: | |
78 | raise CastileSyntaxError( | |
79 | "Expected '%s', but found '%s' (line %s, near '%s')" % ( | |
80 | token, self.token, self.line, self.near_text() | |
81 | ) | |
82 | ) | |
83 | ||
84 | def expect_type(self, type): | |
85 | self.check_type(type) | |
86 | token = self.token | |
87 | self.scan() | |
88 | return token | |
89 | ||
90 | def on(self, token): | |
91 | return self.token == token | |
92 | ||
93 | def on_any(self, tokens): | |
94 | return self.token in tokens | |
95 | ||
96 | def on_type(self, type): | |
97 | return self.type == type | |
98 | ||
99 | def check_type(self, type): | |
100 | if not self.type == type: | |
101 | raise CastileSyntaxError( | |
102 | "Expected %s, but found %s ('%s') (line %s, near '%s')" % ( | |
103 | type, self.type, self.token, self.line, self.near_text() | |
104 | ) | |
105 | ) | |
106 | ||
107 | def consume(self, token): | |
108 | if self.token == token: | |
109 | self.scan() | |
110 | return True | |
111 | else: | |
112 | return False | |
113 | ||
114 | def consume_type(self, type): | |
115 | if self.on_type(type): | |
116 | token = self.token | |
117 | self.scan() | |
118 | return token | |
119 | else: | |
120 | return None |
60 | 60 | return True |
61 | 61 | return False |
62 | 62 | |
63 | def contains_instance_of(self, cls): | |
64 | for member in self.content_types: | |
65 | if isinstance(member, cls): | |
66 | return True | |
67 | return False | |
68 | ||
63 | 69 | def __str__(self): |
64 | 70 | h = "union(" |
65 | 71 | h += ', '.join(sorted([str(t) for t in self.content_types])) |
23 | 23 | APPLIANCES="$APPLIANCES tests/appliances/castile-c-c.md" |
24 | 24 | fi |
25 | 25 | |
26 | #APPLIANCES="tests/appliances/castile.py3.md" | |
27 | #APPLIANCES="tests/appliances/castile-c-c.md" | |
28 | #APPLIANCES="tests/appliances/castile-c-javascript.md" | |
29 | ||
26 | 30 | falderal $APPLIANCES tests/Castile.md |
27 | 31 | RESULT=$? |
28 | 32 | rm -f foo.* a.out |
361 | 361 | |
362 | 362 | ### Non-local Values ### |
363 | 363 | |
364 | Literals may appear at the toplevel. Semicolons are optional at toplevel. | |
364 | Literals may appear at the toplevel. Semicolons are optional at toplevel. | |
365 | 365 | |
366 | 366 | | factor = 5; |
367 | 367 | | fun main() { |
369 | 369 | | } |
370 | 370 | = 30 |
371 | 371 | |
372 | Toplevel literals may not be updated. (And thus | |
372 | Toplevel literals may not be updated. Thus, the following looks like it | |
373 | is defining a local with the same name as a toplevel, which is not permitted. | |
373 | 374 | |
374 | 375 | | factor = 5 |
375 | 376 | | fun main() { |
600 | 601 | | } |
601 | 602 | ? mismatch |
602 | 603 | |
603 | Equality can be checked between unions. (TODO) | |
604 | ||
605 | /| fun main() { | |
606 | /| a = 40 as string|integer | |
607 | /| b = 40 as string|integer | |
608 | /| if a == b { | |
609 | /| print("it is") | |
610 | /| } | |
611 | /| } | |
612 | /= ok | |
604 | Equality can be checked between unions, as long as they are | |
605 | unions entirely of simple (non-struct) types. | |
606 | ||
607 | | fun main() { | |
608 | | a = 40 as string|integer | |
609 | | b = 40 as string|integer | |
610 | | if a == b { | |
611 | | print("it is") | |
612 | | } | |
613 | | } | |
614 | = it is | |
613 | 615 | |
614 | 616 | | fun main() { |
615 | 617 | | a = 40 as string|integer |
630 | 632 | | } |
631 | 633 | | } |
632 | 634 | ? mismatch |
635 | ||
636 | Equality cannot be tested between values of a union type | |
637 | that contains a struct type as one of its members. | |
638 | ||
639 | | struct person { name: string; age: integer } | |
640 | | fun main() { | |
641 | | a = 40 as person|integer | |
642 | | b = 40 as person|integer | |
643 | | if a == b { | |
644 | | print("it is") | |
645 | | } | |
646 | | } | |
647 | ? struct | |
633 | 648 | |
634 | 649 | ### Builtins ### |
635 | 650 | |
767 | 782 | | } |
768 | 783 | = 23 |
769 | 784 | |
770 | Structs can be tested for equality. (Since structs are immutable, it | |
771 | doesn't matter if this is structural equality or identity.) | |
785 | Structs cannot be tested for equality with the `==` or `!==` | |
786 | operators. | |
772 | 787 | |
773 | 788 | | struct person { name: string; age: integer } |
774 | 789 | | main = fun() { |
776 | 791 | | k = make person(name:"Jake", age: 23); |
777 | 792 | | j == k |
778 | 793 | | } |
779 | = True | |
780 | ||
781 | | struct person { age: integer; name: string } | |
782 | | main = fun() { | |
783 | | j = make person(age: 23, name:"Jake"); | |
784 | | k = make person(age: 23, name:"John"); | |
785 | | j == k | |
786 | | } | |
787 | = False | |
794 | ? structs cannot be compared | |
788 | 795 | |
789 | 796 | | struct person { age: integer; name: string } |
790 | 797 | | main = fun() { |
792 | 799 | | k = make person(age: 21, name:"Jake"); |
793 | 800 | | j != k |
794 | 801 | | } |
795 | = True | |
802 | ? structs cannot be compared | |
796 | 803 | |
797 | 804 | Structs of two different types cannot be tested for equality. |
798 | 805 | |
805 | 812 | | } |
806 | 813 | ? mismatch |
807 | 814 | |
815 | If you really want to compare two structs for equality, you'll | |
816 | have to write the equality predicate function yourself. | |
817 | ||
818 | | struct person { name: string; age: integer } | |
819 | | equ_person = fun(a: person, b: person) { | |
820 | | a.age == b.age and a.name == b.name | |
821 | | } | |
822 | | main = fun() { | |
823 | | j = make person(age: 23, name:"Jake"); | |
824 | | k = make person(name:"Jake", age: 23); | |
825 | | equ_person(j, k) | |
826 | | } | |
827 | = True | |
828 | ||
808 | 829 | Structs cannot be compared for ordering. |
809 | 830 | |
810 | 831 | | struct person { age: integer; name: string } |
813 | 834 | | k = make person(age: 21, name:"Jake"); |
814 | 835 | | j > k |
815 | 836 | | } |
816 | ? structs cannot be compared for order | |
837 | ? structs cannot be compared | |
817 | 838 | |
818 | 839 | Structs can be passed to functions. |
819 | 840 | |
1228 | 1249 | | } |
1229 | 1250 | = red |
1230 | 1251 | = blue |
1252 | ||
1253 | ### Scoped Structs ### | |
1254 | ||
1255 | When a `struct` is declared, it may be associated with a set of identifiers. | |
1256 | Functions with these global names are the only function definitions which | |
1257 | can `make` such a struct, or see that it has fields; to all other functions, | |
1258 | these operations will not be available. It is in this way that encapsulation | |
1259 | is accomplished. | |
1260 | ||
1261 | | struct list { | |
1262 | | value: string; | |
1263 | | next: list|void; | |
1264 | | } for (cons, singleton, length) | |
1265 | | | |
1266 | | fun cons(v: string, l: list) { | |
1267 | | make list(value:v, next:l as list|void) | |
1268 | | } | |
1269 | | | |
1270 | | fun singleton(v: string) { | |
1271 | | make list(value:v, next:null as list|void) | |
1272 | | } | |
1273 | | | |
1274 | | length : list|void -> integer | |
1275 | | fun length(l: list|void) { | |
1276 | | typecase l is void { return 0 } | |
1277 | | typecase l is list { return 1 + length(l.next) } | |
1278 | | } | |
1279 | | | |
1280 | | fun main() { | |
1281 | | l = cons("first", cons("second", singleton("third"))); | |
1282 | | print(str(length(l as list|void))); | |
1283 | | } | |
1284 | = 3 | |
1285 | ||
1286 | | struct list { | |
1287 | | value: string; | |
1288 | | next: list|void; | |
1289 | | } for (cons, singleton, length) | |
1290 | | | |
1291 | | fun cons(v: string, l: list) { | |
1292 | | make list(value:v, next:l as list|void) | |
1293 | | } | |
1294 | | | |
1295 | | fun singleton(v: string) { | |
1296 | | make list(value:v, next:null as list|void) | |
1297 | | } | |
1298 | | | |
1299 | | length : list|void -> integer | |
1300 | | fun length(l: list|void) { | |
1301 | | typecase l is void { return 0 } | |
1302 | | typecase l is list { return 1 + length(l.next) } | |
1303 | | } | |
1304 | | | |
1305 | | fun main() { | |
1306 | | l = make list(value:"first", next:null as list|void); | |
1307 | | print(str(length(l as list|void))); | |
1308 | | } | |
1309 | ? make | |
1310 | ||
1311 | | struct list { | |
1312 | | value: string; | |
1313 | | next: list|void; | |
1314 | | } for (cons, singleton, length) | |
1315 | | | |
1316 | | fun cons(v: string, l: list) { | |
1317 | | make list(value:v, next:l as list|void) | |
1318 | | } | |
1319 | | | |
1320 | | fun singleton(v: string) { | |
1321 | | make list(value:v, next:null as list|void) | |
1322 | | } | |
1323 | | | |
1324 | | fun main() { | |
1325 | | l = cons("first", cons("second", singleton("third"))); | |
1326 | | print(l.value); | |
1327 | | } | |
1328 | ? struct | |
1329 | ||
1330 | One can use this facility to implement abstract data types. | |
1331 | ||
1332 | | struct assoc { | |
1333 | | key: string; | |
1334 | | value: string; | |
1335 | | next: assoc|void; | |
1336 | | } for (singleton, update, lookup, remove) | |
1337 | | | |
1338 | | fun singleton(k: string, v: string) { | |
1339 | | make assoc(key:k, value:v, next:null as assoc|void) | |
1340 | | } | |
1341 | | | |
1342 | | fun update(k: string, v: string, a: assoc) { | |
1343 | | make assoc(key:k, value:v, next:a as assoc|void) | |
1344 | | } | |
1345 | | | |
1346 | | lookup : assoc, string -> string|void | |
1347 | | fun lookup(a: assoc, k: string) { | |
1348 | | if a.key == k { | |
1349 | | return a.value as string|void | |
1350 | | } | |
1351 | | n = a.next | |
1352 | | typecase n is void { | |
1353 | | return null as string|void | |
1354 | | } | |
1355 | | typecase n is assoc { | |
1356 | | return lookup(n, k) | |
1357 | | } | |
1358 | | } | |
1359 | | | |
1360 | | fun main() { | |
1361 | | a = update("1", "first", update("2", "second", singleton("3", "third"))); | |
1362 | | r = lookup(a, "2"); | |
1363 | | print("um"); | |
1364 | | typecase r is void { print("NOT FOUND"); } | |
1365 | | typecase r is string { print(r); } | |
1366 | | print("ya"); | |
1367 | | } | |
1368 | = um | |
1369 | = second | |
1370 | = ya | |
1371 | ||
1372 | This program should work even with a redundant upcast in it. | |
1373 | ||
1374 | | struct assoc { | |
1375 | | key: string; | |
1376 | | value: string; | |
1377 | | next: assoc|void; | |
1378 | | } for (singleton, update, lookup, remove) | |
1379 | | | |
1380 | | fun singleton(k: string, v: string) { | |
1381 | | make assoc(key:k, value:v, next:null as assoc|void) | |
1382 | | } | |
1383 | | | |
1384 | | fun update(k: string, v: string, a: assoc) { | |
1385 | | make assoc(key:k, value:v, next:a as assoc|void) | |
1386 | | } | |
1387 | | | |
1388 | | lookup : assoc, string -> string|void | |
1389 | | fun lookup(a: assoc, k: string) { | |
1390 | | if a.key == k { | |
1391 | | return a.value as string|void | |
1392 | | } | |
1393 | | n = a.next | |
1394 | | typecase n is void { | |
1395 | | return null as string|void | |
1396 | | } | |
1397 | | typecase n is assoc { | |
1398 | | return lookup(n, k) as string|void | |
1399 | | } | |
1400 | | } | |
1401 | | | |
1402 | | fun main() { | |
1403 | | a = update("1", "first", update("2", "second", singleton("3", "third"))); | |
1404 | | r = lookup(a, "2"); | |
1405 | | print("um"); | |
1406 | | typecase r is void { print("NOT FOUND"); } | |
1407 | | typecase r is string { print(r); } | |
1408 | | print("ya"); | |
1409 | | } | |
1410 | = um | |
1411 | = second | |
1412 | = ya |