From: NeilBrown Date: Sat, 4 Dec 2021 23:34:58 +0000 (+1100) Subject: oceani: allow types to be used before declaration X-Git-Url: https://ocean-lang.org/code/?p=ocean;a=commitdiff_plain;h=a0ff449bc7dff46460913fc7b1a379fb71db866b oceani: allow types to be used before declaration As we prepare all types after parsing, we can now use a type before it is declared. We need to be careful of mutually recursive structures, and we need to retry prepare_type if it fails because a needed member hasn't been prepared yet. Signed-off-by: NeilBrown --- diff --git a/csrc/oceani-tests.mdc b/csrc/oceani-tests.mdc index c13a9d1..d155701 100644 --- a/csrc/oceani-tests.mdc +++ b/csrc/oceani-tests.mdc @@ -585,6 +585,7 @@ Time to test if structure declarations and accesses work correctly. struct foo size:[three]number name:string + thing:baz active:Boolean = True struct baz { a:number; b:Boolean; } @@ -607,6 +608,7 @@ Time to test if structure declarations and accesses work correctly. for i:=0; then i=i+1; while i < 4: print info[i].name, info[i].active, info[i].size[0] + info[0].thing.b = True ###### output: structs @@ -776,10 +778,10 @@ separate file first, then run from there. This is a multiline string With an unsupportable suffix """Aa - .tmp.code:14:11: error: undefined type: unknown .tmp.code:15:12: error: name undeclared: unknowable .tmp.code:17:12: error: array size must be a constant: zzsize .tmp.code:20:12: error: unrecognised number: 00123 + .tmp.code:14:11: error: type used but not declared: unknown ## Tests for type errors @@ -792,7 +794,7 @@ These programs were generated by looking for the various places that `type_err()` are called. ###### test list - oceani_failing_tests += type_err1 type_err2 type_err3 type_err4 + oceani_failing_tests += type_err1 type_err2 type_err3 type_err4 type_err5 ###### test: type_err1 @@ -921,6 +923,22 @@ various places that `type_err()` are called. .tmp.code:3:14: info: variable 'b' was set as none here. oceani: type error in program - not running. +###### test: type_err5 + struct foo + bar:baz + a:number + struct baz + bat:foo + b:string + struct foo + c:number + +###### output: type_err5 + .tmp.code:8:7: error: type already declared: foo + .tmp.code:2:7: info: this is location of declartion: foo + .tmp.code:2:7: error: type has recursive definition: foo + .tmp.code:5:7: error: type has recursive definition: baz + ###### test list oceani_failing_tests += type_err_const type_err_const1 type_err_const2 missing_program bad_main diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index 9720d22..e09fe07 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -367,6 +367,7 @@ context so indicate that parsing failed. static void type_err(struct parse_context *c, char *fmt, struct exec *loc, struct type *t1, int rules, struct type *t2); + static void tok_err(struct parse_context *c, char *fmt, struct token *t); ###### core functions @@ -789,10 +790,11 @@ which might be reported in error messages. struct type { struct text name; struct type *next; + struct token first_use; int size, align; int anon; void (*init)(struct type *type, struct value *val); - void (*prepare_type)(struct parse_context *c, struct type *type, int parse_time); + int (*prepare_type)(struct parse_context *c, struct type *type, int parse_time); void (*print)(struct type *type, struct value *val, FILE *f); void (*print_type)(struct type *type, FILE *f); int (*cmp_order)(struct type *t1, struct type *t2, @@ -836,7 +838,10 @@ which might be reported in error messages. struct type *n; n = calloc(1, sizeof(*n)); - *n = *proto; + if (proto) + *n = *proto; + else + n->size = -1; n->name = s; n->anon = anon; n->next = c->typelist; @@ -924,10 +929,35 @@ which might be reported in error messages. static void prepare_types(struct parse_context *c) { struct type *t; + int retry = 1; + enum { none, some, cannot } progress = none; - for (t = c->typelist; t; t = t->next) - if (t->prepare_type) - t->prepare_type(c, t, 1); + while (retry) { + retry = 0; + + for (t = c->typelist; t; t = t->next) { + if (t->size < 0) + tok_err(c, "error: type used but not declared", + &t->first_use); + if (t->size == 0 && t->prepare_type) { + if (t->prepare_type(c, t, 1)) + progress = some; + else if (progress == cannot) + tok_err(c, "error: type has recursive definition", + &t->first_use); + else + retry = 1; + } + } + switch (progress) { + case cannot: + retry = 0; break; + case none: + progress = cannot; break; + case some: + progress = none; break; + } + } } ###### forward decls @@ -966,12 +996,10 @@ symbol for those. $*type Type -> IDENTIFIER ${ - $0 = find_type(c, $1.txt); + $0 = find_type(c, $ID.txt); if (!$0) { - tok_err(c, - "error: undefined type", &$1); - - $0 = Tnone; + $0 = add_type(c, $ID.txt, NULL); + $0->first_use = $ID; } }$ ## type grammar @@ -2251,33 +2279,37 @@ with a const size by whether they are prepared at parse time or not. ###### value functions - static void array_prepare_type(struct parse_context *c, struct type *type, + static int array_prepare_type(struct parse_context *c, struct type *type, int parse_time) { struct value *vsize; mpz_t q; if (type->array.static_size) - return; + return 1; // UNTESTED if (type->array.unspec && parse_time) - return; + return 1; // UNTESTED if (parse_time && type->array.vsize && !type->array.vsize->global) - return; + return 1; // UNTESTED if (type->array.vsize) { vsize = var_value(c, type->array.vsize); if (!vsize) - return; // UNTESTED + return 1; // UNTESTED mpz_init(q); mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num)); type->array.size = mpz_get_si(q); mpz_clear(q); } + if (!parse_time) + return 1; + if (type->array.member->size <= 0) + return 0; - if (parse_time && type->array.member->size) { - type->array.static_size = 1; - type->size = type->array.size * type->array.member->size; - type->align = type->array.member->align; - } + type->array.static_size = 1; + type->size = type->array.size * type->array.member->size; + type->align = type->array.member->align; + + return 1; } static void array_init(struct type *type, struct value *val) @@ -2606,21 +2638,24 @@ function will be needed. free_fieldlist(t->structure.field_list); } - static void structure_prepare_type(struct parse_context *c, - struct type *t, int parse_time) + static int structure_prepare_type(struct parse_context *c, + struct type *t, int parse_time) { int cnt = 0; struct fieldlist *f; if (!parse_time || t->structure.fields) - return; + return 1; for (f = t->structure.field_list; f; f=f->prev) { enum prop_err perr; cnt += 1; + if (f->f.type->size <= 0) + return 0; if (f->f.type->prepare_type) - f->f.type->prepare_type(c, f->f.type, 1); + f->f.type->prepare_type(c, f->f.type, parse_time); + if (f->init == NULL) continue; do { @@ -2653,6 +2688,7 @@ function will be needed. f = f->prev; } + return 1; } static struct type structure_prototype = { @@ -2755,9 +2791,23 @@ function will be needed. ###### top level grammar DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ { - struct type *t = - add_type(c, $2.txt, &structure_prototype); + struct type *t; + t = find_type(c, $ID.txt); + if (!t) + t = add_type(c, $ID.txt, &structure_prototype); + else if (t->size >= 0) { + 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->structure.field_list = $first_use = $ID; } }$ $*fieldlist