]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: clean up interp_prog()
[ocean] / csrc / oceani.mdc
index 520212be6d144365a586638bf695098689460058..b83f6bca4e5a3683d1166281cca7c2852e4dfd9b 100644 (file)
@@ -105,6 +105,7 @@ structures can be used.
 
 ###### Parser: header
        ## macros
 
 ###### Parser: header
        ## macros
+       struct parse_context;
        ## ast
        struct parse_context {
                struct token_config config;
        ## ast
        struct parse_context {
                struct token_config config;
@@ -242,7 +243,7 @@ structures can be used.
                                fprintf(stderr, "oceani: type error in program - not running.\n");
                                exit(1);
                        }
                                fprintf(stderr, "oceani: type error in program - not running.\n");
                                exit(1);
                        }
-                       interp_prog(context.prog, argv+optind+1);
+                       interp_prog(&context, context.prog, argv+optind+1);
                }
                free_exec(context.prog);
 
                }
                free_exec(context.prog);
 
@@ -287,14 +288,6 @@ consistent across all the branches.  When the variable is not used
 outside the if, the variables in the different branches are distinct
 and can be of different types.
 
 outside the if, the variables in the different branches are distinct
 and can be of different types.
 
-Determining the types of all variables early is important for
-processing command line arguments.  These can be assigned to any of
-several types of variable, but we must first know the correct type so
-any required conversion can happen.  If a variable is associated with
-a command line argument but no type can be interpreted (e.g. the
-variable is only ever used in a `print` statement), then the type is
-set to 'string'.
-
 Undeclared names may only appear in "use" statements and "case" expressions.
 These names are given a type of "label" and a unique value.
 This allows them to fill the role of a name in an enumerated type, which
 Undeclared names may only appear in "use" statements and "case" expressions.
 These names are given a type of "label" and a unique value.
 This allows them to fill the role of a name in an enumerated type, which
@@ -407,11 +400,11 @@ various entities.
 ### Types
 
 Values come in a wide range of types, with more likely to be added.
 ### Types
 
 Values come in a wide range of types, with more likely to be added.
-Each type needs to be able to parse and print its own values (for
-convenience at least) as well as to compare two values, at least for
-equality and possibly for order.  For now, values might need to be
-duplicated and freed, though eventually such manipulations will be
-better integrated into the language.
+Each type needs to be able to print its own values (for convenience at
+least) as well as to compare two values, at least for equality and
+possibly for order.  For now, values might need to be duplicated and
+freed, though eventually such manipulations will be better integrated
+into the language.
 
 Rather than requiring every numeric type to support all numeric
 operations (add, multiple, etc), we allow types to be able to present
 
 Rather than requiring every numeric type to support all numeric
 operations (add, multiple, etc), we allow types to be able to present
@@ -437,7 +430,7 @@ Named type are stored in a simple linked list.  Objects of each type are
                struct type *next;
                int size, align;
                void (*init)(struct type *type, struct value *val);
                struct type *next;
                int size, align;
                void (*init)(struct type *type, struct value *val);
-               int (*parse)(struct type *type, char *str, struct value *val);
+               void (*prepare_type)(struct parse_context *c, struct type *type, int parse_time);
                void (*print)(struct type *type, struct value *val);
                void (*print_type)(struct type *type, FILE *f);
                int (*cmp_order)(struct type *t1, struct type *t2,
                void (*print)(struct type *type, struct value *val);
                void (*print_type)(struct type *type, FILE *f);
                int (*cmp_order)(struct type *t1, struct type *t2,
@@ -541,20 +534,14 @@ Named type are stored in a simple linked list.  Objects of each type are
                        printf("*Unknown*");            // NOTEST
        }
 
                        printf("*Unknown*");            // NOTEST
        }
 
-       static int parse_value(struct type *type, char *arg, 
-                              struct value *val)
-       {
-               if (type && type->parse)
-                       return type->parse(type, arg, val);
-               return 0;                               // NOTEST
-       }
-
-       static struct value *val_alloc(struct type *t, struct value *init)
+       static struct value *val_alloc(struct parse_context *c, struct type *t,
+                                      struct value *init)
        {
                struct value *ret;
 
        {
                struct value *ret;
 
-               if (!t->size)
-                       val_init(t, NULL);
+               if (t->prepare_type)
+                       t->prepare_type(c, t, 0);
+
                ret = calloc(1, t->size);
                if (init)
                        memcpy(ret, init, t->size);
                ret = calloc(1, t->size);
                if (init)
                        memcpy(ret, init, t->size);
@@ -574,7 +561,6 @@ Named type are stored in a simple linked list.  Objects of each type are
        static int value_cmp(struct type *tl, struct type *tr,
                             struct value *left, struct value *right);
        static void print_value(struct type *type, struct value *v);
        static int value_cmp(struct type *tl, struct type *tr,
                             struct value *left, struct value *right);
        static void print_value(struct type *type, struct value *v);
-       static int parse_value(struct type *type, char *arg, struct value *val);
 
 ###### free context types
 
 
 ###### free context types
 
@@ -627,9 +613,6 @@ A separate function encoding these cases will simplify some code later.
                return require == have;
        }
 
                return require == have;
        }
 
-When assigning command line arguments to variables, we need to be able
-to parse each type from a string.
-
 ###### includes
        #include <gmp.h>
        #include "parse_string.h"
 ###### includes
        #include <gmp.h>
        #include "parse_string.h"
@@ -747,57 +730,10 @@ to parse each type from a string.
                }
        }
 
                }
        }
 
-       static int _parse_value(struct type *type, char *arg, struct value *val)
-       {
-               struct text tx;
-               int neg = 0;
-               char tail[3] = "";
-
-               switch(type->vtype) {
-               case Vlabel:                            // NOTEST
-               case Vnone:                             // NOTEST
-                       return 0;                       // NOTEST
-               case Vstr:
-                       val->str.len = strlen(arg);
-                       val->str.txt = malloc(val->str.len);
-                       memcpy(val->str.txt, arg, val->str.len);
-                       break;
-               case Vnum:
-                       if (*arg == '-') {
-                               neg = 1;
-                               arg++;
-                       }
-                       tx.txt = arg; tx.len = strlen(tx.txt);
-                       if (number_parse(val->num, tail, tx) == 0)
-                               mpq_init(val->num);
-                       else if (neg)
-                               mpq_neg(val->num, val->num);
-                       if (tail[0]) {
-                               printf("Unsupported suffix: %s\n", arg);
-                               return 0;
-                       }
-                       break;
-               case Vbool:
-                       if (strcasecmp(arg, "true") == 0 ||
-                           strcmp(arg, "1") == 0)
-                               val->bool = 1;
-                       else if (strcasecmp(arg, "false") == 0 ||
-                                strcmp(arg, "0") == 0)
-                               val->bool = 0;
-                       else {
-                               printf("Bad bool: %s\n", arg);
-                               return 0;
-                       }
-                       break;
-               }
-               return 1;
-       }
-
        static void _free_value(struct type *type, struct value *v);
 
        static struct type base_prototype = {
                .init = _val_init,
        static void _free_value(struct type *type, struct value *v);
 
        static struct type base_prototype = {
                .init = _val_init,
-               .parse = _parse_value,
                .print = _print_value,
                .cmp_order = _value_cmp,
                .cmp_eq = _value_cmp,
                .print = _print_value,
                .cmp_order = _value_cmp,
                .cmp_eq = _value_cmp,
@@ -1487,11 +1423,12 @@ in `rval`.
                struct value rval, *lval;
        };
 
                struct value rval, *lval;
        };
 
-       static struct lrval _interp_exec(struct exec *e);
+       static struct lrval _interp_exec(struct parse_context *c, struct exec *e);
 
 
-       static struct value interp_exec(struct exec *e, struct type **typeret)
+       static struct value interp_exec(struct parse_context *c, struct exec *e,
+                                       struct type **typeret)
        {
        {
-               struct lrval ret = _interp_exec(e);
+               struct lrval ret = _interp_exec(c, e);
 
                if (!ret.type) abort();
                if (typeret)
 
                if (!ret.type) abort();
                if (typeret)
@@ -1501,16 +1438,19 @@ in `rval`.
                return ret.rval;
        }
 
                return ret.rval;
        }
 
-       static struct value *linterp_exec(struct exec *e, struct type **typeret)
+       static struct value *linterp_exec(struct parse_context *c, struct exec *e,
+                                         struct type **typeret)
        {
        {
-               struct lrval ret = _interp_exec(e);
+               struct lrval ret = _interp_exec(c, e);
 
 
-               if (typeret)
+               if (ret.lval)
                        *typeret = ret.type;
                        *typeret = ret.type;
+               else
+                       free_value(ret.type, &ret.rval);
                return ret.lval;
        }
 
                return ret.lval;
        }
 
-       static struct lrval _interp_exec(struct exec *e)
+       static struct lrval _interp_exec(struct parse_context *c, struct exec *e)
        {
                struct lrval ret;
                struct value rv = {}, *lrv = NULL;
        {
                struct lrval ret;
                struct value rv = {}, *lrv = NULL;
@@ -1553,10 +1493,6 @@ different phases of parse, analyse, print, interpret.
 
 Thus far we have arrays and structs.
 
 
 Thus far we have arrays and structs.
 
-Some complex types need do not exist in a name table, so they are kept
-on a linked list in the context (`anon_typelist`).  This allows them to
-be freed when parsing is complete.
-
 #### Arrays
 
 Arrays can be declared by giving a size and a type, as `[size]type' so
 #### Arrays
 
 Arrays can be declared by giving a size and a type, as `[size]type' so
@@ -1571,39 +1507,67 @@ ever be referenced by the name it is declared with.  It is likely that
 a "`copy`" primitive will eventually be define which can be used to
 make a copy of an array with controllable recursive depth.
 
 a "`copy`" primitive will eventually be define which can be used to
 make a copy of an array with controllable recursive depth.
 
+For now we have two sorts of array, those with fixed size either because
+it is given as a literal number or because it is a struct member (which
+cannot have a runtime-changing size), and those with a size that is
+determined at runtime - local variables with a const size.  The former
+have their size calculated at parse time, the latter at run time.
+
+For the latter type, the `size` field of the type is the size of a
+pointer, and the array is reallocated every time it comes into scope.
+
+We differentiate struct fields with a const size from local variables
+with a const size by whether they are prepared at parse time or not.
+
 ###### type union fields
 
        struct {
 ###### type union fields
 
        struct {
-               int size;
+               short size;
+               short static_size;
                struct variable *vsize;
                struct type *member;
        } array;
 
 ###### value union fields
                struct variable *vsize;
                struct type *member;
        } array;
 
 ###### value union fields
-       void *array;
+       void *array;  // used if not static_size
 
 ###### value functions
 
 
 ###### value functions
 
-       static void array_init(struct type *type, struct value *val)
+       static void array_prepare_type(struct parse_context *c, struct type *type,
+                                      int parse_time)
        {
        {
-               int i;
+               mpz_t q;
+               if (!type->array.vsize || type->array.static_size)
+                       return;
 
 
-               if (type->array.vsize) {
-                       mpz_t q;
-                       mpz_init(q);
-                       mpz_tdiv_q(q, mpq_numref(type->array.vsize->val->num),
-                                  mpq_denref(type->array.vsize->val->num));
-                       type->array.size = mpz_get_si(q);
-                       mpz_clear(q);
+               mpz_init(q);
+               mpz_tdiv_q(q, mpq_numref(type->array.vsize->val->num),
+                          mpq_denref(type->array.vsize->val->num));
+               type->array.size = mpz_get_si(q);
+               mpz_clear(q);
+
+               if (parse_time) {
+                       type->array.static_size = 1;
+                       type->size = type->array.size * type->array.member->size;
+                       type->align = type->array.member->align;
                }
                }
-               type->size = type->array.size * type->array.member->size;
-               type->align = type->array.member->align;
+       }
+
+       static void array_init(struct type *type, struct value *val)
+       {
+               int i;
+               void *ptr = val->ptr;
 
                if (!val)
 
                if (!val)
-                       return;         
+                       return;
+               if (!type->array.static_size) {
+                       val->array = calloc(type->array.size, 
+                                           type->array.member->size);
+                       ptr = val->array;
+               }
                for (i = 0; i < type->array.size; i++) {
                        struct value *v;
                for (i = 0; i < type->array.size; i++) {
                        struct value *v;
-                       v = (void*)val->ptr + i * type->array.member->size;
+                       v = (void*)ptr + i * type->array.member->size;
                        val_init(type->array.member, v);
                }
        }
                        val_init(type->array.member, v);
                }
        }
@@ -1611,12 +1575,17 @@ make a copy of an array with controllable recursive depth.
        static void array_free(struct type *type, struct value *val)
        {
                int i;
        static void array_free(struct type *type, struct value *val)
        {
                int i;
+               void *ptr = val->ptr;
 
 
+               if (!type->array.static_size)
+                       ptr = val->array;
                for (i = 0; i < type->array.size; i++) {
                        struct value *v;
                for (i = 0; i < type->array.size; i++) {
                        struct value *v;
-                       v = (void*)val->ptr + i * type->array.member->size;
+                       v = (void*)ptr + i * type->array.member->size;
                        free_value(type->array.member, v);
                }
                        free_value(type->array.member, v);
                }
+               if (!type->array.static_size)
+                       free(ptr);
        }
 
        static int array_compat(struct type *require, struct type *have)
        }
 
        static int array_compat(struct type *require, struct type *have)
@@ -1645,27 +1614,34 @@ make a copy of an array with controllable recursive depth.
 
        static struct type array_prototype = {
                .init = array_init,
 
        static struct type array_prototype = {
                .init = array_init,
+               .prepare_type = array_prepare_type,
                .print_type = array_print_type,
                .compat = array_compat,
                .free = array_free,
                .print_type = array_print_type,
                .compat = array_compat,
                .free = array_free,
+               .size = sizeof(void*),
+               .align = sizeof(void*),
        };
 
        };
 
+###### declare terminals
+       $TERM [ ]
+
 ###### type grammar
 
 ###### type grammar
 
-       | [ NUMBER ] Type ${
-               $0 = calloc(1, sizeof(struct type));
-               *($0) = array_prototype;
-               $0->array.member = $<4;
-               $0->array.vsize = NULL;
-               {
+       | [ NUMBER ] Type ${ {
                char tail[3];
                mpq_t num;
                char tail[3];
                mpq_t num;
+               struct text noname = { "", 0 };
+               struct type *t;
+
+               $0 = t = add_type(c, noname, &array_prototype);
+               t->array.member = $<4;
+               t->array.vsize = NULL;
                if (number_parse(num, tail, $2.txt) == 0)
                        tok_err(c, "error: unrecognised number", &$2);
                else if (tail[0])
                        tok_err(c, "error: unsupported number suffix", &$2);
                else {
                if (number_parse(num, tail, $2.txt) == 0)
                        tok_err(c, "error: unrecognised number", &$2);
                else if (tail[0])
                        tok_err(c, "error: unsupported number suffix", &$2);
                else {
-                       $0->array.size = mpz_get_ui(mpq_numref(num));
+                       t->array.size = mpz_get_ui(mpq_numref(num));
                        if (mpz_cmp_ui(mpq_denref(num), 1) != 0) {
                                tok_err(c, "error: array size must be an integer",
                                        &$2);
                        if (mpz_cmp_ui(mpq_denref(num), 1) != 0) {
                                tok_err(c, "error: array size must be an integer",
                                        &$2);
@@ -1674,41 +1650,26 @@ make a copy of an array with controllable recursive depth.
                                        &$2);
                        mpq_clear(num);
                }
                                        &$2);
                        mpq_clear(num);
                }
-               $0->next = c->anon_typelist;
-               c->anon_typelist = $0;
-               }
-       }$
+               t->array.static_size = 1;
+               t->size = t->array.size * t->array.member->size;
+               t->align = t->array.member->align;
+       } }$
 
        | [ IDENTIFIER ] Type ${ {
                struct variable *v = var_ref(c, $2.txt);
 
        | [ IDENTIFIER ] Type ${ {
                struct variable *v = var_ref(c, $2.txt);
+               struct text noname = { "", 0 };
 
                if (!v)
                        tok_err(c, "error: name undeclared", &$2);
                else if (!v->constant)
                        tok_err(c, "error: array size must be a constant", &$2);
 
 
                if (!v)
                        tok_err(c, "error: name undeclared", &$2);
                else if (!v->constant)
                        tok_err(c, "error: array size must be a constant", &$2);
 
-               $0 = calloc(1, sizeof(struct type));
-               *($0) = array_prototype;
+               $0 = add_type(c, noname, &array_prototype);
                $0->array.member = $<4;
                $0->array.size = 0;
                $0->array.vsize = v;
                $0->array.member = $<4;
                $0->array.size = 0;
                $0->array.vsize = v;
-               $0->next = c->anon_typelist;
-               c->anon_typelist = $0;
        } }$
 
        } }$
 
-###### parse context
-
-       struct type *anon_typelist;
-
-###### free context types
-
-       while (context.anon_typelist) {
-               struct type *t = context.anon_typelist;
-
-               context.anon_typelist = t->next;
-               free(t);
-       }
-
 ###### Binode types
        Index,
 
 ###### Binode types
        Index,
 
@@ -1753,17 +1714,22 @@ make a copy of an array with controllable recursive depth.
        case Index: {
                mpz_t q;
                long i;
        case Index: {
                mpz_t q;
                long i;
+               void *ptr;
 
 
-               lleft = linterp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               lleft = linterp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                mpz_init(q);
                mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
                i = mpz_get_si(q);
                mpz_clear(q);
 
                mpz_init(q);
                mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
                i = mpz_get_si(q);
                mpz_clear(q);
 
+               if (ltype->array.static_size)
+                       ptr = lleft;
+               else
+                       ptr = *(void**)lleft;
                rvtype = ltype->array.member;
                if (i >= 0 && i < ltype->array.size)
                rvtype = ltype->array.member;
                if (i >= 0 && i < ltype->array.size)
-                       lrv = (void*)lleft + i * rvtype->size;
+                       lrv = ptr + i * rvtype->size;
                else
                        val_init(ltype->array.member, &rv);
                ltype = NULL;
                else
                        val_init(ltype->array.member, &rv);
                ltype = NULL;
@@ -1835,7 +1801,12 @@ function will be needed.
                for (i = 0; i < type->structure.nfields; i++) {
                        struct value *v;
                        v = (void*) val->ptr + type->structure.fields[i].offset;
                for (i = 0; i < type->structure.nfields; i++) {
                        struct value *v;
                        v = (void*) val->ptr + type->structure.fields[i].offset;
-                       val_init(type->structure.fields[i].type, v);
+                       if (type->structure.fields[i].init)
+                               dup_value(type->structure.fields[i].type, 
+                                         type->structure.fields[i].init,
+                                         v);
+                       else
+                               val_init(type->structure.fields[i].type, v);
                }
        }
 
                }
        }
 
@@ -1886,6 +1857,9 @@ function will be needed.
                free(e);
                break;
 
                free(e);
                break;
 
+###### declare terminals
+       $TERM struct .
+
 ###### variable grammar
 
        | Variable . IDENTIFIER ${ {
 ###### variable grammar
 
        | Variable . IDENTIFIER ${ {
@@ -1950,7 +1924,7 @@ function will be needed.
        {
                struct fieldref *f = cast(fieldref, e);
                struct type *ltype;
        {
                struct fieldref *f = cast(fieldref, e);
                struct type *ltype;
-               struct value *lleft = linterp_exec(f->left, &ltype);
+               struct value *lleft = linterp_exec(c, f->left, &ltype);
                lrv = (void*)lleft->ptr + ltype->structure.fields[f->index].offset;
                rvtype = ltype->structure.fields[f->index].type;
                break;
                lrv = (void*)lleft->ptr + ltype->structure.fields[f->index].offset;
                rvtype = ltype->structure.fields[f->index].type;
                break;
@@ -2039,15 +2013,16 @@ function will be needed.
                        if (!ok)
                                c->parse_error = 1;
                        else {
                        if (!ok)
                                c->parse_error = 1;
                        else {
-                               struct value vl = interp_exec($5, NULL);
-                               $0->f.init = val_alloc($0->f.type, &vl);
+                               struct value vl = interp_exec(c, $5, NULL);
+                               $0->f.init = val_alloc(c, $0->f.type, &vl);
                        }
                } }$
                | IDENTIFIER : Type ${
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
                        $0->f.type = $<3;
                        }
                } }$
                | IDENTIFIER : Type ${
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
                        $0->f.type = $<3;
-                       $0->f.init = val_alloc($0->f.type, NULL);
+                       if ($0->f.type->prepare_type)
+                               $0->f.type->prepare_type(c, $0->f.type, 1);
                }$
 
 ###### forward decls
                }$
 
 ###### forward decls
@@ -2132,6 +2107,8 @@ an executable.
 
 ###### Grammar
 
 
 ###### Grammar
 
+       $TERM True False
+
        $*val
        Value ->  True ${
                        $0 = new_val(Tbool, $1);
        $*val
        Value ->  True ${
                        $0 = new_val(Tbool, $1);
@@ -2253,6 +2230,8 @@ link to find the primary instance.
 
 ###### Grammar
 
 
 ###### Grammar
 
+       $TERM : ::
+
        $*var
        VariableDecl -> IDENTIFIER : ${ {
                struct variable *v = var_decl(c, $1.txt);
        $*var
        VariableDecl -> IDENTIFIER : ${ {
                struct variable *v = var_decl(c, $1.txt);
@@ -2509,11 +2488,11 @@ there.
 
        case CondExpr: {
                struct binode *b2 = cast(binode, b->right);
 
        case CondExpr: {
                struct binode *b2 = cast(binode, b->right);
-               left = interp_exec(b->left, &ltype);
+               left = interp_exec(c, b->left, &ltype);
                if (left.bool)
                if (left.bool)
-                       rv = interp_exec(b2->left, &rvtype);
+                       rv = interp_exec(c, b2->left, &rvtype);
                else
                else
-                       rv = interp_exec(b2->right, &rvtype);
+                       rv = interp_exec(c, b2->right, &rvtype);
                }
                break;
 
                }
                break;
 
@@ -2626,27 +2605,27 @@ evaluate the second expression if not necessary.
 
 ###### interp binode cases
        case And:
 
 ###### interp binode cases
        case And:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                rv.bool = rv.bool && right.bool;
                break;
        case AndThen:
                rv.bool = rv.bool && right.bool;
                break;
        case AndThen:
-               rv = interp_exec(b->left, &rvtype);
+               rv = interp_exec(c, b->left, &rvtype);
                if (rv.bool)
                if (rv.bool)
-                       rv = interp_exec(b->right, NULL);
+                       rv = interp_exec(c, b->right, NULL);
                break;
        case Or:
                break;
        case Or:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                rv.bool = rv.bool || right.bool;
                break;
        case OrElse:
                rv.bool = rv.bool || right.bool;
                break;
        case OrElse:
-               rv = interp_exec(b->left, &rvtype);
+               rv = interp_exec(c, b->left, &rvtype);
                if (!rv.bool)
                if (!rv.bool)
-                       rv = interp_exec(b->right, NULL);
+                       rv = interp_exec(c, b->right, NULL);
                break;
        case Not:
                break;
        case Not:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                rv.bool = !rv.bool;
                break;
 
                rv.bool = !rv.bool;
                break;
 
@@ -2754,8 +2733,8 @@ expression operator, and the `CMPop` non-terminal will match one of them.
        case NEql:
        {
                int cmp;
        case NEql:
        {
                int cmp;
-               left = interp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               left = interp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                cmp = value_cmp(ltype, rtype, &left, &right);
                rvtype = Tbool;
                switch (b->op) {
                cmp = value_cmp(ltype, rtype, &left, &right);
                rvtype = Tbool;
                switch (b->op) {
@@ -2772,9 +2751,14 @@ expression operator, and the `CMPop` non-terminal will match one of them.
 
 ### Expressions: The rest
 
 
 ### Expressions: The rest
 
-The remaining expressions with the highest precedence are arithmetic and
-string concatenation.  String concatenation (`++`) has the same
-precedence as multiplication and division, but lower than the uniary.
+The remaining expressions with the highest precedence are arithmetic,
+string concatenation, and string conversion.  String concatenation
+(`++`) has the same precedence as multiplication and division, but lower
+than the uniary.
+
+String conversion is a temporary feature until I get a better type
+system.  `$` is a prefix operator which expects a string and returns
+a number.
 
 `+` and `-` are both infix and prefix operations (where they are
 absolute value and negation).  These have different operator names.
 
 `+` and `-` are both infix and prefix operations (where they are
 absolute value and negation).  These have different operator names.
@@ -2788,12 +2772,13 @@ should only insert brackets were needed for precedence.
        Times, Divide, Rem,
        Concat,
        Absolute, Negate,
        Times, Divide, Rem,
        Concat,
        Absolute, Negate,
+       StringConv,
        Bracket,
 
 ###### expr precedence
        $LEFT + - Eop
        $LEFT * / % ++ Top
        Bracket,
 
 ###### expr precedence
        $LEFT + - Eop
        $LEFT * / % ++ Top
-       $LEFT Uop
+       $LEFT Uop $
        $TERM ( )
 
 ###### expression grammar
        $TERM ( )
 
 ###### expression grammar
@@ -2834,6 +2819,7 @@ should only insert brackets were needed for precedence.
 
        Uop ->    + ${ $0.op = Absolute; }$
                | - ${ $0.op = Negate; }$
 
        Uop ->    + ${ $0.op = Absolute; }$
                | - ${ $0.op = Negate; }$
+               | $ ${ $0.op = StringConv; }$
 
        Top ->    * ${ $0.op = Times; }$
                | / ${ $0.op = Divide; }$
 
        Top ->    * ${ $0.op = Times; }$
                | / ${ $0.op = Divide; }$
@@ -2862,14 +2848,15 @@ should only insert brackets were needed for precedence.
                if (bracket) printf(")");
                break;
        case Absolute:
                if (bracket) printf(")");
                break;
        case Absolute:
-               if (bracket) printf("(");
-               printf("+");
-               print_exec(b->right, indent, bracket);
-               if (bracket) printf(")");
-               break;
        case Negate:
        case Negate:
+       case StringConv:
                if (bracket) printf("(");
                if (bracket) printf("(");
-               printf("-");
+               switch (b->op) {
+               case Absolute:   fputs("+", stdout); break;
+               case Negate:     fputs("-", stdout); break;
+               case StringConv: fputs("$", stdout); break;
+               default: abort();       // NOTEST
+               }                       // NOTEST
                print_exec(b->right, indent, bracket);
                if (bracket) printf(")");
                break;
                print_exec(b->right, indent, bracket);
                if (bracket) printf(")");
                break;
@@ -2906,36 +2893,45 @@ should only insert brackets were needed for precedence.
                                   Tstr, rules, type);
                return Tstr;
 
                                   Tstr, rules, type);
                return Tstr;
 
+       case StringConv:
+               /* op must be string, result is number */
+               propagate_types(b->left, c, ok, Tstr, 0);
+               if (!type_compat(type, Tnum, 0))
+                       type_err(c,
+                         "error: Can only convert string to number, not %1",
+                               prog, type, 0, NULL);
+               return Tnum;
+
        case Bracket:
                return propagate_types(b->right, c, ok, type, 0);
 
 ###### interp binode cases
 
        case Plus:
        case Bracket:
                return propagate_types(b->right, c, ok, type, 0);
 
 ###### interp binode cases
 
        case Plus:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                mpq_add(rv.num, rv.num, right.num);
                break;
        case Minus:
                mpq_add(rv.num, rv.num, right.num);
                break;
        case Minus:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                mpq_sub(rv.num, rv.num, right.num);
                break;
        case Times:
                mpq_sub(rv.num, rv.num, right.num);
                break;
        case Times:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                mpq_mul(rv.num, rv.num, right.num);
                break;
        case Divide:
                mpq_mul(rv.num, rv.num, right.num);
                break;
        case Divide:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                mpq_div(rv.num, rv.num, right.num);
                break;
        case Rem: {
                mpz_t l, r, rem;
 
                mpq_div(rv.num, rv.num, right.num);
                break;
        case Rem: {
                mpz_t l, r, rem;
 
-               left = interp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               left = interp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                mpz_init(l); mpz_init(r); mpz_init(rem);
                mpz_tdiv_q(l, mpq_numref(left.num), mpq_denref(left.num));
                mpz_tdiv_q(r, mpq_numref(right.num), mpq_denref(right.num));
                mpz_init(l); mpz_init(r); mpz_init(rem);
                mpz_tdiv_q(l, mpq_numref(left.num), mpq_denref(left.num));
                mpz_tdiv_q(r, mpq_numref(right.num), mpq_denref(right.num));
@@ -2947,22 +2943,43 @@ should only insert brackets were needed for precedence.
                break;
        }
        case Negate:
                break;
        }
        case Negate:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                mpq_neg(rv.num, rv.num);
                break;
        case Absolute:
                mpq_neg(rv.num, rv.num);
                break;
        case Absolute:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                mpq_abs(rv.num, rv.num);
                break;
        case Bracket:
                mpq_abs(rv.num, rv.num);
                break;
        case Bracket:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                break;
        case Concat:
                break;
        case Concat:
-               left = interp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               left = interp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                rvtype = Tstr;
                rv.str = text_join(left.str, right.str);
                break;
                rvtype = Tstr;
                rv.str = text_join(left.str, right.str);
                break;
+       case StringConv:
+               right = interp_exec(c, b->right, &rvtype);
+               rtype = Tstr;
+               rvtype = Tnum;
+
+               struct text tx = right.str;
+               char tail[3];
+               int neg = 0;
+               if (tx.txt[0] == '-') {
+                       neg = 1;
+                       tx.txt++;
+                       tx.len--;
+               }
+               if (number_parse(rv.num, tail, tx) == 0)
+                       mpq_init(rv.num);
+               else if (neg)
+                       mpq_neg(rv.num, rv.num);
+               if (tail[0])
+                       printf("Unsupported suffix: %.*s\n", tx.len, tx.txt);
+
+               break;
 
 ###### value functions
 
 
 ###### value functions
 
@@ -3032,11 +3049,10 @@ is in-place.
 ###### Binode types
        Block,
 
 ###### Binode types
        Block,
 
-###### expr precedence
-       $TERM pass
-
 ###### Grammar
 
 ###### Grammar
 
+       $TERM { } ;
+
        $*binode
        Block -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
                | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
        $*binode
        Block -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
                | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
@@ -3106,6 +3122,7 @@ is in-place.
                        $0->right = $<1;
                        }$
 
                        $0->right = $<1;
                        }$
 
+       $TERM pass
        SimpleStatement -> pass ${ $0 = NULL; }$
                | ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$
                ## SimpleStatement Grammar
        SimpleStatement -> pass ${ $0 = NULL; }$
                | ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$
                ## SimpleStatement Grammar
@@ -3165,7 +3182,7 @@ is in-place.
                while (rvtype == Tnone &&
                       b) {
                        if (b->left)
                while (rvtype == Tnone &&
                       b) {
                        if (b->left)
-                               rv = interp_exec(b->left, &rvtype);
+                               rv = interp_exec(c, b->left, &rvtype);
                        b = cast(binode, b->right);
                }
                break;
                        b = cast(binode, b->right);
                }
                break;
@@ -3254,7 +3271,7 @@ same solution.
                        if (b->left) {
                                if (sep)
                                        putchar(sep);
                        if (b->left) {
                                if (sep)
                                        putchar(sep);
-                               left = interp_exec(b->left, &ltype);
+                               left = interp_exec(c, b->left, &ltype);
                                print_value(ltype, &left);
                                free_value(ltype, &left);
                                if (b->right)
                                print_value(ltype, &left);
                                free_value(ltype, &left);
                                if (b->right)
@@ -3281,6 +3298,9 @@ it is declared, and error will be raised as the name is created as
        Assign,
        Declare,
 
        Assign,
        Declare,
 
+###### declare terminals
+       $TERM =
+
 ###### SimpleStatement Grammar
        | Variable = Expression ${
                        $0 = new(binode);
 ###### SimpleStatement Grammar
        | Variable = Expression ${
                        $0 = new(binode);
@@ -3382,8 +3402,8 @@ it is declared, and error will be raised as the name is created as
 ###### interp binode cases
 
        case Assign:
 ###### interp binode cases
 
        case Assign:
-               lleft = linterp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               lleft = linterp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                if (lleft) {
                        free_value(ltype, lleft);
                        dup_value(ltype, &right, lleft);
                if (lleft) {
                        free_value(ltype, lleft);
                        dup_value(ltype, &right, lleft);
@@ -3396,15 +3416,14 @@ it is declared, and error will be raised as the name is created as
                struct variable *v = cast(var, b->left)->var;
                if (v->merged)
                        v = v->merged;
                struct variable *v = cast(var, b->left)->var;
                if (v->merged)
                        v = v->merged;
+               free_value(v->type, v->val);
+               free(v->val);
                if (b->right) {
                if (b->right) {
-                       right = interp_exec(b->right, &rtype);
-                       free_value(v->type, v->val);
-                       free(v->val);
-                       v->val = val_alloc(v->type, &right);
+                       right = interp_exec(c, b->right, &rtype);
+                       v->val = val_alloc(c, v->type, &right);
                        rtype = Tnone;
                } else {
                        rtype = Tnone;
                } else {
-                       free_value(v->type, v->val);
-                       v->val = val_alloc(v->type, NULL);
+                       v->val = val_alloc(c, v->type, NULL);
                }
                break;
        }
                }
                break;
        }
@@ -3432,7 +3451,7 @@ function.
                        if (v->var->type == Tnone) {
                                /* Convert this to a label */
                                v->var->type = Tlabel;
                        if (v->var->type == Tnone) {
                                /* Convert this to a label */
                                v->var->type = Tlabel;
-                               v->var->val = val_alloc(Tlabel, NULL);
+                               v->var->val = val_alloc(c, Tlabel, NULL);
                                v->var->val->label = v->var->val;
                        }
                }
                                v->var->val->label = v->var->val;
                        }
                }
@@ -3456,7 +3475,7 @@ function.
 ###### interp binode cases
 
        case Use:
 ###### interp binode cases
 
        case Use:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                break;
 
 ### The Conditional Statement
                break;
 
 ### The Conditional Statement
@@ -3886,44 +3905,44 @@ defined.
                struct value v, cnd;
                struct type *vtype, *cndtype;
                struct casepart *cp;
                struct value v, cnd;
                struct type *vtype, *cndtype;
                struct casepart *cp;
-               struct cond_statement *c = cast(cond_statement, e);
+               struct cond_statement *cs = cast(cond_statement, e);
 
 
-               if (c->forpart)
-                       interp_exec(c->forpart, NULL);
+               if (cs->forpart)
+                       interp_exec(c, cs->forpart, NULL);
                do {
                do {
-                       if (c->condpart)
-                               cnd = interp_exec(c->condpart, &cndtype);
+                       if (cs->condpart)
+                               cnd = interp_exec(c, cs->condpart, &cndtype);
                        else
                                cndtype = Tnone;
                        if (!(cndtype == Tnone ||
                              (cndtype == Tbool && cnd.bool != 0)))
                                break;
                        // cnd is Tnone or Tbool, doesn't need to be freed
                        else
                                cndtype = Tnone;
                        if (!(cndtype == Tnone ||
                              (cndtype == Tbool && cnd.bool != 0)))
                                break;
                        // cnd is Tnone or Tbool, doesn't need to be freed
-                       if (c->dopart)
-                               interp_exec(c->dopart, NULL);
+                       if (cs->dopart)
+                               interp_exec(c, cs->dopart, NULL);
 
 
-                       if (c->thenpart) {
-                               rv = interp_exec(c->thenpart, &rvtype);
-                               if (rvtype != Tnone || !c->dopart)
+                       if (cs->thenpart) {
+                               rv = interp_exec(c, cs->thenpart, &rvtype);
+                               if (rvtype != Tnone || !cs->dopart)
                                        goto Xcond_done;
                                free_value(rvtype, &rv);
                                rvtype = Tnone;
                        }
                                        goto Xcond_done;
                                free_value(rvtype, &rv);
                                rvtype = Tnone;
                        }
-               } while (c->dopart);
+               } while (cs->dopart);
 
 
-               for (cp = c->casepart; cp; cp = cp->next) {
-                       v = interp_exec(cp->value, &vtype);
+               for (cp = cs->casepart; cp; cp = cp->next) {
+                       v = interp_exec(c, cp->value, &vtype);
                        if (value_cmp(cndtype, vtype, &v, &cnd) == 0) {
                                free_value(vtype, &v);
                                free_value(cndtype, &cnd);
                        if (value_cmp(cndtype, vtype, &v, &cnd) == 0) {
                                free_value(vtype, &v);
                                free_value(cndtype, &cnd);
-                               rv = interp_exec(cp->action, &rvtype);
+                               rv = interp_exec(c, cp->action, &rvtype);
                                goto Xcond_done;
                        }
                        free_value(vtype, &v);
                }
                free_value(cndtype, &cnd);
                                goto Xcond_done;
                        }
                        free_value(vtype, &v);
                }
                free_value(cndtype, &cnd);
-               if (c->elsepart)
-                       rv = interp_exec(c->elsepart, &rvtype);
+               if (cs->elsepart)
+                       rv = interp_exec(c, cs->elsepart, &rvtype);
                else
                        rvtype = Tnone;
        Xcond_done:
                else
                        rvtype = Tnone;
        Xcond_done:
@@ -3951,6 +3970,8 @@ various declarations in the parse context.
        $void
        Ocean -> OptNL DeclarationList
 
        $void
        Ocean -> OptNL DeclarationList
 
+       ## declare terminals
+
        OptNL ->
                | OptNL NEWLINE
        Newlines -> NEWLINE
        OptNL ->
                | OptNL NEWLINE
        Newlines -> NEWLINE
@@ -3994,6 +4015,8 @@ searching through for the Nth constant for decreasing N.
 
 ###### top level grammar
 
 
 ###### top level grammar
 
+       $TERM const
+
        DeclareConstant -> const { IN OptNL ConstList OUT OptNL } Newlines
                | const { SimpleConstList } Newlines
                | const IN OptNL ConstList OUT Newlines
        DeclareConstant -> const { IN OptNL ConstList OUT OptNL } Newlines
                | const { SimpleConstList } Newlines
                | const IN OptNL ConstList OUT Newlines
@@ -4035,8 +4058,8 @@ searching through for the Nth constant for decreasing N.
                if (!ok)
                        c->parse_error = 1;
                else if (v) {
                if (!ok)
                        c->parse_error = 1;
                else if (v) {
-                       struct value res = interp_exec($5, &v->type);
-                       v->val = val_alloc(v->type, &res);
+                       struct value res = interp_exec(c, $5, &v->type);
+                       v->val = val_alloc(c, v->type, &res);
                }
        } }$
 
                }
        } }$
 
@@ -4100,6 +4123,8 @@ analysis is a bit more interesting at this level.
                        c->prog = $<1;
        } }$
 
                        c->prog = $<1;
        } }$
 
+       $TERM program
+
        $*binode
        Program -> program OpenScope Varlist ColonBlock Newlines ${
                $0 = new(binode);
        $*binode
        Program -> program OpenScope Varlist ColonBlock Newlines ${
                $0 = new(binode);
@@ -4183,7 +4208,7 @@ analysis is a bit more interesting at this level.
                return !!ok;
        }
 
                return !!ok;
        }
 
-       static void interp_prog(struct exec *prog, char **argv)
+       static void interp_prog(struct parse_context *c, struct exec *prog, char **argv)
        {
                struct binode *p = cast(binode, prog);
                struct binode *al;
        {
                struct binode *p = cast(binode, prog);
                struct binode *al;
@@ -4196,24 +4221,30 @@ analysis is a bit more interesting at this level.
                while (al) {
                        struct var *v = cast(var, al->left);
                        struct value *vl = v->var->val;
                while (al) {
                        struct var *v = cast(var, al->left);
                        struct value *vl = v->var->val;
+                       struct value arg;
 
                        if (argv[0] == NULL) {
                                printf("Not enough args\n");
                                exit(1);
                        }
 
                        if (argv[0] == NULL) {
                                printf("Not enough args\n");
                                exit(1);
                        }
-                       al = cast(binode, al->right);
-                       if (vl)
-                               free_value(v->var->type, vl);
+                       if (v->var->type != Tstr) {
+                               printf("Arg not string!!\n"); // NOTEST
+                               exit(2);                      // NOTEST
+                       }
                        if (!vl) {
                        if (!vl) {
-                               vl = val_alloc(v->var->type, NULL);
+                               vl = val_alloc(c, v->var->type, NULL);
                                v->var->val = vl;
                        }
                                v->var->val = vl;
                        }
-                       free_value(v->var->type, vl);
-                       if (!parse_value(v->var->type, argv[0], vl))
-                               exit(1);
+
+                       arg.str.txt = argv[0];
+                       arg.str.len = strlen(argv[0]);
+                       free_value(Tstr, vl);
+                       dup_value(Tstr, &arg, vl);
+
+                       al = cast(binode, al->right);
                        argv++;
                }
                        argv++;
                }
-               v = interp_exec(p->right, &vtype);
+               v = interp_exec(c, p->right, &vtype);
                free_value(vtype, &v);
        }
 
                free_value(vtype, &v);
        }
 
@@ -4248,11 +4279,13 @@ things which will likely grow as the languages grows.
                name:string
                alive:Boolean
 
                name:string
                alive:Boolean
 
-       program A B:
+       program Astr Bstr:
                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
 
+               A := $Astr; B := $Bstr
+
                /* When a variable is defined in both branches of an 'if',
                 * and used afterwards, the variables are merged.
                 */
                /* When a variable is defined in both branches of an 'if',
                 * and used afterwards, the variables are merged.
                 */