]> ocean-lang.org Git - ocean/commitdiff
oceani: allow global constants to be used before declared.
authorNeilBrown <neil@brown.name>
Sat, 4 Dec 2021 22:37:00 +0000 (09:37 +1100)
committerNeilBrown <neil@brown.name>
Sat, 4 Dec 2021 23:02:32 +0000 (10:02 +1100)
When an undeclared name is used, it gets allocated a Tnone variable.
If one of these is found in a 'use' statement, it is assumed to be
a label (that will change later).
If it gets declared as a constant, its type gets set appropriately.

When evaluating global constants, if a value cannot be determined, we
continue with other constants, then, retry.  If the failure was because
some constant was not yet declared, this will eventually resolved.
If resolution is not possible, an error is produced.

Currently, non-global variables and functions cannot resolve.
This is tracking in the new Enoconst prop_err flag.

Signed-off-by: NeilBrown <neil@brown.name>
csrc/oceani-tests.mdc
csrc/oceani.mdc

index 359c8e504675d0dc36ceed1f4a0f880da8130d26..c13a9d173a5f0d876a500791f05fe2630023f073 100644 (file)
@@ -167,6 +167,7 @@ Now some contants
 
 ###### test: consts
        const
+               tau ::= pi * 2
                pi ::= 3.141 592 653
                four ::= 2 + 2 ; five ::= 10/2
        const pie ::= "I like Pie";
@@ -176,12 +177,12 @@ Now some contants
        func main()
                print "Hello World, what lovely oceans you have!"
                print "are there", five, "?"
-               print pi, pie, "but", cake
+               print pi, pie, "but", cake, "Tau is", tau
 
 ###### output: consts
        Hello World, what lovely oceans you have!
        are there 5 ?
-       3.141592653 I like Pie but The cake is a lie
+       3.141592653 I like Pie but The cake is a lie Tau is 6.283185306
 
 Test merging of variables from multiple cases
 
@@ -921,7 +922,7 @@ various places that `type_err()` are called.
        oceani: type error in program - not running.
 
 ###### test list
-       oceani_failing_tests += type_err_const type_err_const1 missing_program bad_main
+       oceani_failing_tests += type_err_const type_err_const1 type_err_const2 missing_program bad_main
 
 ###### test: type_err_const
        const
@@ -961,6 +962,15 @@ various places that `type_err()` are called.
        .tmp.code:3:12: Syntax error in constant: :
        .tmp.code:4:12: Syntax error in constant: :
 
+###### test: type_err_const2
+       const
+               four ::= two + two
+               two ::= four / 2
+       
+###### output: type_err_const2
+       .tmp.code:3:8: error: const four cannot be resolved.
+       .tmp.code:4:8: error: const two cannot be resolved.
+
 ###### test: missing_program
        const
                foo::="bar"
index 725719e2558a19c5f1c2f5c4456b97c002a20c4f..9720d222fac37ffced3f0494c446e3af2203eb19 100644 (file)
@@ -586,13 +586,15 @@ pointer together with some `val_rules` flags) that the `exec` is
 expected to return, and returns the type that it does return, either of
 which can be `NULL` signifying "unknown".  A `prop_err` flag set is
 passed by reference.  It has `Efail` set when an error is found, and
-`Eretry` when the type for some element is set via propagation.  If it
-remains unchanged at `0`, then no more propagation is needed.
+`Eretry` when the type for some element is set via propagation.  If
+any expression cannot be evaluated immediately, `Enoconst` is set.
+
+If it remains unchanged at `0`, then no more propagation is needed.
 
 ###### ast
 
        enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1, Rnoconstant = 1<<2};
-       enum prop_err {Efail = 1<<0, Eretry = 1<<1};
+       enum prop_err {Efail = 1<<0, Eretry = 1<<1, Enoconst = 1<<2};
 
 ###### format cases
        case 'r':
@@ -1873,7 +1875,7 @@ tell if it was set or not later.
        {
                if (!v->global) {
                        if (!c->local || !v->type)
-                               return NULL;
+                               return NULL;    // UNTESTED
                        if (v->frame_pos + v->type->size > c->local_size) {
                                printf("INVALID frame_pos\n");  // NOTEST
                                exit(2);                        // NOTEST
@@ -2074,7 +2076,11 @@ correctly.
                struct variable *v = var_ref(c, $1.txt);
                $0 = new_pos(var, $1);
                if (v == NULL) {
-                       /* This might be a label - allocate a var just in case */
+                       /* This might be a global const or a label
+                        * Allocate a var with impossible type Tnone,
+                        * which will be adjusted when we find out what it is,
+                        * or will trigger an error.
+                        */
                        v = var_decl(c, $1.txt);
                        if (v) {
                                v->type = Tnone;
@@ -2136,14 +2142,14 @@ correctly.
                                v->where_set = prog;
                                *perr |= Eretry;
                        }
-                       return type;
-               }
-               if (!type_compat(type, v->type, rules)) {
+               } else if (!type_compat(type, v->type, rules)) {
                        type_err(c, "error: expected %1%r but variable '%v' is %2", prog,
                                 type, rules, v->type);
                        type_err(c, "info: this is where '%v' was set to %1", v->where_set,
                                 v->type, rules, NULL);
                }
+               if (!v->global || v->frame_pos < 0)
+                       *perr |= Enoconst;
                if (!type)
                        return v->type;
                return type;
@@ -2254,11 +2260,13 @@ with a const size by whether they are prepared at parse time or not.
                        return;
                if (type->array.unspec && parse_time)
                        return;
+               if (parse_time && type->array.vsize && !type->array.vsize->global)
+                       return;
 
                if (type->array.vsize) {
                        vsize = var_value(c, type->array.vsize);
                        if (!vsize)
-                               return;
+                               return; // UNTESTED
                        mpz_init(q);
                        mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
                        type->array.size = mpz_get_si(q);
@@ -3149,6 +3157,7 @@ it in the "SimpleStatement Grammar" which will be described later.
                                 prog, NULL, 0, NULL);
                        return NULL;
                }
+               *perr |= Enoconst;
                v->var->type->check_args(c, perr, v->var->type, args);
                return v->var->type->function.return_type;
        }
@@ -4920,9 +4929,17 @@ constants.
                        v->global = 1;
                } else {
                        v = var_ref(c, $1.txt);
-                       tok_err(c, "error: name already declared", &$1);
-                       type_err(c, "info: this is where '%v' was first declared",
-                                v->where_decl, NULL, 0, NULL);
+                       if (v->type == Tnone) {
+                               v->where_decl = var;
+                               v->where_set = var;
+                               v->type = $<CT;
+                               v->constant = 1;
+                               v->global = 1;
+                       } else {
+                               tok_err(c, "error: name already declared", &$1);
+                               type_err(c, "info: this is where '%v' was first declared",
+                                        v->where_decl, NULL, 0, NULL);
+                       }
                }
                var->var = v;
 
@@ -4942,23 +4959,46 @@ constants.
        static void resolve_consts(struct parse_context *c)
        {
                struct binode *b;
+               int retry = 1;
+               enum { none, some, cannot } progress = none;
+
                c->constlist = reorder_bilist(c->constlist);
-               for (b = cast(binode, c->constlist); b;
-                    b = cast(binode, b->right)) {
-                       enum prop_err perr;
-                       struct binode *vb = cast(binode, b->left);
-                       struct var *v = cast(var, vb->left);
-                       do {
-                               perr = 0;
-                               propagate_types(vb->right, c, &perr,
-                                               v->var->type, 0);
-                       } while (perr & Eretry);
-                       if (perr & Efail)
-                               c->parse_error += 1;
-                       else {
-                               struct value res = interp_exec(
-                                       c, vb->right, &v->var->type);
-                               global_alloc(c, v->var->type, v->var, &res);
+               while (retry) {
+                       retry = 0;
+                       for (b = cast(binode, c->constlist); b;
+                            b = cast(binode, b->right)) {
+                               enum prop_err perr;
+                               struct binode *vb = cast(binode, b->left);
+                               struct var *v = cast(var, vb->left);
+                               if (v->var->frame_pos >= 0)
+                                       continue;
+                               do {
+                                       perr = 0;
+                                       propagate_types(vb->right, c, &perr,
+                                                       v->var->type, 0);
+                               } while (perr & Eretry);
+                               if (perr & Efail)
+                                       c->parse_error += 1;
+                               else if (!(perr & Enoconst)) {
+                                       progress = some;
+                                       struct value res = interp_exec(
+                                               c, vb->right, &v->var->type);
+                                       global_alloc(c, v->var->type, v->var, &res);
+                               } else {
+                                       if (progress == cannot)
+                                               type_err(c, "error: const %v cannot be resolved.",
+                                                        v, NULL, 0, NULL);
+                                       else
+                                               retry = 1;
+                               }
+                       }
+                       switch (progress) {
+                       case cannot:
+                               retry = 0; break;
+                       case none:
+                               progress = cannot; break;
+                       case some:
+                               progress = none; break;
                        }
                }
        }