Commits

Anonymous committed cc216af

AST nodes have tags (names) and types (language-domain.)

  • Participants
  • Parent commits be78940

Comments (0)

Files changed (9)

File README.markdown

 Structs can be tested for equality.  (Since structs are immutable, it
 doesn't matter if this is structural equality or identity.)
 
-    | struct person { age: integer; name: string }
-    | main = fun() {
-    |   var j = make person(age: 23, name:"Jake");
-    |   var k = make person(age: 23, name:"Jake");
-    |   j == k
-    | }
-    = True
+    /| struct person { age: integer; name: string }
+    /| main = fun() {
+    /|   var j = make person(age: 23, name:"Jake");
+    /|   var k = make person(age: 23, name:"Jake");
+    /|   j == k
+    /| }
+    /= True
 
-    | struct person { name: string; age: integer }
-    | main = fun() {
-    |   var j = make person(age: 23, name:"Jake");
-    |   var k = make person(name:"Jake", age: 23);
-    |   j == k
-    | }
-    = True
+    /| struct person { name: string; age: integer }
+    /| main = fun() {
+    /|   var j = make person(age: 23, name:"Jake");
+    /|   var k = make person(name:"Jake", age: 23);
+    /|   j == k
+    /| }
+    /= True
 
-    | struct person { age: integer; name: string }
-    | main = fun() {
-    |   var j = make person(age: 23, name:"Jake");
-    |   var k = make person(age: 23, name:"John");
-    |   j == k
-    | }
-    = False
+    /| struct person { age: integer; name: string }
+    /| main = fun() {
+    /|   var j = make person(age: 23, name:"Jake");
+    /|   var k = make person(age: 23, name:"John");
+    /|   j == k
+    /| }
+    /= False
 
 Structs can be passed to functions.
 

File src/castile/ast.py

 class AST(object):
-    def __init__(self, type, children=None, value=None):
-        # TODO 'type' should be 'tag' or 'kind' or smth; 'type' should be
+    def __init__(self, tag, children=None, value=None):
+        # TODO 'type' should be
         # the type, in the type system, of the value this node evaluates to
-        self._type = type
+        self._tag = tag
         self._value = value
         if children is not None:
             self._children = children
         for child in self.children:
             assert isinstance(child, AST), \
               "child %r of %r is not an AST node" % (child, self)
+        self.type = None  # typechecker may populate this
         self.aux = None  # typechecker may populate this
+        # TODO document what it means for particular nodes
         #print "created %r" % self
 
     @property
-    def type(self):
-        return self._type
+    def tag(self):
+        return self._tag
 
     @property
     def value(self):
 
     def __repr__(self):
         if self.value is None:
-            return 'AST(%r,%r)' % (self.type, self.children)
+            return 'AST(%r,%r)' % (self.tag, self.children)
         if not self.children:
-            return 'AST(%r,value=%r)' % (self.type, self.value)
-        return 'AST(%r,%r,value=%r)' % (self.type, self.children, self.value)
+            return 'AST(%r,value=%r)' % (self.ttag, self.value)
+        return 'AST(%r,%r,value=%r)' % (self.tag, self.children, self.value)
 
     def minirepr(self):
         return '%s(%s:%s)' % (
-            self.type, self.value,
+            self.tag, self.value,
             ','.join([c.minirepr() for c in self.children])
         )
 
     def pprint(self, indent):
-        h = ('  ' * indent) + self.type
+        h = ('  ' * indent) + self.tag
         if self.value is not None:
             h += '=%r' % self.value
         h += '\n'

File src/castile/backends/javascript.py

             self.compile(asts[-1])
 
     def compile(self, ast):
-        if ast.type == 'Program':
+        if ast.tag == 'Program':
             self.out.write("""\
 /* AUTOMATICALLY GENERATED -- EDIT AT OWN RISK */
 
 if (result !== undefined && result !== null)
   print(repr(result));
 """)
-        elif ast.type == 'Defn':
+        elif ast.tag == 'Defn':
             self.out.write('%s = ' % ast.value)
             self.compile(ast.children[0])
             self.out.write(';\n')
-        elif ast.type in ('StructDefn', 'Forward'):
+        elif ast.tag in ('StructDefn', 'Forward'):
             pass
-        elif ast.type == 'FunLit':
+        elif ast.tag == 'FunLit':
             self.out.write('function(')
             self.compile(ast.children[0])
             self.out.write(')\n')
             self.compile(ast.children[1])
-        elif ast.type == 'Args':
+        elif ast.tag == 'Args':
             self.commas(ast.children)
-        elif ast.type == 'Arg':
+        elif ast.tag == 'Arg':
             self.out.write(ast.value)
-        elif ast.type == 'Body':
+        elif ast.tag == 'Body':
             self.out.write('{')
             self.compile(ast.children[0])
-            assert ast.children[1].type == 'Block'
+            assert ast.children[1].tag == 'Block'
             block = ast.children[1]
             for child in block.children:
                 self.compile(child)
                 self.out.write(';\n')
             self.out.write('}')
-        elif ast.type == 'VarDecls':
+        elif ast.tag == 'VarDecls':
             for child in ast.children:
                 self.compile(child)
-        elif ast.type == 'VarDecl':
+        elif ast.tag == 'VarDecl':
             self.out.write('%s = ' % ast.value)
             self.compile(ast.children[0])
             self.out.write(';\n')
-        elif ast.type == 'Block':
+        elif ast.tag == 'Block':
             self.out.write('{')
             for child in ast.children:
                 self.compile(child)
                 self.out.write(';\n')
             self.out.write('}')
-        elif ast.type == 'While':
+        elif ast.tag == 'While':
             self.out.write('while (')
             self.compile(ast.children[0])
             self.out.write(')')
             self.compile(ast.children[1])
-        elif ast.type == 'Op':
+        elif ast.tag == 'Op':
             self.out.write('(')
             self.compile(ast.children[0])
             self.out.write(' %s ' % OPS.get(ast.value, ast.value))
             self.compile(ast.children[1])
             self.out.write(')')
-        elif ast.type == 'VarRef':
+        elif ast.tag == 'VarRef':
             self.out.write(ast.value)
-        elif ast.type == 'FunCall':
+        elif ast.tag == 'FunCall':
             self.compile(ast.children[0])
             self.out.write('(')
             self.commas(ast.children[1:])
             self.out.write(')')
-        elif ast.type == 'If':
+        elif ast.tag == 'If':
             self.out.write('if(')
             self.compile(ast.children[0])
             self.out.write(')')
                 self.compile(ast.children[2])
             else:  # just-if
                 self.compile(ast.children[1])
-        elif ast.type == 'Return':
+        elif ast.tag == 'Return':
             self.out.write('return ')
             self.compile(ast.children[0])
-        elif ast.type == 'Break':
+        elif ast.tag == 'Break':
             self.out.write('break')
-        elif ast.type == 'Not':
+        elif ast.tag == 'Not':
             self.out.write('!(')
             self.compile(ast.children[0])
             self.out.write(')')
-        elif ast.type == 'None':
+        elif ast.tag == 'None':
             self.out.write('null')
-        elif ast.type == 'IntLit':
+        elif ast.tag == 'IntLit':
             self.out.write(str(ast.value))
-        elif ast.type == 'StrLit':
+        elif ast.tag == 'StrLit':
             self.out.write("'%s'" % ast.value)
-        elif ast.type == 'BoolLit':
+        elif ast.tag == 'BoolLit':
             if ast.value:
                 self.out.write("true")
             else:
                 self.out.write("false")
-        elif ast.type == 'Assignment':
+        elif ast.tag == 'Assignment':
             self.compile(ast.children[0])
             self.out.write(' = ')
             self.compile(ast.children[1])
-        elif ast.type == 'Make':
+        elif ast.tag == 'Make':
             self.out.write('{')
             self.commas(ast.children[1:])
             self.out.write('}')
-        elif ast.type == 'FieldInit':
+        elif ast.tag == 'FieldInit':
             self.out.write("'%s':" % ast.value)
             self.compile(ast.children[0])
-        elif ast.type == 'Index':
+        elif ast.tag == 'Index':
             self.compile(ast.children[0])
             self.out.write('.%s' % ast.value)
-        elif ast.type == 'TypeCast':
+        elif ast.tag == 'TypeCast':
             self.out.write("['%s'," % ast.aux)
             self.compile(ast.children[0])
             self.out.write(']')
-        elif ast.type == 'TypeCase':
+        elif ast.tag == 'TypeCase':
             self.out.write('if (')
             self.compile(ast.children[0])
             self.out.write("[0] == '%s')" % ast.aux)

File src/castile/backends/ruby.py

             self.compile(asts[-1])
 
     def compile(self, ast):
-        if ast.type == 'Program':
+        if ast.tag == 'Program':
             self.out.write("""\
 # AUTOMATICALLY GENERATED -- EDIT AT OWN RISK
 
   puts(repr(result))
 end
 """)
-        elif ast.type == 'Defn':
+        elif ast.tag == 'Defn':
             self.out.write('%s = ' % ast.value)
             self.compile(ast.children[0])
             self.out.write('\n')
-        elif ast.type in ('Forward'):
+        elif ast.tag == 'Forward':
             self.out.write('%s = nil\n' % ast.value)
-        elif ast.type in ('StructDefn'):
+        elif ast.tag == 'StructDefn':
             pass
-        elif ast.type == 'FunLit':
+        elif ast.tag == 'FunLit':
             self.out.write('lambda { |')
             self.compile(ast.children[0])
             self.out.write('|\n')
             self.compile(ast.children[1])
             self.out.write('return nil }')
-        elif ast.type == 'Args':
+        elif ast.tag == 'Args':
             self.commas(ast.children)
-        elif ast.type == 'Arg':
+        elif ast.tag == 'Arg':
             self.out.write(ast.value)
-        elif ast.type == 'Body':
+        elif ast.tag == 'Body':
             self.compile(ast.children[0])
             self.compile(ast.children[1])
-        elif ast.type == 'VarDecls':
+        elif ast.tag == 'VarDecls':
             for child in ast.children:
                 self.compile(child)
-        elif ast.type == 'VarDecl':
+        elif ast.tag == 'VarDecl':
             self.out.write('%s = ' % ast.value)
             self.compile(ast.children[0])
             self.out.write('\n')
-        elif ast.type == 'Block':
+        elif ast.tag == 'Block':
             for child in ast.children:
                 self.compile(child)
                 self.out.write('\n')
-        elif ast.type == 'While':
+        elif ast.tag == 'While':
             self.out.write('while (')
             self.compile(ast.children[0])
             self.out.write(') do\n')
             self.compile(ast.children[1])
             self.out.write('end\n')
-        elif ast.type == 'Op':
+        elif ast.tag == 'Op':
             self.out.write('(')
             self.compile(ast.children[0])
             self.out.write(' %s ' % OPS.get(ast.value, ast.value))
             self.compile(ast.children[1])
             self.out.write(')')
-        elif ast.type == 'VarRef':
+        elif ast.tag == 'VarRef':
             self.out.write(ast.value)
-        elif ast.type == 'FunCall':
+        elif ast.tag == 'FunCall':
             self.compile(ast.children[0])
             self.out.write('.call(')
             self.commas(ast.children[1:])
             self.out.write(')')
-        elif ast.type == 'If':
+        elif ast.tag == 'If':
             self.out.write('if (')
             self.compile(ast.children[0])
             self.out.write(') then\n')
             else:  # just-if
                 self.compile(ast.children[1])
             self.out.write('end\n')
-        elif ast.type == 'Return':
+        elif ast.tag == 'Return':
             self.out.write('return ')
             self.compile(ast.children[0])
-        elif ast.type == 'Break':
+        elif ast.tag == 'Break':
             self.out.write('break')
-        elif ast.type == 'Not':
+        elif ast.tag == 'Not':
             self.out.write('!(')
             self.compile(ast.children[0])
             self.out.write(')')
-        elif ast.type == 'None':
+        elif ast.tag == 'None':
             self.out.write('nil')
-        elif ast.type == 'BoolLit':
+        elif ast.tag == 'BoolLit':
             if ast.value:
                 self.out.write("true")
             else:
                 self.out.write("false")
-        elif ast.type == 'IntLit':
+        elif ast.tag == 'IntLit':
             self.out.write(str(ast.value))
-        elif ast.type == 'StrLit':
+        elif ast.tag == 'StrLit':
             self.out.write("'%s'" % ast.value)
-        elif ast.type == 'Assignment':
+        elif ast.tag == 'Assignment':
             self.compile(ast.children[0])
             self.out.write(' = ')
             self.compile(ast.children[1])
-        elif ast.type == 'Make':
+        elif ast.tag == 'Make':
             self.out.write('{')
             self.commas(ast.children[1:])
             self.out.write('}')
-        elif ast.type == 'FieldInit':
+        elif ast.tag == 'FieldInit':
             self.out.write("'%s'," % ast.value)
             self.compile(ast.children[0])
-        elif ast.type == 'Index':
+        elif ast.tag == 'Index':
             self.compile(ast.children[0])
             self.out.write('["%s"]' % ast.value)
-        elif ast.type == 'TypeCast':
+        elif ast.tag == 'TypeCast':
             self.out.write("['%s'," % ast.aux)
             self.compile(ast.children[0])
             self.out.write(']')
-        elif ast.type == 'TypeCase':
+        elif ast.tag == 'TypeCase':
             self.out.write('if (')
             self.compile(ast.children[0])
             self.out.write("[0] == '%s')" % ast.aux)

File src/castile/backends/stackmac.py

         self.tag_count = 0      # next tag to generate
 
     def size_of(self, ast):
-        if ast.type == 'Type':
+        if ast.tag == 'Type':
             if ast.value == 'void':
                 return 0
             else:
                 return 1
-        elif ast.type in ('FunType', 'StructType', 'UnionType'):
+        elif ast.tag in ('FunType', 'StructType', 'UnionType'):
             # TODO might be unboxed, all on stack, in future
             return 1
         else:
         return self.tags[value]
 
     def compile(self, ast):
-        if ast.type == 'Program':
+        if ast.tag == 'Program':
             self.out.write("""\
 ; AUTOMATICALLY GENERATED -- EDIT AT OWN RISK
 
 get_global main_index
 call
 """ % self.global_pos)
-        elif ast.type == 'Defn':
+        elif ast.tag == 'Defn':
             self.out.write('%s_index=%d\n' % (ast.value, self.global_pos))
             self.global_pos += 1
             self.compile(ast.children[0])
-        elif ast.type in ('StructDefn', 'Forward'):
+        elif ast.tag in ('StructDefn', 'Forward'):
             pass
-        elif ast.type == 'FunLit':
+        elif ast.tag == 'FunLit':
             past_fun = self.get_label('past_fun')
             self.out.write('jmp %s\n' % past_fun)
             save_fun = self.fun_lit
             # base this on return type: void = 0, int = 1, union = 2, etc
             # TODO use size_of, when it works on types
             returnsize = 1
-            if ast.aux.return_type == Void():
+            if ast.type.return_type == Void():
                 returnsize = 0
             self.out.write('set_returnsize %d\n' % returnsize)
             self.out.write('clear_baseptr %d\n' % (0 - self.fun_argsize))
             self.out.write('push %s\n' % self.fun_lit)
             self.fun_argsize = save_argsize
             self.fun_lit = save_fun
-        elif ast.type == 'Args':
+        elif ast.tag == 'Args':
             argsize = 0
             for child in ast.children:
-                assert child.type == 'Arg'
+                assert child.tag == 'Arg'
                 argsize += self.size_of(child.children[0])
             self.fun_argsize = argsize
             # first arg passed is DEEPEST, so go backwards.
                 self.out.write('%s_local_%s=%d\n' %
                     (self.fun_lit, child.value, pos))
                 pos += self.size_of(child.children[0])
-        elif ast.type == 'Body':
+        elif ast.tag == 'Body':
             self.compile(ast.children[0])
             self.compile(ast.children[1])
-        elif ast.type == 'VarDecls':
+        elif ast.tag == 'VarDecls':
             for child in ast.children:
                 self.compile(child)
-        elif ast.type == 'VarDecl':
+        elif ast.tag == 'VarDecl':
             self.compile(ast.children[0])
             self.out.write('%s_local_%s=%s\n' %
                 (self.fun_lit, ast.value, self.local_pos))
             self.local_pos += 1
-        elif ast.type == 'Block':
+        elif ast.tag == 'Block':
             for child in ast.children:
                 self.compile(child)
-        elif ast.type == 'While':
+        elif ast.tag == 'While':
             start = self.get_label('loop_start')
             end = self.get_label('loop_end')
             save = self.loop_end
             self.out.write('jmp %s\n' % start)
             self.out.write('%s:\n' % end)
             self.loop_end = self.loop_end
-        elif ast.type == 'Op':
+        elif ast.tag == 'Op':
             self.compile(ast.children[0])
             self.compile(ast.children[1])
             self.out.write('%s\n' % OPS.get(ast.value, ast.value))
-        elif ast.type == 'VarRef':
+        elif ast.tag == 'VarRef':
             if ast.aux == 'global':
                 self.out.write('builtin_%s\n' % ast.value)
             elif ast.aux == 'toplevel':
                 self.out.write('get_global %s_index\n' % ast.value)
             else:
                 self.out.write('get_local %s_local_%s\n' % (self.fun_lit, ast.value))
-        elif ast.type == 'FunCall':
+        elif ast.tag == 'FunCall':
             for child in ast.children[1:]:
                 self.out.write('; push argument\n')
                 self.compile(child)
             self.out.write('; push function\n')
             self.compile(ast.children[0])
             self.out.write('call\n')
-        elif ast.type == 'If':
+        elif ast.tag == 'If':
             else_part = self.get_label('else_part')
             end_if = self.get_label('end_if')
             self.compile(ast.children[0])
             if len(ast.children) == 3:
                 self.compile(ast.children[2])
             self.out.write('%s:\n' % end_if)
-        elif ast.type == 'Return':
+        elif ast.tag == 'Return':
             self.compile(ast.children[0])
             self.out.write('jmp exeunt_%s\n' % self.fun_lit)
-        elif ast.type == 'Break':
+        elif ast.tag == 'Break':
             self.out.write('jmp %s\n' % self.loop_end)
-        elif ast.type == 'Not':
+        elif ast.tag == 'Not':
             self.compile(ast.children[0])
             self.out.write('not\n')
-        elif ast.type == 'None':
+        elif ast.tag == 'None':
             pass  # sizeof(void) == 0
-        elif ast.type == 'BoolLit':
+        elif ast.tag == 'BoolLit':
             if ast.value:
                 self.out.write("push -1\n")
             else:
                 self.out.write("push 0\n")
-        elif ast.type == 'IntLit':
+        elif ast.tag == 'IntLit':
             self.out.write('push %s\n' % ast.value)
-        elif ast.type == 'StrLit':
+        elif ast.tag == 'StrLit':
             self.out.write('push %r\n' % ast.value)
-        elif ast.type == 'Assignment':
+        elif ast.tag == 'Assignment':
             self.compile(ast.children[1])
             self.out.write('; assign to...\n')
-            assert ast.children[0].type == 'VarRef'
+            assert ast.children[0].tag == 'VarRef'
             self.out.write('set_local %s_local_%s\n' % (self.fun_lit, ast.children[0].value))
-        elif ast.type == 'Make':
+        elif ast.tag == 'Make':
             # TODO store in the order defined in the struct?
             fields = {}
             for child in ast.children[1:]:
                 self.compile(fields[position])
             self.out.write('push %d\n' % (len(ast.children) - 1))
             self.out.write('make_struct\n')  # sigh
-        elif ast.type == 'FieldInit':
+        elif ast.tag == 'FieldInit':
             self.compile(ast.children[0])
-        elif ast.type == 'Index':
+        elif ast.tag == 'Index':
             self.compile(ast.children[0])
             self.out.write('get_field %d\n' % ast.aux)
-        elif ast.type == 'TypeCast':
+        elif ast.tag == 'TypeCast':
             self.compile(ast.children[0])
             self.out.write('; tag with "%s"\n' % ast.aux)
             if ast.aux == 'void':
                 self.out.write('push 0\n')
             tag = self.get_tag(ast.aux)
             self.out.write('tag %d\n' % tag)
-        elif ast.type == 'TypeCase':
+        elif ast.tag == 'TypeCase':
             end_typecase = self.get_label('end_typecase')
             self.compile(ast.children[0])
             self.out.write('dup\n')
             # set the value to the untagged value of the value
             self.out.write('dup\n')
             self.out.write('get_value\n')
-            assert ast.children[0].type == 'VarRef'
+            assert ast.children[0].tag == 'VarRef'
             self.out.write('set_local %s_local_%s\n' % (self.fun_lit, ast.children[0].value))
             
             self.compile(ast.children[2])

File src/castile/checker.py

         raise CastileTypeError("type mismatch: %s != %s" % (t1, t2))
 
     def is_assignable(self, ast):
-        assert ast.type == 'VarRef'
+        assert ast.tag == 'VarRef'
         name = ast.value
         return name in self.assignable
 
     def collect_structs(self, ast):
         for child in ast.children:
-            if child.type == 'StructDefn':
+            if child.tag == 'StructDefn':
                 self.collect_struct(child)
 
     def collect_struct(self, ast):
         te = []
         i = 0
         for child in ast.children:
-            assert child.type == 'FieldDefn'
+            assert child.tag == 'FieldDefn'
             field_name = child.value
             if field_name in struct_fields:
                 raise CastileTypeError('already-defined field %s' % field_name)
 
     # context is modified as side-effect of traversal
     def type_of(self, ast):
-        if ast.type == 'Op':
+        if ast.tag == 'Op':
             if ast.value in ('and', 'or'):
                 self.assert_eq(self.type_of(ast.children[0]), Boolean())
                 self.assert_eq(self.type_of(ast.children[1]), Boolean())
-                return Boolean()
+                ast.type = Boolean()
             elif ast.value in ('+', '-', '*', '/'):
                 type1 = self.type_of(ast.children[0])
                 type2 = self.type_of(ast.children[1])
                 self.assert_eq(type1, type2)
                 self.assert_eq(type1, Integer())
-                return Integer()
+                ast.type = Integer()
             elif ast.value in ('==', '!=', '>', '>=', '<', '<='):
                 type1 = self.type_of(ast.children[0])
                 type2 = self.type_of(ast.children[1])
                 self.assert_eq(type1, type2)
-                return Boolean()
-        elif ast.type == 'Not':
+                ast.type = Boolean()
+        elif ast.tag == 'Not':
             type1 = self.type_of(ast.children[0])
             self.assert_eq(type1, Boolean())
-            return Boolean()
-        elif ast.type == 'IntLit':
-            return Integer()
-        elif ast.type == 'StrLit':
-            return String()
-        elif ast.type == 'BoolLit':
-            return Boolean()
-        elif ast.type == 'FunLit':
+            ast.type = Boolean()
+        elif ast.tag == 'IntLit':
+            ast.type = Integer()
+        elif ast.tag == 'StrLit':
+            ast.type = String()
+        elif ast.tag == 'BoolLit':
+            ast.type = Boolean()
+        elif ast.tag == 'FunLit':
             save_context = self.context
             self.context = ScopedContext({}, self.toplevel_context)
             self.return_type = None
             self.return_type = None
             if return_type is None:
                 return_type = Void()
-            t = Function(arg_types, return_type)
-            ast.aux = t
-            return t
-        elif ast.type == 'Args':
+            ast.type = Function(arg_types, return_type)
+        elif ast.tag == 'Args':
             types = []
             for child in ast.children:
                 types.append(self.type_of(child))
             return types
-        elif ast.type == 'Arg':
+        elif ast.tag == 'Arg':
             return self.set(ast.value, self.type_of(ast.children[0]))
-        elif ast.type == 'Type':
+        elif ast.tag == 'Type':
             map = {
                 'integer': Integer(),
                 'boolean': Boolean(),
                 'void': Void(),
             }
             return map[ast.value]
-        elif ast.type == 'Body':
+        elif ast.tag == 'Body':
             self.context = ScopedContext({}, self.context)
             for child in ast.children:
                 self.assert_eq(self.type_of(child), Void())
             self.context = self.context.parent
             return Void()
-        elif ast.type == 'VarDecls':
+        elif ast.tag == 'VarDecls':
             for child in ast.children:
                 self.assert_eq(self.type_of(child), Void())
             return Void()
-        elif ast.type == 'VarDecl':
+        elif ast.tag == 'VarDecl':
             name = ast.value
             if name in self.context:
                 raise CastileTypeError('declaration of %s shadows previous' % name)
             self.assignable[name] = True
             self.set(name, self.type_of(ast.children[0]))
             return Void()
-        elif ast.type == 'FunType':
+        elif ast.tag == 'FunType':
             return_type = self.type_of(ast.children[0])
             return Function([self.type_of(c) for c in ast.children[1:]],
                             return_type)
-        elif ast.type == 'UnionType':
+        elif ast.tag == 'UnionType':
             return Union([self.type_of(c) for c in ast.children])
-        elif ast.type == 'StructType':
+        elif ast.tag == 'StructType':
             return Struct(ast.value)
-        elif ast.type == 'VarRef':
-            v = self.context[ast.value]
+        elif ast.tag == 'VarRef':
+            ast.type = self.context[ast.value]
             ast.aux = self.context.level(ast.value)
-            return v
-        elif ast.type == 'None':
-            return Void()
-        elif ast.type == 'FunCall':
+        elif ast.tag == 'None':
+            ast.type = Void()
+        elif ast.tag == 'FunCall':
             t1 = self.type_of(ast.children[0])
             assert isinstance(t1, Function), \
               '%r is not a function' % t1
             for child in ast.children[1:]:
                 self.assert_eq(self.type_of(child), t1.arg_types[i])
                 i += 1
-            return t1.return_type
-        elif ast.type == 'Return':
+            ast.type = t1.return_type
+        elif ast.tag == 'Return':
             t1 = self.type_of(ast.children[0])
             if self.return_type is None:
                 self.return_type = t1
             else:
                 self.assert_eq(t1, self.return_type)
             return Void()
-        elif ast.type == 'Break':
+        elif ast.tag == 'Break':
             return Void()
-        elif ast.type == 'If':
+        elif ast.tag == 'If':
             t1 = self.type_of(ast.children[0])
             assert t1 == Boolean()
             t2 = self.type_of(ast.children[1])
                 return t2
             else:
                 return Void()
-        elif ast.type == 'While':
+        elif ast.tag == 'While':
             t1 = self.type_of(ast.children[0])
             assert t1 == Boolean()
             t2 = self.type_of(ast.children[1])
             return Void()
-        elif ast.type == 'Block':
+        elif ast.tag == 'Block':
             for child in ast.children:
                 self.assert_eq(self.type_of(child), Void())
             return Void()
-        elif ast.type == 'Assignment':
+        elif ast.tag == 'Assignment':
             t1 = self.type_of(ast.children[0])
             if not self.is_assignable(ast.children[0]):
                 raise CastileTypeError('cannot assign to non-local')
             t2 = self.type_of(ast.children[1])
             self.assert_eq(t1, t2)
             return Void()
-        elif ast.type == 'Make':
+        elif ast.tag == 'Make':
             t = self.type_of(ast.children[0])
             if t.name not in self.structs:
                 raise CastileTypeError("undefined struct %s" % t.name)
                 defn.aux = pos
                 self.assert_eq(t1, struct_defn.content_types[pos])
                 i += 1
-            return t
-        elif ast.type == 'FieldInit':
+            ast.type = t
+        elif ast.tag == 'FieldInit':
             return self.type_of(ast.children[0])
-        elif ast.type == 'Index':
+        elif ast.tag == 'Index':
             t = self.type_of(ast.children[0])
             field_name = ast.value
             struct_fields = self.struct_fields[t.name]
             # we make this value available to compiler backends
             ast.aux = index
             # we look up the type from the StructDefinition
-            return self.structs[t.name].content_types[index]
-        elif ast.type == 'TypeCase':
+            ast.type = self.structs[t.name].content_types[index]
+        elif ast.tag == 'TypeCase':
             t1 = self.type_of(ast.children[0])
             t2 = self.type_of(ast.children[1])
             if not isinstance(t1, Union):
             if not t1.contains(t2):
                 raise CastileTypeError('bad typecase, %s not in %s' % (t2, t1))
             # typecheck t3 with variable in children[0] having type t2
-            assert ast.children[0].type == 'VarRef'
+            assert ast.children[0].tag == 'VarRef'
             self.context = ScopedContext({}, self.context)
             self.context[ast.children[0].value] = t2
             t3 = self.type_of(ast.children[2])
             self.context = self.context.parent
             ast.aux = str(t2)
             return t3
-        elif ast.type == 'Program':
+        elif ast.tag == 'Program':
             for defn in ast.children:
                 t1 = self.type_of(defn)
             return Void()
-        elif ast.type == 'Defn':
+        elif ast.tag == 'Defn':
             # reset assignable
             self.assignable = {}
             t = self.type_of(ast.children[0])
                 rt = t.return_type
                 self.assert_eq(t, Function([], rt))
             return t
-        elif ast.type == 'Forward':
+        elif ast.tag == 'Forward':
             t = self.type_of(ast.children[0])
             self.forwards[ast.value] = t
             return self.set(ast.value, t)
-        elif ast.type == 'StructDefn':
+        elif ast.tag == 'StructDefn':
             pass
-        elif ast.type == 'TypeCast':
+        elif ast.tag == 'TypeCast':
             val_t = self.type_of(ast.children[0])
             uni_t = self.type_of(ast.children[1])
             if not isinstance(uni_t, Union):
                                   (uni_t, val_t))
             # for compiler's benefit
             ast.aux = str(val_t)
-            return uni_t
+            ast.type = uni_t
         else:
             raise NotImplementedError(repr(ast))
+        return ast.type

File src/castile/eval.py

 class Closure(object):
     def __init__(self, program, ast):
         self.program = program
-        assert callable(ast) or ast.type == 'FunLit'
+        assert callable(ast) or ast.tag == 'FunLit'
         self.ast = ast
         self.locals = {}
 
         if callable(self.ast):  # for builtins
             return self.ast(*actuals)
         formals = self.ast.children[0]
-        assert formals.type == 'Args'
+        assert formals.tag == 'Args'
         i = 0
         for formal in formals.children:
-            assert formal.type == 'Arg'
+            assert formal.tag == 'Arg'
             self.locals[formal.value] = actuals[i]
             i += 1
         try:
     def eval(self, ast=None):
         if ast is None:
             ast = self.ast.children[1]
-        if ast.type == 'Body':
+        if ast.tag == 'Body':
             self.eval(ast.children[0])  # to collect locals
             return self.eval(ast.children[1])
-        elif ast.type == 'VarDecls':
+        elif ast.tag == 'VarDecls':
             for child in ast.children:
                 self.eval(child)
             return None
-        elif ast.type == 'VarDecl':
+        elif ast.tag == 'VarDecl':
             name = ast.value
             v = self.eval(ast.children[0])
             self.locals[name] = v
             return None
-        elif ast.type == 'Block':
+        elif ast.tag == 'Block':
             v1 = None
             for stmt in ast.children:
                 v1 = self.eval(stmt)
             return v1
-        elif ast.type == 'If':
+        elif ast.tag == 'If':
             v1 = self.eval(ast.children[0])
             if len(ast.children) == 3:  # if-else
                 if v1:
                 if v1:
                     self.eval(ast.children[1])
                 return None
-        elif ast.type == 'Return':
+        elif ast.tag == 'Return':
             v1 = self.eval(ast.children[0])
             raise FunctionReturn(v1)
-        elif ast.type == 'Break':
+        elif ast.tag == 'Break':
             raise WhileBreak()
-        elif ast.type == 'While':
+        elif ast.tag == 'While':
             v1 = self.eval(ast.children[0])
             try:
                 while v1:
             except WhileBreak:
                 pass
             return None
-        elif ast.type == 'Op':
+        elif ast.tag == 'Op':
             v1 = self.eval(ast.children[0])
             op = OPS[ast.value]
             v2 = self.eval(ast.children[1])
             return op(v1, v2)
-        elif ast.type == 'Not':
+        elif ast.tag == 'Not':
             return not self.eval(ast.children[0])
-        elif ast.type == 'VarRef':
+        elif ast.tag == 'VarRef':
             name = ast.value
             if name in self.locals:
                 return self.locals[name]
             return self.program.stab[name]
-        elif ast.type in ['IntLit', 'StrLit', 'BoolLit']:
+        elif ast.tag in ['IntLit', 'StrLit', 'BoolLit']:
             return ast.value
-        elif ast.type in ['None']:
+        elif ast.tag == 'None':
             return None
-        elif ast.type == 'FunLit':
+        elif ast.tag == 'FunLit':
             return Closure(self.program, ast)
-        elif ast.type == 'FunCall':
+        elif ast.tag == 'FunCall':
             fun_val = self.eval(ast.children[0])
             actuals = [self.eval(arg) for arg in ast.children[1:]]
             return fun_val.call(actuals)
-        elif ast.type == 'Assignment':
+        elif ast.tag == 'Assignment':
             var_ref = ast.children[0]
             name = var_ref.value
             v = self.eval(ast.children[1])
             self.locals[name] = v
             return None
-        elif ast.type == 'Index':
+        elif ast.tag == 'Index':
             v = self.eval(ast.children[0])
             return v[ast.value]
-        elif ast.type == 'Make':
+        elif ast.tag == 'Make':
             v = StructDict(ast.value, [arg.value for arg in ast.children[1:]])
             for arg in ast.children[1:]:
                 v[arg.value] = self.eval(arg.children[0])
             return v
-        elif ast.type == 'TypeCast':
+        elif ast.tag == 'TypeCast':
             v = self.eval(ast.children[0])
             return TaggedValue(typeof(v), v)
-        elif ast.type == 'TypeCase':
+        elif ast.tag == 'TypeCase':
             r = self.eval(ast.children[0])
             assert isinstance(r, TaggedValue)
             if r.tag == ast.value:
             self.stab[name] = value
 
     def load(self, ast):
-        assert ast.type == 'Program'
+        assert ast.tag == 'Program'
         # dummy closure for evaluating literals at toplevel
         toplevel = Closure(self, lambda x: x)
         for child in ast.children:
-            if child.type == 'Defn':
+            if child.tag == 'Defn':
                 self.stab[child.value] = toplevel.eval(child.children[0])
 
     def run(self):

File src/castile/parser.py

         self.expect('}')
         return AST('Block', stmts)
 
-    STMT_TYPES = ('If', 'While', 'TypeCase', 'Return', 'Break')
+    STMT_TAGS = ('If', 'While', 'TypeCase', 'Return', 'Break')
 
     def body(self):
         # block for a function body -- automatically promotes the
             self.consume(';')
         if len(stmts) == 0:
             stmts = [AST('Return', [AST('None')])]
-        elif last is not None and last.type not in self.STMT_TYPES:
+        elif last is not None and last.tag not in self.STMT_TAGS:
             stmts[-1] = AST('Return', [stmts[-1]])
         self.expect('}')
         vardecls = AST('VarDecls', vardecls)

File src/castile/transformer.py

         return 'lifted_function%d' % self.count
 
     def lift_functions(self, ast):
-        if ast.type == 'Program':
+        if ast.tag == 'Program':
             children = []
             for child in ast.children:
                 children.append(self.lift_functions(child))
             non_lifted_defns = []
             for child in ast.children:
                 if child.children:
-                    if child.children[0].type == 'FunLit':
+                    if child.children[0].tag == 'FunLit':
                         non_lifted_defns.append(child)
                     else:
                         non_fun_defns.append(child)
             children = non_fun_defns + lifted_defns + non_lifted_defns
-            return AST(ast.type, children, value=ast.value)
-        elif ast.type == 'Defn':
+            return AST(ast.tag, children, value=ast.value)
+        elif ast.tag == 'Defn':
             # skip toplevel funlits; they don't have to be lifted.
             children = []
             for child in ast.children:
-                if child.type == 'FunLit':
+                if child.tag == 'FunLit':
                     grandchildren = []
                     for grandchild in child.children:
                         grandchildren.append(self.lift_functions(grandchild))
                     children.append(AST('FunLit', grandchildren, value=child.value))
                 else:
                     children.append(self.lift_functions(child))
-            return AST(ast.type, children, value=ast.value)
-        elif ast.type == 'FunLit':
+            return AST(ast.tag, children, value=ast.value)
+        elif ast.tag == 'FunLit':
             children = []
             for child in ast.children:
                 children.append(self.lift_functions(child))
-            new_ast = AST(ast.type, children, value=ast.value)
+            new_ast = AST(ast.tag, children, value=ast.value)
             new_ast.aux = ast.aux   # TODO i wish there was a nicer way
             name = self.make_name()
             self.lifted_functions.append((name, new_ast))
             children = []
             for child in ast.children:
                 children.append(self.lift_functions(child))
-            a = AST(ast.type, children, value=ast.value)
+            a = AST(ast.tag, children, value=ast.value)
             a.aux = ast.aux
             return a