Commits

catseye  committed 1ac6f53

Prevent local vars defined inside control blocks. No 'var'.

  • Participants
  • Parent commits daae52c

Comments (0)

Files changed (11)

File README.markdown

     language-level pointers; sharing, if it happens at all, must be
     orchestrated by the implementation.  Global variables and function
     arguments are not mutable, and neither are the fields of structs.
-    (However, local variables *are* mutable.)
+    (But unlike Erlang, local variables *are* mutable.)
 
 Some lines of research underneath all this are, if all we have is a relatively
 crude language, but we make it typesafe and give it a slightly nicer type
               | "(" Expr0 ")"
               | "not" Expr1
               | Literal
-              | ["var"] ident ["=" Expr0].
+              | ident ["=" Expr0].
     Literal ::= strlit
               | ["-"] intlit
               | "true" | "false" | "null"
     | }
     ? undefined
 
-A local variables should not be defined inside an `if`... yeah, tricky, I don't
-think we're there yet.
-
-    | fun main() {
-    |   if (4 > 5) {
-    |     a = 10;
-    |   } else {
-    |     b = 11;
-    |   }
-    |   a
-    | }
-    ? undefined
+A local variables may not be defined inside an `if` or `while` or `typecase`
+block, as it might not be executed.
 
     | fun main() {
     |   if (4 > 5) {
     |   }
     |   b
     | }
-    ? undefined
+    ? within control
+
+    | fun main() {
+    |   b = false;
+    |   while b {
+    |     a = 10;
+    |   }
+    |   a
+    | }
+    ? within control
+
+    | fun main() {
+    |   a = 55 as integer|string;
+    |   typecase a is string {
+    |     b = 7
+    |   }
+    |   a
+    | }
+    ? within control
 
 Assignment, though it syntactically may occur in expressions, has a type of
 void, so it can only really happen at the statement level.

File TODO.markdown

 
 Tests for unions of unions.
 
+Test for equality of union values.
+
 Tests for multiple occurrences of same type in a union.
 
 ### Implementation ###
 
 Convenience:
 
-*   Allow `var x` anywhere, and lift to front of function.
-*   Allow `x = expr` to stand for `var x = expr`, if it's first.  But beware the
-    Lua/Python war on "explicit local".
 *   Should we have automatic promotion (value tagging?)
     Since it causes an operation, I think it should be explicit, but the
     explicit syntax could be more lightweight.

File eg/cat.castile

 fun main() {
-  var c = read(1);
-  var l = 0;
+  c = read(1);
+  l = 0;
   while (len(c) == 1) {
     l = write(c);
     c = read(1)

File eg/deadfish.castile

 fun main() {
-  var a = 0;
-  var c = "";
+  a = 0;
+  c = "";
   while true {
     if a == 256 or a < 0 {
 	a = 0;

File eg/factorial.castile

 }
 
 fun main() {
-  var n = 0 as integer|void;
-  var s = "";
+  n = 0 as integer|void;
+  s = "";
   print("Enter integer to obtain factorial of it, or 'q' to quit.");
   while true {
     s = input(">> ");

File eg/linkedlist.castile

 }
 
 main = fun() {
-  var l = cons("first", cons("second", cons("third", empty())));
-  
-  var h = l;
-  var next = empty();
+  l = cons("first", cons("second", cons("third", empty())));
+
+  h = l;
+  next = empty();
 
   /*
    * Note that we cannot say "h = h.next" in the following loop,

File eg/simple.castile

 fun main() {
-  var x = 12;
+  x = 12;
   if 4 < 5 {
     x = 2 * 3 + 4
   } else {

File eg/tokenizer.castile

 fun instr(c: string, s: string) {
-  var i = 0;
+  i = 0;
   while i < len(s) {
     if c == substr(s, i, 1) { return true; }
     i = i + 1
 }
 
 fun scan(s: string) {
-  var i = 0;
-  var t = "";
-  var c = "";
+  i = 0;
+  t = "";
+  c = "";
   while true {
     c = substr(s, i, 1);
     if c != " " {
 }
 
 fun main() {
-  var s = input(">> ")
-  var r = make result(token: "", remainder: "")
+  s = input(">> ")
+  r = make result(token: "", remainder: "")
   while len(s) > 0 {
     while len(s) > 0 {
       r = scan(s);

File eg/typecase.castile

 fun foo(a, b: integer|string) {
-  var r = a;
+  r = a;
   typecase b is integer {
     r = r + b;
   };
   r
 }
 main = fun() {
-  var a = 0;
   a = foo(a, 333 as integer|string);
   a = foo(a, "hiya" as integer|string);
   a /* should output 337 */

File src/castile/checker.py

         self.forwards = {}
         self.structs = {}  # struct name -> StructDefinition
         self.return_type = None
+        self.within_control = False
+
         self.verbose = False
 
     def set(self, name, type):
         elif ast.tag == 'Break':
             ast.type = Void()
         elif ast.tag == 'If':
+            within_control = self.within_control
+            self.within_control = True
             t1 = self.type_of(ast.children[0])
             assert t1 == Boolean()
             t2 = self.type_of(ast.children[1])
                 ast.type = t2
             else:
                 ast.type = Void()
+            self.within_control = within_control
         elif ast.tag == 'While':
+            within_control = self.within_control
+            self.within_control = True
             t1 = self.type_of(ast.children[0])
             self.assert_eq(t1, Boolean())
             t2 = self.type_of(ast.children[1])
             ast.type = Void()
+            self.within_control = within_control
         elif ast.tag == 'Block':
             for child in ast.children:
                 self.assert_eq(self.type_of(child), Void())
             t1 = None
             name = ast.children[0].value
             if ast.aux == 'defining instance':
+                if self.within_control:
+                    raise CastileTypeError('definition of %s within control block' % name)
                 if name in self.context:
                     raise CastileTypeError('definition of %s shadows previous' % name)
                 self.set(name, 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].tag == 'VarRef'
+            within_control = self.within_control
+            self.within_control = True
             self.context = ScopedContext({}, self.context, level='typecase')
             self.context[ast.children[0].value] = t2
             ast.type = self.type_of(ast.children[2])
             self.context = self.context.parent
+            self.within_control = within_control
         elif ast.tag == 'Program':
             for defn in ast.children:
                 self.assert_eq(self.type_of(defn), Void())

File src/castile/parser.py

             return
         if self.scan_pattern(r'and|or', 'boolean operator'):
             return
-        if self.scan_pattern(r'(var|if|else|while|make|struct|'
+        if self.scan_pattern(r'(if|else|while|make|struct|'
                              r'typecase|is|as|return|break|'
                              r'true|false|null)(?!\w)',
                              'keyword', token_group=2, rest_group=3):
             self.expect(')')
             return e
         else:
-            self.consume('var')
             id = self.expect_type('identifier')
             ast = AST('VarRef', value=id)
             if self.consume('='):