]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: use new slice syntax for argv argument.
[ocean] / csrc / oceani.mdc
index dca205570c9af2c1ae704e0a58c04f01bdd96782..a3574ea24e91167880aa12d2cff4bcefc1a1e412 100644 (file)
@@ -367,14 +367,14 @@ context so indicate that parsing failed.
        static void fput_loc(struct exec *loc, FILE *f);
        static void type_err(struct parse_context *c,
                             char *fmt, struct exec *loc,
        static void fput_loc(struct exec *loc, FILE *f);
        static void type_err(struct parse_context *c,
                             char *fmt, struct exec *loc,
-                            struct type *t1, int rules, struct type *t2);
+                            struct type *t1, enum val_rules rules, struct type *t2);
        static void tok_err(struct parse_context *c, char *fmt, struct token *t);
 
 ###### core functions
 
        static void type_err(struct parse_context *c,
                             char *fmt, struct exec *loc,
        static void tok_err(struct parse_context *c, char *fmt, struct token *t);
 
 ###### core functions
 
        static void type_err(struct parse_context *c,
                             char *fmt, struct exec *loc,
-                            struct type *t1, int rules, struct type *t2)
+                            struct type *t1, enum val_rules rules, struct type *t2)
        {
                fprintf(stderr, "%s:", c->file_name);
                fput_loc(loc, stderr);
        {
                fprintf(stderr, "%s:", c->file_name);
                fput_loc(loc, stderr);
@@ -592,21 +592,24 @@ passed by reference.  It has `Efail` set when an error is found, and
 any expression cannot be evaluated a compile time, `Eruntime` is set.
 If the expression can be copied, `Emaycopy` is set.
 
 any expression cannot be evaluated a compile time, `Eruntime` is set.
 If the expression can be copied, `Emaycopy` is set.
 
-If it remains unchanged at `0`, then no more propagation is needed.
+If `Erval` is set, then the value cannot be assigned to because it is
+a temporary result.  If `Erval` is clear but `Econst` is set, then
+the value can only be assigned once, when the variable is declared.
 
 ###### ast
 
 
 ###### ast
 
-       enum val_rules {Rboolok = 1<<1, Rnoconstant = 1<<2};
+       enum val_rules {Rboolok = 1<<0, Rrefok = 1<<1,};
        enum prop_err {Efail = 1<<0, Eretry = 1<<1, Eruntime = 1<<2,
        enum prop_err {Efail = 1<<0, Eretry = 1<<1, Eruntime = 1<<2,
-                      Emaycopy = 1<<3};
+                      Emaycopy = 1<<3, Erval = 1<<4, Econst = 1<<5};
 
 ###### forward decls
        static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
 
 ###### forward decls
        static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
-                                           struct type *type, int rules);
+                                           struct type *type, enum val_rules rules);
 ###### core functions
 
        static struct type *__propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
 ###### core functions
 
        static struct type *__propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
-                                             struct type *type, int rules)
+                                             enum prop_err *perr_local,
+                                             struct type *type, enum val_rules rules)
        {
                struct type *t;
 
        {
                struct type *t;
 
@@ -628,11 +631,13 @@ If it remains unchanged at `0`, then no more propagation is needed.
        }
 
        static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
        }
 
        static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
-                                           struct type *type, int rules)
+                                           struct type *type, enum val_rules rules)
        {
                int pre_err = c->parse_error;
        {
                int pre_err = c->parse_error;
-               struct type *ret = __propagate_types(prog, c, perr, type, rules);
+               enum prop_err perr_local = 0;
+               struct type *ret = __propagate_types(prog, c, perr, &perr_local, type, rules);
 
 
+               *perr |= perr_local & (Efail | Eretry);
                if (c->parse_error > pre_err)
                        *perr |= Efail;
                return ret;
                if (c->parse_error > pre_err)
                        *perr |= Efail;
                return ret;
@@ -986,7 +991,7 @@ which might be reported in error messages.
 ###### forward decls
 
        static void free_value(struct type *type, struct value *v);
 ###### forward decls
 
        static void free_value(struct type *type, struct value *v);
-       static int type_compat(struct type *require, struct type *have, int rules);
+       static int type_compat(struct type *require, struct type *have, enum val_rules rules);
        static void type_print(struct type *type, FILE *f);
        static void val_init(struct type *type, struct value *v);
        static void dup_value(struct type *type,
        static void type_print(struct type *type, FILE *f);
        static void val_init(struct type *type, struct value *v);
        static void dup_value(struct type *type,
@@ -1051,11 +1056,12 @@ A separate function encoding these cases will simplify some code later.
 
 ###### type functions
 
 
 ###### type functions
 
-       int (*compat)(struct type *this, struct type *other);
+       int (*compat)(struct type *this, struct type *other, enum val_rules rules);
 
 ###### ast functions
 
 
 ###### ast functions
 
-       static int type_compat(struct type *require, struct type *have, int rules)
+       static int type_compat(struct type *require, struct type *have,
+                              enum val_rules rules)
        {
                if ((rules & Rboolok) && have == Tbool)
                        return 1;       // NOTEST
        {
                if ((rules & Rboolok) && have == Tbool)
                        return 1;       // NOTEST
@@ -1063,7 +1069,7 @@ A separate function encoding these cases will simplify some code later.
                        return 1;
 
                if (require->compat)
                        return 1;
 
                if (require->compat)
-                       return require->compat(require, have);
+                       return require->compat(require, have, rules);
 
                return require == have;
        }
 
                return require == have;
        }
@@ -1313,6 +1319,7 @@ executable.
                if (!type_compat(type, val->vtype, rules))
                        type_err(c, "error: expected %1 found %2",
                                   prog, type, rules, val->vtype);
                if (!type_compat(type, val->vtype, rules))
                        type_err(c, "error: expected %1 found %2",
                                   prog, type, rules, val->vtype);
+               *perr |= Erval;
                return val->vtype;
        }
 
                return val->vtype;
        }
 
@@ -1435,6 +1442,7 @@ match "case".
                if (!type_compat(type, Tlabel, rules))
                        type_err(c, "error: expected %1 found %2",
                                 prog, type, rules, Tlabel);
                if (!type_compat(type, Tlabel, rules))
                        type_err(c, "error: expected %1 found %2",
                                 prog, type, rules, Tlabel);
+               *perr |= Erval;
                return Tlabel;
        }
 ###### interp exec cases
                return Tlabel;
        }
 ###### interp exec cases
@@ -2266,13 +2274,6 @@ correctly.
                        return Tnone;                                   // NOTEST
                }
                v = v->merged;
                        return Tnone;                                   // NOTEST
                }
                v = v->merged;
-               if (v->constant && (rules & Rnoconstant)) {
-                       type_err(c, "error: Cannot assign to a constant: %v",
-                                prog, NULL, 0, NULL);
-                       type_err(c, "info: name was defined as a constant here",
-                                v->where_decl, NULL, 0, NULL);
-                       return v->type;
-               }
                if (v->type == Tnone && v->where_decl == prog)
                        type_err(c, "error: variable used but not declared: %v",
                                 prog, NULL, 0, NULL);
                if (v->type == Tnone && v->where_decl == prog)
                        type_err(c, "error: variable used but not declared: %v",
                                 prog, NULL, 0, NULL);
@@ -2290,9 +2291,9 @@ correctly.
                }
                if (!v->global || v->frame_pos < 0)
                        *perr |= Eruntime;
                }
                if (!v->global || v->frame_pos < 0)
                        *perr |= Eruntime;
-               if (!type)
-                       return v->type;
-               return type;
+               if (v->constant)
+                       *perr |= Econst;
+               return v->type;
        }
 
 ###### interp exec cases
        }
 
 ###### interp exec cases
@@ -2459,22 +2460,19 @@ with a const size by whether they are prepared at parse time or not.
                        free(ptr);
        }
 
                        free(ptr);
        }
 
-       static int array_compat(struct type *require, struct type *have)
+       static int array_compat(struct type *require, struct type *have,
+                               enum val_rules rules)
        {
                if (have->compat != require->compat)
                        return 0;
                /* Both are arrays, so we can look at details */
                if (!type_compat(require->array.member, have->array.member, 0))
                        return 0;
        {
                if (have->compat != require->compat)
                        return 0;
                /* Both are arrays, so we can look at details */
                if (!type_compat(require->array.member, have->array.member, 0))
                        return 0;
-               if (have->array.unspec && require->array.unspec) {
-                       if (have->array.vsize && require->array.vsize &&
-                           have->array.vsize != require->array.vsize)  // UNTESTED
-                               /* sizes might not be the same */
-                               return 0;       // UNTESTED
-                       return 1;
-               }
+               if (have->array.unspec && require->array.unspec &&
+                   have->array.size != require->array.size)
+                       return 0;       // UNTESTED
                if (have->array.unspec || require->array.unspec)
                if (have->array.unspec || require->array.unspec)
-                       return 1;       // UNTESTED
+                       return 1;
                if (require->array.vsize == NULL && have->array.vsize == NULL)
                        return require->array.size == have->array.size;
 
                if (require->array.vsize == NULL && have->array.vsize == NULL)
                        return require->array.size == have->array.size;
 
@@ -2552,29 +2550,18 @@ with a const size by whether they are prepared at parse time or not.
                $0->array.vsize = v;
        } }$
 
                $0->array.vsize = v;
        } }$
 
-###### Grammar
-       $*type
-       OptType -> Type ${ $0 = $<1; }$
-               | ${ $0 = NULL; }$
-
 ###### formal type grammar
 
 ###### formal type grammar
 
-       | [ IDENTIFIER :: OptType ] Type ${ {
-               struct variable *v = var_decl(c, $ID.txt);
-
-               v->type = $<OT;
-               v->constant = 1;
-               if (!v->type)
-                       v->type = Tnum;
-               $0 = add_anon_type(c, &array_prototype, "array[var]");
-               $0->array.member = $<6;
+       | [ ] Type ${ {
+               $0 = add_anon_type(c, &array_prototype, "array[]");
+               $0->array.member = $<Type;
                $0->array.size = 0;
                $0->array.unspec = 1;
                $0->array.size = 0;
                $0->array.unspec = 1;
-               $0->array.vsize = v;
+               $0->array.vsize = NULL;
        } }$
 
 ###### Binode types
        } }$
 
 ###### Binode types
-       Index,
+       Index, Length,
 
 ###### term grammar
 
 
 ###### term grammar
 
@@ -2586,6 +2573,13 @@ with a const size by whether they are prepared at parse time or not.
                $0 = b;
        } }$
 
                $0 = b;
        } }$
 
+       | Term [ ] ${ {
+               struct binode *b = new(binode);
+               b->op = Length;
+               b->left = $<Term;
+               $0 = b;
+       } }$
+
 ###### print binode cases
        case Index:
                print_exec(b->left, -1, bracket);
 ###### print binode cases
        case Index:
                print_exec(b->left, -1, bracket);
@@ -2594,13 +2588,18 @@ with a const size by whether they are prepared at parse time or not.
                printf("]");
                break;
 
                printf("]");
                break;
 
+       case Length:
+               print_exec(b->left, -1, bracket);
+               printf("[]");
+               break;
+
 ###### propagate binode cases
        case Index:
                /* left must be an array, right must be a number,
                 * result is the member type of the array
                 */
 ###### propagate binode cases
        case Index:
                /* left must be an array, right must be a number,
                 * result is the member type of the array
                 */
-               propagate_types(b->right, c, perr, Tnum, 0);
-               t = propagate_types(b->left, c, perr, NULL, rules & Rnoconstant);
+               propagate_types(b->right, c, perr_local, Tnum, 0);
+               t = propagate_types(b->left, c, perr, NULL, 0);
                if (!t || t->compat != array_compat) {
                        type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL);
                        return NULL;
                if (!t || t->compat != array_compat) {
                        type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL);
                        return NULL;
@@ -2613,6 +2612,20 @@ with a const size by whether they are prepared at parse time or not.
                }
                break;
 
                }
                break;
 
+       case Length:
+               /* left must be an array, result is a number
+                */
+               t = propagate_types(b->left, c, perr, NULL, 0);
+               if (!t || t->compat != array_compat) {
+                       type_err(c, "error: %1 cannot provide length", prog, t, 0, NULL);
+                       return NULL;
+               }
+               if (!type_compat(type, Tnum, rules))
+                       type_err(c, "error: have %1 but need %2", prog,
+                                        Tnum, rules, type);
+               return Tnum;
+               break;
+
 ###### interp binode cases
        case Index: {
                mpz_t q;
 ###### interp binode cases
        case Index: {
                mpz_t q;
@@ -2638,6 +2651,13 @@ with a const size by whether they are prepared at parse time or not.
                ltype = NULL;
                break;
        }
                ltype = NULL;
                break;
        }
+       case Length: {
+               lleft = linterp_exec(c, b->left, &ltype);
+               mpq_set_ui(rv.num, ltype->array.size, 1);
+               ltype = NULL;
+               rvtype = Tnum;
+               break;
+       }
 
 #### Structs
 
 
 #### Structs
 
@@ -2915,24 +2935,31 @@ function will be needed.
        }
 
 ###### top level grammar
        }
 
 ###### top level grammar
-       DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ {
-               struct type *t;
-               t = find_type(c, $ID.txt);
-               if (!t)
-                       t = add_type(c, $ID.txt, &structure_prototype);
-               else if (t->size >= 0) {
+       $*type
+       StructName -> IDENTIFIER ${ {
+               struct type *t = find_type(c, $ID.txt);
+
+               if (t && t->size >= 0) {
                        tok_err(c, "error: type already declared", &$ID);
                        tok_err(c, "info: this is location of declartion", &t->first_use);
                        tok_err(c, "error: type already declared", &$ID);
                        tok_err(c, "info: this is location of declartion", &t->first_use);
-                       /* Create a new one - duplicate */
-                       t = add_type(c, $ID.txt, &structure_prototype);
-               } else {
-                       struct type tmp = *t;
-                       *t = structure_prototype;
-                       t->name = tmp.name;
-                       t->next = tmp.next;
+                       t = NULL;
                }
                }
-               t->structure.field_list = $<FB;
+               if (!t)
+                       t = add_type(c, $ID.txt, NULL);
                t->first_use = $ID;
                t->first_use = $ID;
+               $0 = t;
+       } }$
+       $void
+       DeclareStruct -> struct StructName FieldBlock Newlines ${ {
+               struct type *t = $<SN;
+               struct type tmp = *t;
+
+               *t = structure_prototype;
+               t->name = tmp.name;
+               t->next = tmp.next;
+               t->first_use = tmp.first_use;
+
+               t->structure.field_list = $<FB;
        } }$
 
        $*fieldlist
        } }$
 
        $*fieldlist
@@ -3116,8 +3143,12 @@ anything in the heap or on the stack.  A reference can be assigned
                /* Nothing to do here */
        }
 
                /* Nothing to do here */
        }
 
-       static int reference_compat(struct type *require, struct type *have)
+       static int reference_compat(struct type *require, struct type *have,
+                                   enum val_rules rules)
        {
        {
+               if (rules & Rrefok)
+                       if (require->reference.referent == have)
+                               return 1;
                if (have->compat != require->compat)
                        return 0;
                if (have->reference.referent != require->reference.referent)
                if (have->compat != require->compat)
                        return 0;
                if (have->reference.referent != require->reference.referent)
@@ -3145,7 +3176,6 @@ anything in the heap or on the stack.  A reference can be assigned
                return Tnone;
        }
 
                return Tnone;
        }
 
-
        static struct type reference_prototype = {
                .print_type = reference_print_type,
                .cmp_eq = reference_cmp,
        static struct type reference_prototype = {
                .print_type = reference_print_type,
                .cmp_eq = reference_cmp,
@@ -3257,6 +3287,7 @@ anything in the heap or on the stack.  A reference can be assigned
                                r->reftype = type;
                                *perr |= Eretry;
                        }
                                r->reftype = type;
                                *perr |= Eretry;
                        }
+                       *perr |= Erval;
                        return type;
                case RefNil:
                        if (type && type->free != reference_free)
                        return type;
                case RefNil:
                        if (type && type->free != reference_free)
@@ -3266,9 +3297,10 @@ anything in the heap or on the stack.  A reference can be assigned
                                r->reftype = type;
                                *perr |= Eretry;
                        }
                                r->reftype = type;
                                *perr |= Eretry;
                        }
+                       *perr |= Erval;
                        return type;
                case RefFree:
                        return type;
                case RefFree:
-                       t = propagate_types(r->right, c, perr, NULL, 0);
+                       t = propagate_types(r->right, c, perr_local, NULL, 0);
                        if (t && t->free != reference_free)
                                type_err(c, "error: @free can only be assigned a reference, not %1",
                                         prog, t, 0, NULL);
                        if (t && t->free != reference_free)
                                type_err(c, "error: @free can only be assigned a reference, not %1",
                                         prog, t, 0, NULL);
@@ -3313,7 +3345,7 @@ anything in the heap or on the stack.  A reference can be assigned
 ###### Expressions: dereference
 
 ###### Binode types
 ###### Expressions: dereference
 
 ###### Binode types
-       Deref,
+       Deref, AddressOf,
 
 ###### term grammar
 
 
 ###### term grammar
 
@@ -3329,25 +3361,46 @@ anything in the heap or on the stack.  A reference can be assigned
                print_exec(b->left, -1, bracket);
                printf("@");
                break;
                print_exec(b->left, -1, bracket);
                printf("@");
                break;
+       case AddressOf:
+               print_exec(b->left, -1, bracket);
+               break;
 
 ###### propagate binode cases
        case Deref:
                /* left must be a reference, and we return what it refers to */
                /* FIXME how can I pass the expected type down? */
                t = propagate_types(b->left, c, perr, NULL, 0);
 
 ###### propagate binode cases
        case Deref:
                /* left must be a reference, and we return what it refers to */
                /* FIXME how can I pass the expected type down? */
                t = propagate_types(b->left, c, perr, NULL, 0);
+               *perr &= ~Erval;
                if (!t || t->free != reference_free)
                        type_err(c, "error: Cannot dereference %1", b, t, 0, NULL);
                else
                        return t->reference.referent;
                break;
 
                if (!t || t->free != reference_free)
                        type_err(c, "error: Cannot dereference %1", b, t, 0, NULL);
                else
                        return t->reference.referent;
                break;
 
+       case AddressOf:
+               /* left must be lval, we create reference to it */
+               if (!type || type->free != reference_free)
+                       t = propagate_types(b->left, c, perr, type, 0); // UNTESTED
+               else
+                       t = propagate_types(b->left, c, perr,
+                                           type->reference.referent, 0);
+               if (t)
+                       t = find_anon_type(c, &reference_prototype, "@%.*s",
+                                       t->name.len, t->name.txt);
+               return t;
+
 ###### interp binode cases
 ###### interp binode cases
-       case Deref: {
+       case Deref:
                left = interp_exec(c, b->left, &ltype);
                lrv = left.ref;
                rvtype = ltype->reference.referent;
                break;
                left = interp_exec(c, b->left, &ltype);
                lrv = left.ref;
                rvtype = ltype->reference.referent;
                break;
-       }
+
+       case AddressOf:
+               rv.ref = linterp_exec(c, b->left, &rvtype);
+               rvtype = find_anon_type(c, &reference_prototype, "@%.*s",
+                                       rvtype->name.len, rvtype->name.txt);
+               break;
 
 
 #### Functions
 
 
 #### Functions
@@ -3440,12 +3493,21 @@ further detailed when Expression Lists are introduced.
                val->function = NULL;
        }
 
                val->function = NULL;
        }
 
-       static int function_compat(struct type *require, struct type *have)
+       static int function_compat(struct type *require, struct type *have,
+                                  enum val_rules rules)
        {
                // FIXME can I do anything here yet?
                return 0;
        }
 
        {
                // FIXME can I do anything here yet?
                return 0;
        }
 
+       static struct exec *take_addr(struct exec *e)
+       {
+               struct binode *rv = new(binode);
+               rv->op = AddressOf;
+               rv->left = e;
+               return rv;
+       }
+
        static void function_check_args(struct parse_context *c, enum prop_err *perr,
                                        struct type *require, struct exec *args)
        {
        static void function_check_args(struct parse_context *c, enum prop_err *perr,
                                        struct type *require, struct exec *args)
        {
@@ -3457,13 +3519,22 @@ further detailed when Expression Lists are introduced.
 
                while (param) {
                        struct var *pv = cast(var, param->left);
 
                while (param) {
                        struct var *pv = cast(var, param->left);
+                       struct type *t = pv->var->type, *t2;
                        if (!arg) {
                                type_err(c, "error: insufficient arguments to function.",
                                         args, NULL, 0, NULL);
                                break;
                        }
                        *perr = 0;
                        if (!arg) {
                                type_err(c, "error: insufficient arguments to function.",
                                         args, NULL, 0, NULL);
                                break;
                        }
                        *perr = 0;
-                       propagate_types(arg->left, c, perr, pv->var->type, 0);
+                       t2 = propagate_types(arg->left, c, perr, t, Rrefok);
+                       if (t->free == reference_free &&
+                           t->reference.referent == t2 &&
+                           !(*perr & Erval)) {
+                               arg->left = take_addr(arg->left);
+                       } else if (!(*perr & Efail) && !type_compat(t2, t, 0)) {
+                               type_err(c, "error: cannot pass rval when reference expected",
+                                        arg->left, NULL, 0, NULL);
+                       }
                        param = cast(binode, param->right);
                        arg = cast(binode, arg->right);
                }
                        param = cast(binode, param->right);
                        arg = cast(binode, arg->right);
                }
@@ -3666,9 +3737,10 @@ it in the "SimpleStatement Grammar" which will be described later.
                        return NULL;
                }
                *perr |= Eruntime;
                        return NULL;
                }
                *perr |= Eruntime;
-               v->var->type->check_args(c, perr, v->var->type, args);
+               v->var->type->check_args(c, perr_local, v->var->type, args);
                if (v->var->type->function.inline_result)
                        *perr |= Emaycopy;
                if (v->var->type->function.inline_result)
                        *perr |= Emaycopy;
+               *perr |= Erval;
                return v->var->type->function.return_type;
        }
 
                return v->var->type->function.return_type;
        }
 
@@ -3781,7 +3853,7 @@ there.
                struct binode *b2 = cast(binode, b->right);
                struct type *t2;
 
                struct binode *b2 = cast(binode, b->right);
                struct type *t2;
 
-               propagate_types(b->left, c, perr, Tbool, 0);
+               propagate_types(b->left, c, perr_local, Tbool, 0);
                t = propagate_types(b2->left, c, perr, type, 0);
                t2 = propagate_types(b2->right, c, perr, type ?: t, 0);
                return t ?: t2;
                t = propagate_types(b2->left, c, perr, type, 0);
                t2 = propagate_types(b2->right, c, perr, type ?: t, 0);
                return t ?: t2;
@@ -3955,6 +4027,7 @@ evaluate the second expression if not necessary.
                if (type && type != Tbool)
                        type_err(c, "error: %1 operation found where %2 expected", prog,
                                   Tbool, 0, type);
                if (type && type != Tbool)
                        type_err(c, "error: %1 operation found where %2 expected", prog,
                                   Tbool, 0, type);
+               *perr |= Erval;
                return Tbool;
 
 ###### interp binode cases
                return Tbool;
 
 ###### interp binode cases
@@ -4076,6 +4149,7 @@ expression operator, and the `CMPop` non-terminal will match one of them.
                if (!type_compat(type, Tbool, 0))
                        type_err(c, "error: Comparison returns %1 but %2 expected", prog,
                                    Tbool, rules, type);
                if (!type_compat(type, Tbool, 0))
                        type_err(c, "error: Comparison returns %1 but %2 expected", prog,
                                    Tbool, rules, type);
+               *perr |= Erval;
                return Tbool;
 
 ###### interp binode cases
                return Tbool;
 
 ###### interp binode cases
@@ -4253,6 +4327,7 @@ parentheses around an expression converts it into a Term,
                if (!type_compat(type, Tnum, 0))
                        type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
                                   Tnum, rules, type);
                if (!type_compat(type, Tnum, 0))
                        type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
                                   Tnum, rules, type);
+               *perr |= Erval;
                return Tnum;
 
        case Concat:
                return Tnum;
 
        case Concat:
@@ -4262,6 +4337,7 @@ parentheses around an expression converts it into a Term,
                if (!type_compat(type, Tstr, 0))
                        type_err(c, "error: Concat returns %1 but %2 expected", prog,
                                   Tstr, rules, type);
                if (!type_compat(type, Tstr, 0))
                        type_err(c, "error: Concat returns %1 but %2 expected", prog,
                                   Tstr, rules, type);
+               *perr |= Erval;
                return Tstr;
 
        case StringConv:
                return Tstr;
 
        case StringConv:
@@ -4271,6 +4347,7 @@ parentheses around an expression converts it into a Term,
                        type_err(c,     // UNTESTED
                          "error: Can only convert string to number, not %1",
                                prog, type, 0, NULL);
                        type_err(c,     // UNTESTED
                          "error: Can only convert string to number, not %1",
                                prog, type, 0, NULL);
+               *perr |= Erval;
                return Tnum;
 
        case Test:
                return Tnum;
 
        case Test:
@@ -4279,6 +4356,7 @@ parentheses around an expression converts it into a Term,
                if (!t || !t->test)
                        type_err(c, "error: '?' requires a testable value, not %1",
                                 prog, t, 0, NULL);
                if (!t || !t->test)
                        type_err(c, "error: '?' requires a testable value, not %1",
                                 prog, t, 0, NULL);
+               *perr |= Erval;
                return Tbool;
 
        case Choose:
                return Tbool;
 
        case Choose:
@@ -4290,10 +4368,11 @@ parentheses around an expression converts it into a Term,
                if (t && t->test == NULL)
                        type_err(c, "error: \"??\" requires a testable value, not %1",
                                 prog, t, 0, NULL);
                if (t && t->test == NULL)
                        type_err(c, "error: \"??\" requires a testable value, not %1",
                                 prog, t, 0, NULL);
+               *perr |= Erval;
                return t;
 
        case Bracket:
                return t;
 
        case Bracket:
-               return propagate_types(b->right, c, perr, type, 0);
+               return propagate_types(b->right, c, perr, type, rules);
 
 ###### interp binode cases
 
 
 ###### interp binode cases
 
@@ -4663,7 +4742,7 @@ printed.
                else
                        b = cast(binode, b->right);
                while (b) {
                else
                        b = cast(binode, b->right);
                while (b) {
-                       propagate_types(b->left, c, perr, NULL, 0);
+                       propagate_types(b->left, c, perr_local, NULL, 0);
                        b = cast(binode, b->right);
                }
                break;
                        b = cast(binode, b->right);
                }
                break;
@@ -4699,8 +4778,8 @@ it is declared, it is assumed to be a global constant which are allowed to
 be declared at any time.
 
 ###### Binode types
 be declared at any time.
 
 ###### Binode types
-       Assign,
-       Declare,
+       Assign, AssignRef,
+       Declare, DeclareRef,
 
 ###### declare terminals
        $TERM =
 
 ###### declare terminals
        $TERM =
@@ -4736,6 +4815,7 @@ be declared at any time.
 ###### print binode cases
 
        case Assign:
 ###### print binode cases
 
        case Assign:
+       case AssignRef:
                do_indent(indent, "");
                print_exec(b->left, -1, bracket);
                printf(" = ");
                do_indent(indent, "");
                print_exec(b->left, -1, bracket);
                printf(" = ");
@@ -4745,6 +4825,7 @@ be declared at any time.
                break;
 
        case Declare:
                break;
 
        case Declare:
+       case DeclareRef:
                {
                struct variable *v = cast(var, b->left)->var;
                do_indent(indent, "");
                {
                struct variable *v = cast(var, b->left)->var;
                do_indent(indent, "");
@@ -4774,30 +4855,60 @@ be declared at any time.
 ###### propagate binode cases
 
        case Assign:
 ###### propagate binode cases
 
        case Assign:
+       case AssignRef:
        case Declare:
        case Declare:
-               /* Both must match and not be labels,
+       case DeclareRef:
+               /* Both must match, or left may be ref and right an lval
                 * Type must support 'dup',
                 * For Assign, left must not be constant.
                 * result is Tnone
                 */
                 * Type must support 'dup',
                 * For Assign, left must not be constant.
                 * result is Tnone
                 */
-               t = propagate_types(b->left, c, perr, NULL,
-                                   (b->op == Assign ? Rnoconstant : 0));
+               *perr &= ~(Erval | Econst);
+               t = propagate_types(b->left, c, perr, NULL, 0);
                if (!b->right)
                        return Tnone;
 
                if (t) {
                if (!b->right)
                        return Tnone;
 
                if (t) {
-                       if (propagate_types(b->right, c, perr, t, 0) != t)
-                               if (b->left->type == Xvar)
-                                       type_err(c, "info: variable '%v' was set as %1 here.",
-                                                cast(var, b->left)->var->where_set, t, rules, NULL);
+                       struct type *t2 = propagate_types(b->right, c, perr_local,
+                                                         t, Rrefok);
+                       if (!t2 || t2 == t || (*perr_local & Efail))
+                               ; // No more effort needed
+                       else if (t->free == reference_free &&
+                                t->reference.referent == t2 &&
+                                !(*perr_local & Erval)) {
+                               if (b->op == Assign)
+                                       b->op = AssignRef;
+                               if (b->op == Declare)
+                                       b->op = DeclareRef;
+                       }
+                       else if (t->free == reference_free &&
+                                t->reference.referent == t2 &&
+                                (*perr_local & Erval))
+                               type_err(c, "error: Cannot assign an rval to a reference.",
+                                        b, NULL, 0, NULL);
                } else {
                } else {
-                       t = propagate_types(b->right, c, perr, NULL, 0);
+                       t = propagate_types(b->right, c, perr_local, NULL, 0);
                        if (t)
                        if (t)
-                               propagate_types(b->left, c, perr, t,
-                                               (b->op == Assign ? Rnoconstant : 0));
+                               propagate_types(b->left, c, perr, t, 0);
                }
                }
-               if (t && t->dup == NULL && !(*perr & Emaycopy))
+               if (*perr & Erval)
+                       type_err(c, "error: cannot assign to an rval", b,
+                                NULL, 0, NULL);
+               else if ((b->op == Assign || b->op == AssignRef) && (*perr & Econst)) {
+                       type_err(c, "error: Cannot assign to a constant: %v",
+                                b->left, NULL, 0, NULL);
+                       if (b->left->type == Xvar) {
+                               struct var *var = cast(var, b->left);
+                               struct variable *v = var->var;
+                               type_err(c, "info: name was defined as a constant here",
+                                        v->where_decl, NULL, 0, NULL);
+                       }
+               }
+               if (t && t->dup == NULL && !(*perr_local & Emaycopy))
                        type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL);
                        type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL);
+               if (b->left->type == Xvar && (*perr_local & Efail))
+                       type_err(c, "info: variable '%v' was set as %1 here.",
+                                cast(var, b->left)->var->where_set, t, rules, NULL);
                return Tnone;
 
                break;
                return Tnone;
 
                break;
@@ -4805,13 +4916,21 @@ be declared at any time.
 ###### interp binode cases
 
        case Assign:
 ###### interp binode cases
 
        case Assign:
+       case AssignRef:
                lleft = linterp_exec(c, b->left, &ltype);
                lleft = linterp_exec(c, b->left, &ltype);
-               if (lleft)
+               if (!lleft)
+                       // FIXME lleft==NULL probably means illegal array ref
+                       // should that cause a runtime error
+                       ;
+               else if (b->op == AssignRef)
+                       lleft->ref = linterp_exec(c, b->right, &rtype);
+               else
                        dinterp_exec(c, b->right, lleft, ltype, 1);
                ltype = Tnone;
                break;
 
        case Declare:
                        dinterp_exec(c, b->right, lleft, ltype, 1);
                ltype = Tnone;
                break;
 
        case Declare:
+       case DeclareRef:
        {
                struct variable *v = cast(var, b->left)->var;
                struct value *val;
        {
                struct variable *v = cast(var, b->left)->var;
                struct value *val;
@@ -4819,10 +4938,12 @@ be declared at any time.
                val = var_value(c, v);
                if (v->type->prepare_type)
                        v->type->prepare_type(c, v->type, 0);
                val = var_value(c, v);
                if (v->type->prepare_type)
                        v->type->prepare_type(c, v->type, 0);
-               if (b->right)
-                       dinterp_exec(c, b->right, val, v->type, 0);
-               else
+               if (!b->right)
                        val_init(v->type, val);
                        val_init(v->type, val);
+               else if (b->op == DeclareRef)
+                       val->ref = linterp_exec(c, b->right, &rtype);
+               else
+                       dinterp_exec(c, b->right, val, v->type, 0);
                break;
        }
 
                break;
        }
 
@@ -5246,7 +5367,7 @@ casepart` to track a list of case parts.
 
 ###### propagate binode cases
        case Loop:
 
 ###### propagate binode cases
        case Loop:
-               t = propagate_types(b->right, c, perr, Tnone, 0);
+               t = propagate_types(b->right, c, perr_local, Tnone, 0);
                if (!type_compat(Tnone, t, 0))
                        *perr |= Efail; // UNTESTED
                return propagate_types(b->left, c, perr, type, rules);
                if (!type_compat(Tnone, t, 0))
                        *perr |= Efail; // UNTESTED
                return propagate_types(b->left, c, perr, type, rules);
@@ -5632,8 +5753,8 @@ is a bit more interesting at this level.
                        struct value fn = {.function = code};
                        struct type *t;
                        var_block_close(c, CloseFunction, code);
                        struct value fn = {.function = code};
                        struct type *t;
                        var_block_close(c, CloseFunction, code);
-                       t = add_anon_type(c, &function_prototype, 
-                                         "func %.*s", name->name->name.len, 
+                       t = add_anon_type(c, &function_prototype,
+                                         "func %.*s", name->name->name.len,
                                          name->name->name.txt);
                        name->type = t;
                        t->function.params = reorder_bilist(args);
                                          name->name->name.txt);
                        name->type = t;
                        t->function.params = reorder_bilist(args);
@@ -5745,7 +5866,7 @@ is a bit more interesting at this level.
                                all_ok = 0;
                        if (!v->type->function.inline_result &&
                            !v->type->function.return_type->dup) {
                                all_ok = 0;
                        if (!v->type->function.inline_result &&
                            !v->type->function.return_type->dup) {
-                               type_err(c, "error: function cannot return value of type %1", 
+                               type_err(c, "error: function cannot return value of type %1",
                                         v->where_decl, v->type->function.return_type, 0, NULL);
                        }
 
                                         v->where_decl, v->type->function.return_type, 0, NULL);
                        }
 
@@ -5814,15 +5935,12 @@ is a bit more interesting at this level.
                        struct value *vl = var_value(c, v->var);
                        struct value arg;
                        struct type *t;
                        struct value *vl = var_value(c, v->var);
                        struct value arg;
                        struct type *t;
-                       mpq_t argcq;
                        int i;
 
                        switch (anum++) {
                        case 0: /* argv */
                                t = v->var->type;
                        int i;
 
                        switch (anum++) {
                        case 0: /* argv */
                                t = v->var->type;
-                               mpq_init(argcq);
-                               mpq_set_ui(argcq, argc, 1);
-                               memcpy(var_value(c, t->array.vsize), &argcq, sizeof(argcq));
+                               t->array.size = argc;
                                t->prepare_type(c, t, 0);
                                array_init(v->var->type, vl);
                                for (i = 0; i < argc; i++) {
                                t->prepare_type(c, t, 0);
                                array_init(v->var->type, vl);
                                for (i = 0; i < argc; i++) {
@@ -5876,7 +5994,7 @@ things which will likely grow as the languages grows.
                name:string
                alive:Boolean
 
                name:string
                alive:Boolean
 
-       func main(argv:[argc::]string)
+       func main(argv:[]string)
                print "Hello World, what lovely oceans you have!"
                print "Are there", five, "?"
                print pi, pie, "but", cake
                print "Hello World, what lovely oceans you have!"
                print "Are there", five, "?"
                print pi, pie, "but", cake