From e4967da39aff091cd9dfa187cf77c84ba8643293 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 5 Dec 2021 09:37:00 +1100 Subject: [PATCH 1/1] oceani: allow global constants to be used before declared. 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 --- csrc/oceani-tests.mdc | 16 ++++++-- csrc/oceani.mdc | 96 ++++++++++++++++++++++++++++++------------- 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/csrc/oceani-tests.mdc b/csrc/oceani-tests.mdc index 359c8e5..c13a9d1 100644 --- a/csrc/oceani-tests.mdc +++ b/csrc/oceani-tests.mdc @@ -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" diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index 725719e..9720d22 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -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 = $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; } } } -- 2.43.0