Commits

Anonymous committed c3f2dea

Clean up AST.aux, allow empty structs in stackmac; all tests pass.

Comments (0)

Files changed (8)

     | struct person { name: string; age: integer }
     = 23
 
-Structs may contain structs which don't exist.  (Surprisingly.  Might just leave this in.)
+Structs may not contain structs which don't exist.
 
     | struct person { name: string; age: foobar }
     | main = fun() { 333 }
-    = 333
-
-    | struct person { name: string; age: foobar }
-    | main = fun() { make person(name:"Jake", age:23) }
-    ? type mismatch
+    ? undefined
 
 Types must match when making a struct.
 
     | }
     = second
 
-Structs may be empty.  In combination with unions, this lets us create
-"typed enums".
+Structs may be empty.
+
+    | struct red { }
+    | fun show(color: red) {
+    |   print("hi")
+    | }
+    | main = fun() {
+    |   show(make red());
+    | }
+    = hi
+
+In combination with unions, this lets us create "typed enums".
 
     | struct red { }
     | struct green { }
         # typechecker may populate this.  parser will not.
         self.type = type
         # typechecker may populate this.  parser will not.
-        # TODO document what it means for particular nodes
+        # on VarRef nodes, this is the level of the reference
+        #  ('global', 'toplevel', or None for locals)
+        # on FieldInit and Index nodes, this is the position
+        #  (offset) of the field within the struct
         self.aux = aux
         if children is not None:
             self.children = children

src/castile/backends/javascript.py

             self.compile(ast.children[0])
             self.out.write('.%s' % ast.value)
         elif ast.tag == 'TypeCast':
-            self.out.write("['%s'," % ast.aux)
+            self.out.write("['%s'," % str(ast.children[0].type))
             self.compile(ast.children[0])
             self.out.write(']')
         elif ast.tag == 'TypeCase':
             self.out.write('if (')
             self.compile(ast.children[0])
-            self.out.write("[0] == '%s')" % ast.aux)
+            self.out.write("[0] == '%s')" % str(ast.children[1].type))
             self.out.write('{ var save=')
             self.compile(ast.children[0])
             self.out.write('; ')

src/castile/backends/ruby.py

             self.compile(ast.children[0])
             self.out.write('["%s"]' % ast.value)
         elif ast.tag == 'TypeCast':
-            self.out.write("['%s'," % ast.aux)
+            self.out.write("['%s'," % str(ast.children[0].type))
             self.compile(ast.children[0])
             self.out.write(']')
         elif ast.tag == 'TypeCase':
             self.out.write('if (')
             self.compile(ast.children[0])
-            self.out.write("[0] == '%s')" % ast.aux)
+            self.out.write("[0] == '%s')" % str(ast.children[1].type))
             self.out.write('then save=')
             self.compile(ast.children[0])
             self.out.write('\n')

src/castile/backends/stackmac.py

-from castile.types import Void, String
+from castile.types import *
 
 # Compile to some hypothetical stack-based machine.
 # Not yet in a good way.
         self.tags = {}          # numeric tags established so far
         self.tag_count = 0      # next tag to generate
 
-    def size_of(self, ast):
-        if ast.tag == 'Type':
-            if ast.value == 'void':
+    def size_of(self, type):
+        if type == Void():
+            return 0
+        elif isinstance(type, Struct):
+            if self.struct_size(type) == 0:
                 return 0
             else:
                 return 1
-        elif ast.tag in ('FunType', 'StructType', 'UnionType'):
+        elif isinstance(type, Union):
             # TODO might be unboxed, all on stack, in future
             return 1
         else:
-            raise NotImplementedError(ast)
+            return 1
+
+    def struct_size(self, type):
+        assert isinstance(type, Struct)
+        size = 0
+        for t in type.defn.content_types:
+            size += self.size_of(t)
+        return size
 
     def get_label(self, pref):
         count = self.labels.get(pref, 0)
             # TODO copy the result value(S) to the first arg position
             # (for now the opcode handles that)
             self.out.write('exeunt_%s:\n' % 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.type.return_type == Void():
-                returnsize = 0
+            returnsize = self.size_of(ast.type.return_type)
             self.out.write('set_returnsize %d\n' % returnsize)
             self.out.write('clear_baseptr %d\n' % (0 - self.fun_argsize))
             self.out.write('rts\n')
             argsize = 0
             for child in ast.children:
                 assert child.tag == 'Arg'
-                argsize += self.size_of(child.children[0])
+                argsize += self.size_of(child.type)
             self.fun_argsize = argsize
             # first arg passed is DEEPEST, so go backwards.
             pos = 0 - self.fun_argsize
             for child in ast.children:
                 self.out.write('%s_local_%s=%d\n' %
                     (self.fun_lit, child.value, pos))
-                pos += self.size_of(child.children[0])
+                pos += self.size_of(ast.type)
         elif ast.tag == 'Body':
             self.compile(ast.children[0])
             self.compile(ast.children[1])
                 fields[child.aux] = child   # FieldInit.aux = position in struct
             for position in sorted(fields):
                 self.compile(fields[position])
-            self.out.write('push %d\n' % (len(ast.children) - 1))
-            self.out.write('make_struct\n')  # sigh
+            self.out.write('make_struct %d\n' % self.struct_size(ast.type))
         elif ast.tag == 'FieldInit':
             self.compile(ast.children[0])
         elif ast.tag == 'Index':
             self.out.write('get_field %d\n' % ast.aux)
         elif ast.tag == 'TypeCast':
             self.compile(ast.children[0])
-            self.out.write('; tag with "%s"\n' % ast.aux)
-            if ast.aux == 'void':
+            t = str(ast.children[0].type)
+            self.out.write('; tag with "%s"\n' % t)
+            if self.size_of(ast.children[0].type) == 0:
                 # special case.  there is nothing on the stack
                 self.out.write('push 0\n')
-            tag = self.get_tag(ast.aux)
+            tag = self.get_tag(t)
             self.out.write('tag %d\n' % tag)
         elif ast.tag == 'TypeCase':
             end_typecase = self.get_label('end_typecase')
             self.compile(ast.children[0])
             self.out.write('dup\n')
             self.out.write('get_tag\n')
-            tag = self.get_tag(ast.aux)
+            tag = self.get_tag(str(ast.children[1].type))
             self.out.write('push %d\n' % tag)
             self.out.write('eq\n')
             self.out.write('bzero %s\n' % end_typecase)

src/castile/checker.py

             te.append(self.type_of(child.children[0]))
         self.structs[name] = StructDefinition(ast.value, struct_fields, te)
 
+    def resolve_structs(self, ast):
+          if isinstance(ast.type, Struct):
+              if ast.type.name not in self.structs:
+                  raise CastileTypeError('undefined struct %s' % name)
+              ast.type.defn = self.structs[ast.type.name]
+          for child in ast.children:
+              self.resolve_structs(child)
+
     # context is modified as side-effect of traversal
     def type_of(self, ast):
         if ast.tag == 'Op':
             self.context[ast.children[0].value] = t2
             ast.type = self.type_of(ast.children[2])
             self.context = self.context.parent
-            ast.aux = str(t2)
         elif ast.tag == 'Program':
             for defn in ast.children:
                 self.assert_eq(self.type_of(defn), Void())
             ast.type = Void()
+            self.resolve_structs(ast)
         elif ast.tag == 'Defn':
             # reset assignable
             self.assignable = {}
             if not uni_t.contains(val_t):
                 raise CastileTypeError('bad cast, %s does not include %s' %
                                   (uni_t, val_t))
-            # for compiler's benefit
-            ast.aux = str(val_t)
             ast.type = uni_t
         else:
             raise NotImplementedError(repr(ast))

src/castile/stackmac.py

         elif op == 'set_local':
             stack[baseptr + arg] = stack.pop()
         elif op == 'make_struct':
-            size = stack.pop()
-            struct = stack[-size:]
-            stack = stack[:-size]
-            stack.append(struct)
+            if arg > 0:
+                struct = stack[-arg:]
+                stack = stack[:-arg]
+                stack.append(struct)
         elif op == 'get_field':
             obj = stack.pop()
             stack.append(obj[arg])
 class Struct(Type):
     def __init__(self, name):
         self.name = name
+        self.defn = None
 
     def __str__(self):
         return "struct %s" % self.name