X-Git-Url: https://ocean-lang.org/code/?p=ocean;a=blobdiff_plain;f=csrc%2Foceani.mdc;h=e7976cf086ef03b338527549a4d3d069af7afd45;hp=3befc631dbf4ce72dd2adf34517a0deaeeb97c9d;hb=ef3bbba20f9e426125498f78fe3b1f4ea75de68a;hpb=2193e1c775bee42588715d13076496c8f8f2974a diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index 3befc63..e7976cf 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -130,7 +130,7 @@ structures can be used. struct parse_context *c = config2context(config); ###### Parser: code - + #define _GNU_SOURCE #include #include #include @@ -242,9 +242,11 @@ structures can be used. parse_oceani(ss->code, &context.config, dotrace ? stderr : NULL); + resolve_consts(&context); + prepare_types(&context); if (!context.parse_error && !analyse_funcs(&context)) { fprintf(stderr, "oceani: type error in program - not running.\n"); - context.parse_error = 1; + context.parse_error += 1; } if (doprint) { @@ -265,6 +267,7 @@ structures can be used. while (context.scope_depth > 0) scope_pop(&context); ## free global vars + ## free const decls ## free context types ## free context storage exit(context.parse_error ? 1 : 0); @@ -364,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 @@ -392,14 +396,14 @@ context so indicate that parsing failed. } } fputs("\n", stderr); - c->parse_error = 1; + c->parse_error += 1; } static void tok_err(struct parse_context *c, char *fmt, struct token *t) { fprintf(stderr, "%s:%d:%d: %s: %.*s\n", c->file_name, t->line, t->col, fmt, t->txt.len, t->txt.txt); - c->parse_error = 1; + c->parse_error += 1; } ## Entities: declared and predeclared. @@ -580,15 +584,20 @@ program and looking for errors. So `propagate_types` is passed an expected type (being a `struct type` 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". An `ok` flag is passed -by reference. It is set to `0` when an error is found, and `2` when -any change is made. If it remains unchanged at `1`, then no more -propagation is needed. +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 +any expression cannot be evaluated immediately, `Enoconst` is set. +If the expression can be copied, `Emaycopy` 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, Enoconst = 1<<2, + Emaycopy = 1<<3}; ###### format cases case 'r': @@ -597,11 +606,11 @@ propagation is needed. break; ###### forward decls - static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok, + static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr, struct type *type, int rules); ###### core functions - static struct type *__propagate_types(struct exec *prog, struct parse_context *c, int *ok, + static struct type *__propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr, struct type *type, int rules) { struct type *t; @@ -623,13 +632,14 @@ propagation is needed. return Tnone; } - static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok, + static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr, struct type *type, int rules) { - struct type *ret = __propagate_types(prog, c, ok, type, rules); + int pre_err = c->parse_error; + struct type *ret = __propagate_types(prog, c, perr, type, rules); - if (c->parse_error) - *ok = 0; + if (c->parse_error > pre_err) + *perr |= Efail; return ret; } @@ -647,6 +657,9 @@ the location of a value, which can be updated, in `lval`. Others will set `lval` to NULL indicating that there is a value of appropriate type in `rval`. +###### forward decls + static struct value interp_exec(struct parse_context *c, struct exec *e, + struct type **typeret); ###### core functions struct lrval { @@ -763,6 +776,10 @@ have not yet been implemented. Named type are stored in a simple linked list. Objects of each type are "values" which are often passed around by value. +There are both explicitly named types, and anonymous types. Anonymous +cannot be accessed by name, but are used internally and have a name +which might be reported in error messages. + ###### ast struct value { @@ -775,9 +792,11 @@ Named type are stored in a simple linked list. Objects of each type are 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, @@ -800,31 +819,57 @@ Named type are stored in a simple linked list. Objects of each type are struct type *typelist; +###### includes + #include + ###### ast functions static struct type *find_type(struct parse_context *c, struct text s) { - struct type *l = c->typelist; + struct type *t = c->typelist; - while (l && - text_cmp(l->name, s) != 0) - l = l->next; - return l; + while (t && (t->anon || + text_cmp(t->name, s) != 0)) + t = t->next; + return t; } - static struct type *add_type(struct parse_context *c, struct text s, - struct type *proto) + static struct type *_add_type(struct parse_context *c, struct text s, + struct type *proto, int anon) { 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; c->typelist = n; return n; } + static struct type *add_type(struct parse_context *c, struct text s, + struct type *proto) + { + return _add_type(c, s, proto, 0); + } + + static struct type *add_anon_type(struct parse_context *c, + struct type *proto, char *name, ...) + { + struct text t; + va_list ap; + + va_start(ap, name); + vasprintf(&t.txt, name, ap); + va_end(ap); + t.len = strlen(name); + return _add_type(c, t, proto, 1); + } + static void free_type(struct type *t) { /* The type is always a reference to something in the @@ -844,12 +889,12 @@ Named type are stored in a simple linked list. Objects of each type are { if (!type) fputs("*unknown*type*", f); // NOTEST - else if (type->name.len) + else if (type->name.len && !type->anon) fprintf(f, "%.*s", type->name.len, type->name.txt); else if (type->print_type) type->print_type(type, f); else - fputs("*invalid*type*", f); // NOTEST + fputs("*invalid*type*", f); } static void val_init(struct type *type, struct value *val) @@ -883,6 +928,40 @@ Named type are stored in a simple linked list. Objects of each type are fprintf(f, "*Unknown*"); // NOTEST } + static void prepare_types(struct parse_context *c) + { + struct type *t; + int retry = 1; + enum { none, some, cannot } progress = none; + + 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 static void free_value(struct type *type, struct value *v); @@ -903,6 +982,8 @@ Named type are stored in a simple linked list. Objects of each type are context.typelist = t->next; if (t->free_type) t->free_type(t); + if (t->anon) + free(t->name.txt); free(t); } @@ -917,12 +998,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 @@ -1080,7 +1159,7 @@ A separate function encoding these cases will simplify some code later. mpf_t fl; mpf_init2(fl, 20); mpf_set_q(fl, v->num); - gmp_fprintf(f, "%Fg", fl); + gmp_fprintf(f, "%.10Fg", fl); mpf_clear(fl); break; } @@ -1155,44 +1234,38 @@ executable. $*val Value -> True ${ - $0 = new_val(Tbool, $1); - $0->val.bool = 1; - }$ - | False ${ - $0 = new_val(Tbool, $1); - $0->val.bool = 0; - }$ - | NUMBER ${ - $0 = new_val(Tnum, $1); - { - char tail[3]; - if (number_parse($0->val.num, tail, $1.txt) == 0) - mpq_init($0->val.num); // UNTESTED - if (tail[0]) - tok_err(c, "error: unsupported number suffix", - &$1); - } - }$ - | STRING ${ - $0 = new_val(Tstr, $1); - { - char tail[3]; - string_parse(&$1, '\\', &$0->val.str, tail); - if (tail[0]) - tok_err(c, "error: unsupported string suffix", - &$1); - } - }$ - | MULTI_STRING ${ - $0 = new_val(Tstr, $1); - { - char tail[3]; - string_parse(&$1, '\\', &$0->val.str, tail); + $0 = new_val(Tbool, $1); + $0->val.bool = 1; + }$ + | False ${ + $0 = new_val(Tbool, $1); + $0->val.bool = 0; + }$ + | NUMBER ${ { + char tail[3]; + $0 = new_val(Tnum, $1); + if (number_parse($0->val.num, tail, $1.txt) == 0) + mpq_init($0->val.num); // UNTESTED if (tail[0]) - tok_err(c, "error: unsupported string suffix", + tok_err(c, "error: unsupported number suffix", &$1); - } - }$ + } }$ + | STRING ${ { + char tail[3]; + $0 = new_val(Tstr, $1); + string_parse(&$1, '\\', &$0->val.str, tail); + if (tail[0]) + tok_err(c, "error: unsupported string suffix", + &$1); + } }$ + | MULTI_STRING ${ { + char tail[3]; + $0 = new_val(Tstr, $1); + string_parse(&$1, '\\', &$0->val.str, tail); + if (tail[0]) + tok_err(c, "error: unsupported string suffix", + &$1); + } }$ ###### print exec cases case Xval: @@ -1200,6 +1273,7 @@ executable. struct val *v = cast(val, e); if (v->vtype == Tstr) printf("\""); + // FIXME how to ensure numbers have same precision. print_value(v->vtype, &v->val, stdout); if (v->vtype == Tstr) printf("\""); @@ -1551,10 +1625,10 @@ need to be freed. For this we need to be able to find it, so assume that while (v) { struct variable *next = v->previous; - if (v->global) { + if (v->global && v->frame_pos >= 0) { free_value(v->type, var_value(&context, v)); - if (v->depth == 0) - // This is a global constant + if (v->depth == 0 && v->type->free == function_free) + // This is a function constant free_exec(v->where_decl); } free(v); @@ -1821,13 +1895,17 @@ tell if it was set or not later. short local_size; void *global, *local; +###### forward decls + static struct value *global_alloc(struct parse_context *c, struct type *t, + struct variable *v, struct value *init); + ###### ast functions static struct value *var_value(struct parse_context *c, struct variable *v) { if (!v->global) { if (!c->local || !v->type) - return NULL; // NOTEST + return NULL; // UNTESTED if (v->frame_pos + v->type->size > c->local_size) { printf("INVALID frame_pos\n"); // NOTEST exit(2); // NOTEST @@ -1853,7 +1931,7 @@ tell if it was set or not later. t->prepare_type(c, t, 1); // NOTEST if (c->global_size & (t->align - 1)) - c->global_size = (c->global_size + t->align) & ~(t->align-1); + c->global_size = (c->global_size + t->align) & ~(t->align-1); // NOTEST if (!v) { v = &scratch; v->type = t; @@ -1969,7 +2047,7 @@ correctly. v->where_decl, NULL, 0, NULL); } } }$ - | IDENTIFIER :: ${ { + | IDENTIFIER :: ${ { struct variable *v = var_decl(c, $1.txt); $0 = new_pos(var, $1); $0->var = v; @@ -1985,7 +2063,7 @@ correctly. v->where_decl, NULL, 0, NULL); } } }$ - | IDENTIFIER : Type ${ { + | IDENTIFIER : Type ${ { struct variable *v = var_decl(c, $1.txt); $0 = new_pos(var, $1); $0->var = v; @@ -2003,7 +2081,7 @@ correctly. v->where_decl, NULL, 0, NULL); } } }$ - | IDENTIFIER :: Type ${ { + | IDENTIFIER :: Type ${ { struct variable *v = var_decl(c, $1.txt); $0 = new_pos(var, $1); $0->var = v; @@ -2028,7 +2106,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; @@ -2037,7 +2119,7 @@ correctly. } } cast(var, $0)->var = v; - } }$ + } }$ ###### print exec cases case Xvar: @@ -2085,19 +2167,19 @@ correctly. type_err(c, "error: variable used but not declared: %v", prog, NULL, 0, NULL); if (v->type == NULL) { - if (type && *ok != 0) { + if (type && !(*perr & Efail)) { v->type = type; v->where_set = prog; - *ok = 2; + *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; @@ -2147,8 +2229,8 @@ simple "Value" (to be explained later). ###### Grammar $*exec Term -> Value ${ $0 = $<1; }$ - | Variable ${ $0 = $<1; }$ - ## term grammar + | Variable ${ $0 = $<1; }$ + ## term grammar Thus far the complex types we have are arrays and structs. @@ -2199,25 +2281,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.vsize || type->array.static_size) - return; - - vsize = var_value(c, type->array.vsize); - 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 (type->array.static_size) + return 1; // UNTESTED + if (type->array.unspec && parse_time) + return 1; // UNTESTED + if (parse_time && type->array.vsize && !type->array.vsize->global) + return 1; // UNTESTED - if (parse_time) { - type->array.static_size = 1; - type->size = type->array.size * type->array.member->size; - type->align = type->array.member->align; + if (type->array.vsize) { + vsize = var_value(c, type->array.vsize); + if (!vsize) + 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; + + 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) @@ -2284,8 +2378,10 @@ with a const size by whether they are prepared at parse time or not. struct binding *b = type->array.vsize->name; fprintf(f, "%.*s%s]", b->name.len, b->name.txt, type->array.unspec ? "::" : ""); - } else + } else if (type->array.size) fprintf(f, "%d]", type->array.size); + else + fprintf(f, "]"); type_print(type->array.member, f); } @@ -2307,19 +2403,16 @@ with a const size by whether they are prepared at parse time or not. | [ NUMBER ] Type ${ { char tail[3]; mpq_t num; - struct text noname = { "", 0 }; struct type *t; + int elements = 0; - $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); mpq_clear(num); } else { - t->array.size = mpz_get_ui(mpq_numref(num)); + elements = 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); @@ -2328,21 +2421,22 @@ with a const size by whether they are prepared at parse time or not. &$2); mpq_clear(num); } - t->array.static_size = 1; - t->size = t->array.size * t->array.member->size; - t->align = t->array.member->align; + + $0 = t = add_anon_type(c, &array_prototype, "array[%d]", elements ); + t->array.size = elements; + t->array.member = $<4; + t->array.vsize = NULL; } }$ | [ 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); - $0 = add_type(c, noname, &array_prototype); + $0 = add_anon_type(c, &array_prototype, "array[%.*s]", $2.txt.len, $2.txt.txt); $0->array.member = $<4; $0->array.size = 0; $0->array.vsize = v; @@ -2357,13 +2451,12 @@ with a const size by whether they are prepared at parse time or not. | [ IDENTIFIER :: OptType ] Type ${ { struct variable *v = var_decl(c, $ID.txt); - struct text noname = { "", 0 }; v->type = $constant = 1; if (!v->type) v->type = Tnum; - $0 = add_type(c, noname, &array_prototype); + $0 = add_anon_type(c, &array_prototype, "array[var]"); $0->array.member = $<6; $0->array.size = 0; $0->array.unspec = 1; @@ -2396,8 +2489,8 @@ with a const size by whether they are prepared at parse time or not. /* left must be an array, right must be a number, * result is the member type of the array */ - propagate_types(b->right, c, ok, Tnum, 0); - t = propagate_types(b->left, c, ok, NULL, rules & Rnoconstant); + propagate_types(b->right, c, perr, Tnum, 0); + t = propagate_types(b->left, c, perr, NULL, rules & Rnoconstant); if (!t || t->compat != array_compat) { type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL); return NULL; @@ -2486,7 +2579,12 @@ function will be needed. struct type *type; struct value *init; int offset; - } *fields; + } *fields; // This is created when field_list is analysed. + struct fieldlist { + struct fieldlist *prev; + struct field f; + struct exec *init; + } *field_list; // This is created during parsing } structure; ###### type functions @@ -2521,6 +2619,15 @@ function will be needed. } } + static void free_fieldlist(struct fieldlist *f) + { + if (!f) + return; + free_fieldlist(f->prev); + free_exec(f->init); + free(f); + } + static void structure_free_type(struct type *t) { int i; @@ -2530,6 +2637,60 @@ function will be needed. t->structure.fields[i].init); } free(t->structure.fields); + free_fieldlist(t->structure.field_list); + } + + 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 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, parse_time); + + if (f->init == NULL) + continue; + do { + perr = 0; + propagate_types(f->init, c, &perr, f->f.type, 0); + } while (perr & Eretry); + if (perr & Efail) + c->parse_error += 1; // NOTEST + } + + t->structure.nfields = cnt; + t->structure.fields = calloc(cnt, sizeof(struct field)); + f = t->structure.field_list; + while (cnt > 0) { + int a = f->f.type->align; + cnt -= 1; + t->structure.fields[cnt] = f->f; + if (t->size & (a-1)) + t->size = (t->size | (a-1)) + 1; + t->structure.fields[cnt].offset = t->size; + t->size += ((f->f.type->size - 1) | (a-1)) + 1; + if (a > t->align) + t->align = a; + + if (f->init && !c->parse_error) { + struct value vl = interp_exec(c, f->init, NULL); + t->structure.fields[cnt].init = + global_alloc(c, f->f.type, NULL, &vl); + } + + f = f->prev; + } + return 1; } static struct type structure_prototype = { @@ -2537,6 +2698,7 @@ function will be needed. .free = structure_free, .free_type = structure_free_type, .print_type_decl = structure_print_type, + .prepare_type = structure_prepare_type, }; ###### exec type @@ -2594,7 +2756,7 @@ function will be needed. case Xfieldref: { struct fieldref *f = cast(fieldref, prog); - struct type *st = propagate_types(f->left, c, ok, NULL, 0); + struct type *st = propagate_types(f->left, c, perr, NULL, 0); if (!st) type_err(c, "error: unknown type for field access", f->left, // UNTESTED @@ -2629,100 +2791,61 @@ function will be needed. break; } -###### ast - struct fieldlist { - struct fieldlist *prev; - struct field f; - }; - -###### ast functions - static void free_fieldlist(struct fieldlist *f) - { - if (!f) - return; - free_fieldlist(f->prev); - if (f->f.init) { - free_value(f->f.type, f->f.init); // UNTESTED - free(f->f.init); // UNTESTED - } - free(f); - } - ###### top level grammar DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ { - struct type *t = - add_type(c, $2.txt, &structure_prototype); - int cnt = 0; - struct fieldlist *f; - - for (f = $3; f; f=f->prev) - cnt += 1; - - t->structure.nfields = cnt; - t->structure.fields = calloc(cnt, sizeof(struct field)); - f = $3; - while (cnt > 0) { - int a = f->f.type->align; - cnt -= 1; - t->structure.fields[cnt] = f->f; - if (t->size & (a-1)) - t->size = (t->size | (a-1)) + 1; - t->structure.fields[cnt].offset = t->size; - t->size += ((f->f.type->size - 1) | (a-1)) + 1; - if (a > t->align) - t->align = a; - f->f.init = NULL; - f = f->prev; - } - } }$ + 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 FieldBlock -> { IN OptNL FieldLines OUT OptNL } ${ $0 = $ SimpleFieldList Newlines ${ $0 = $prev = $prev = $ Field ${ $0 = $prev = $prev = $ IDENTIFIER : Type = Expression ${ { - int ok; - - $0 = calloc(1, sizeof(struct fieldlist)); - $0->f.name = $1.txt; - $0->f.type = $<3; - $0->f.init = NULL; - do { - ok = 1; - propagate_types($<5, c, &ok, $3, 0); - } while (ok == 2); - if (!ok) - c->parse_error = 1; // UNTESTED - else { - struct value vl = interp_exec(c, $5, NULL); - $0->f.init = global_alloc(c, $0->f.type, NULL, &vl); - } - } }$ - | IDENTIFIER : Type ${ - $0 = calloc(1, sizeof(struct fieldlist)); - $0->f.name = $1.txt; - $0->f.type = $<3; - if ($0->f.type->prepare_type) - $0->f.type->prepare_type(c, $0->f.type, 1); - }$ + $0 = calloc(1, sizeof(struct fieldlist)); + $0->f.name = $ID.txt; + $0->f.type = $f.init = NULL; + $0->init = $f.name = $ID.txt; + $0->f.type = $next) - if (t->print_type_decl && !t->check_args && t->name.txt[0] != ' ') { + if (!t->anon && t->print_type_decl && + !t->check_args) { i += 1; if (i == target) break; @@ -2852,7 +2976,7 @@ further detailed when Expression Lists are introduced. struct exec *function; ###### type functions - void (*check_args)(struct parse_context *c, int *ok, + void (*check_args)(struct parse_context *c, enum prop_err *perr, struct type *require, struct exec *args); ###### value functions @@ -2869,7 +2993,7 @@ further detailed when Expression Lists are introduced. return 0; } - static void function_check_args(struct parse_context *c, int *ok, + static void function_check_args(struct parse_context *c, enum prop_err *perr, struct type *require, struct exec *args) { /* This should be 'compat', but we don't have a 'tuple' type to @@ -2885,8 +3009,8 @@ further detailed when Expression Lists are introduced. args, NULL, 0, NULL); break; } - *ok = 1; - propagate_types(arg->left, c, ok, pv->var->type, 0); + *perr = 0; + propagate_types(arg->left, c, perr, pv->var->type, 0); param = cast(binode, param->right); arg = cast(binode, arg->right); } @@ -2960,50 +3084,50 @@ further detailed when Expression Lists are introduced. $*variable FuncName -> IDENTIFIER ${ { - struct variable *v = var_decl(c, $1.txt); - struct var *e = new_pos(var, $1); + struct variable *v = var_decl(c, $1.txt); + struct var *e = new_pos(var, $1); + e->var = v; + if (v) { + v->where_decl = e; + $0 = v; + } else { + v = var_ref(c, $1.txt); e->var = v; - if (v) { - v->where_decl = e; - $0 = v; - } else { - v = var_ref(c, $1.txt); - e->var = v; - type_err(c, "error: function '%v' redeclared", - e, NULL, 0, NULL); - type_err(c, "info: this is where '%v' was first declared", - v->where_decl, NULL, 0, NULL); - free_exec(e); - } - } }$ + type_err(c, "error: function '%v' redeclared", + e, NULL, 0, NULL); + type_err(c, "info: this is where '%v' was first declared", + v->where_decl, NULL, 0, NULL); + free_exec(e); + } + } }$ $*binode Args -> ArgsLine NEWLINE ${ $0 = $left; - *bp = $left; + *bp = $ ${ $0 = NULL; }$ - | Varlist ${ $0 = $<1; }$ - | Varlist ; ${ $0 = $<1; }$ + | Varlist ${ $0 = $<1; }$ + | Varlist ; ${ $0 = $<1; }$ Varlist -> Varlist ; ArgDecl ${ - $0 = new(binode); - $0->op = List; - $0->left = $right = $op = List; - $0->left = NULL; - $0->right = $op = List; + $0->left = $right = $op = List; + $0->left = NULL; + $0->right = $ IDENTIFIER : FormalType ${ { @@ -3085,7 +3209,10 @@ it in the "SimpleStatement Grammar" which will be described later. prog, NULL, 0, NULL); return NULL; } - v->var->type->check_args(c, ok, v->var->type, args); + *perr |= Enoconst; + v->var->type->check_args(c, perr, v->var->type, args); + if (v->var->type->function.inline_result) + *perr |= Emaycopy; return v->var->type->function.return_type; } @@ -3140,7 +3267,7 @@ Term - others will follow. $*exec Expression -> Term ${ $0 = $right); struct type *t2; - propagate_types(b->left, c, ok, Tbool, 0); - t = propagate_types(b2->left, c, ok, type, Rnolabel); - t2 = propagate_types(b2->right, c, ok, type ?: t, Rnolabel); + propagate_types(b->left, c, perr, Tbool, 0); + t = propagate_types(b2->left, c, perr, type, Rnolabel); + t2 = propagate_types(b2->right, c, perr, type ?: t, Rnolabel); return t ?: t2; } @@ -3255,17 +3382,17 @@ lists. In that case a separate function is used to print them. $*binode ExpressionList -> ExpressionList , Expression ${ - $0 = new(binode); - $0->op = List; - $0->left = $<1; - $0->right = $<3; - }$ - | Expression ${ - $0 = new(binode); - $0->op = List; - $0->left = NULL; - $0->right = $<1; - }$ + $0 = new(binode); + $0->op = List; + $0->left = $<1; + $0->right = $<3; + }$ + | Expression ${ + $0 = new(binode); + $0->op = List; + $0->left = NULL; + $0->right = $<1; + }$ ### Expressions: Boolean @@ -3287,42 +3414,42 @@ evaluate the second expression if not necessary. $LEFT not ###### expression grammar - | Expression or Expression ${ { - struct binode *b = new(binode); - b->op = Or; - b->left = $<1; - b->right = $<3; - $0 = b; - } }$ - | Expression or else Expression ${ { - struct binode *b = new(binode); - b->op = OrElse; - b->left = $<1; - b->right = $<4; - $0 = b; - } }$ + | Expression or Expression ${ { + struct binode *b = new(binode); + b->op = Or; + b->left = $<1; + b->right = $<3; + $0 = b; + } }$ + | Expression or else Expression ${ { + struct binode *b = new(binode); + b->op = OrElse; + b->left = $<1; + b->right = $<4; + $0 = b; + } }$ - | Expression and Expression ${ { - struct binode *b = new(binode); - b->op = And; - b->left = $<1; - b->right = $<3; - $0 = b; - } }$ - | Expression and then Expression ${ { - struct binode *b = new(binode); - b->op = AndThen; - b->left = $<1; - b->right = $<4; - $0 = b; - } }$ + | Expression and Expression ${ { + struct binode *b = new(binode); + b->op = And; + b->left = $<1; + b->right = $<3; + $0 = b; + } }$ + | Expression and then Expression ${ { + struct binode *b = new(binode); + b->op = AndThen; + b->left = $<1; + b->right = $<4; + $0 = b; + } }$ - | not Expression ${ { - struct binode *b = new(binode); - b->op = Not; - b->right = $<2; - $0 = b; - } }$ + | not Expression ${ { + struct binode *b = new(binode); + b->op = Not; + b->right = $<2; + $0 = b; + } }$ ###### print binode cases case And: @@ -3367,8 +3494,8 @@ evaluate the second expression if not necessary. case OrElse: case Not: /* both must be Tbool, result is Tbool */ - propagate_types(b->left, c, ok, Tbool, 0); - propagate_types(b->right, c, ok, Tbool, 0); + propagate_types(b->left, c, perr, Tbool, 0); + propagate_types(b->right, c, perr, Tbool, 0); if (type && type != Tbool) type_err(c, "error: %1 operation found where %2 expected", prog, Tbool, 0, type); @@ -3444,12 +3571,12 @@ expression operator, and the `CMPop` non-terminal will match one of them. ###### Grammar $eop - CMPop -> < ${ $0.op = Less; }$ - | > ${ $0.op = Gtr; }$ - | <= ${ $0.op = LessEq; }$ - | >= ${ $0.op = GtrEq; }$ - | == ${ $0.op = Eql; }$ - | != ${ $0.op = NEql; }$ + CMPop -> < ${ $0.op = Less; }$ + | > ${ $0.op = Gtr; }$ + | <= ${ $0.op = LessEq; }$ + | >= ${ $0.op = GtrEq; }$ + | == ${ $0.op = Eql; }$ + | != ${ $0.op = NEql; }$ ###### print binode cases @@ -3482,13 +3609,13 @@ expression operator, and the `CMPop` non-terminal will match one of them. case Eql: case NEql: /* Both must match but not be labels, result is Tbool */ - t = propagate_types(b->left, c, ok, NULL, Rnolabel); + t = propagate_types(b->left, c, perr, NULL, Rnolabel); if (t) - propagate_types(b->right, c, ok, t, 0); + propagate_types(b->right, c, perr, t, 0); else { - t = propagate_types(b->right, c, ok, NULL, Rnolabel); // UNTESTED + t = propagate_types(b->right, c, perr, NULL, Rnolabel); // UNTESTED if (t) // UNTESTED - t = propagate_types(b->left, c, ok, t, 0); // UNTESTED + t = propagate_types(b->left, c, perr, t, 0); // UNTESTED } if (!type_compat(type, Tbool, 0)) type_err(c, "error: Comparison returns %1 but %2 expected", prog, @@ -3589,17 +3716,17 @@ parentheses around an expression converts it into a Term, ###### Grammar $eop - Eop -> + ${ $0.op = Plus; }$ - | - ${ $0.op = Minus; }$ + Eop -> + ${ $0.op = Plus; }$ + | - ${ $0.op = Minus; }$ - Uop -> + ${ $0.op = Absolute; }$ - | - ${ $0.op = Negate; }$ - | $ ${ $0.op = StringConv; }$ + Uop -> + ${ $0.op = Absolute; }$ + | - ${ $0.op = Negate; }$ + | $ ${ $0.op = StringConv; }$ - Top -> * ${ $0.op = Times; }$ - | / ${ $0.op = Divide; }$ - | % ${ $0.op = Rem; }$ - | ++ ${ $0.op = Concat; }$ + Top -> * ${ $0.op = Times; }$ + | / ${ $0.op = Divide; }$ + | % ${ $0.op = Rem; }$ + | ++ ${ $0.op = Concat; }$ ###### print binode cases case Plus: @@ -3652,8 +3779,8 @@ parentheses around an expression converts it into a Term, case Negate: /* as propagate_types ignores a NULL, * unary ops fit here too */ - propagate_types(b->left, c, ok, Tnum, 0); - propagate_types(b->right, c, ok, Tnum, 0); + propagate_types(b->left, c, perr, Tnum, 0); + propagate_types(b->right, c, perr, Tnum, 0); if (!type_compat(type, Tnum, 0)) type_err(c, "error: Arithmetic returns %1 but %2 expected", prog, Tnum, rules, type); @@ -3661,8 +3788,8 @@ parentheses around an expression converts it into a Term, case Concat: /* both must be Tstr, result is Tstr */ - propagate_types(b->left, c, ok, Tstr, 0); - propagate_types(b->right, c, ok, Tstr, 0); + propagate_types(b->left, c, perr, Tstr, 0); + propagate_types(b->right, c, perr, Tstr, 0); if (!type_compat(type, Tstr, 0)) type_err(c, "error: Concat returns %1 but %2 expected", prog, Tstr, rules, type); @@ -3670,7 +3797,7 @@ parentheses around an expression converts it into a Term, case StringConv: /* op must be string, result is number */ - propagate_types(b->left, c, ok, Tstr, 0); + propagate_types(b->left, c, perr, Tstr, 0); if (!type_compat(type, Tnum, 0)) type_err(c, // UNTESTED "error: Can only convert string to number, not %1", @@ -3678,7 +3805,7 @@ parentheses around an expression converts it into a Term, return Tnum; case Bracket: - return propagate_types(b->right, c, ok, type, 0); + return propagate_types(b->right, c, perr, type, 0); ###### interp binode cases @@ -3836,78 +3963,78 @@ the common header for all reductions to use. $*binode Block -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $ OpenScope { IN OptNL Statementlist OUT OptNL } ${ $0 = $ { OpenScope IN OptNL Statementlist OUT OptNL } ${ $0 = $ { IN OptNL Statementlist OUT OptNL } ${ $0 = $ ComplexStatements ${ $0 = reorder_bilist($ ComplexStatements ComplexStatement ${ - if ($2 == NULL) { - $0 = $<1; - } else { - $0 = new(binode); - $0->op = Block; - $0->left = $<1; - $0->right = $<2; - } - }$ - | ComplexStatement ${ - if ($1 == NULL) { - $0 = NULL; - } else { - $0 = new(binode); - $0->op = Block; - $0->left = NULL; - $0->right = $<1; - } - }$ - - $*exec - ComplexStatement -> SimpleStatements Newlines ${ - $0 = reorder_bilist($ SimpleStatements ; SimpleStatement ${ + if ($2 == NULL) { + $0 = $<1; + } else { $0 = new(binode); $0->op = Block; $0->left = $<1; - $0->right = $<3; - }$ - | SimpleStatement ${ + $0->right = $<2; + } + }$ + | ComplexStatement ${ + if ($1 == NULL) { + $0 = NULL; + } else { $0 = new(binode); $0->op = Block; $0->left = NULL; $0->right = $<1; - }$ + } + }$ + + $*exec + ComplexStatement -> SimpleStatements Newlines ${ + $0 = reorder_bilist($ SimpleStatements ; SimpleStatement ${ + $0 = new(binode); + $0->op = Block; + $0->left = $<1; + $0->right = $<3; + }$ + | SimpleStatement ${ + $0 = new(binode); + $0->op = Block; + $0->left = NULL; + $0->right = $<1; + }$ $TERM pass $*exec SimpleStatement -> pass ${ $0 = NULL; }$ - | ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$ - ## SimpleStatement Grammar + | ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$ + ## SimpleStatement Grammar ###### print binode cases case Block: @@ -3945,7 +4072,7 @@ the common header for all reductions to use. struct binode *e; for (e = b; e; e = cast(binode, e->right)) { - t = propagate_types(e->left, c, ok, NULL, rules); + t = propagate_types(e->left, c, perr, NULL, rules); if ((rules & Rboolok) && (t == Tbool || t == Tnone)) t = NULL; if (t == Tnone && e->right) @@ -4034,7 +4161,7 @@ printed. else b = cast(binode, b->right); while (b) { - propagate_types(b->left, c, ok, NULL, Rnolabel); + propagate_types(b->left, c, perr, NULL, Rnolabel); b = cast(binode, b->right); } break; @@ -4078,31 +4205,31 @@ it is declared, and error will be raised as the name is created as ###### SimpleStatement Grammar | Term = Expression ${ - $0 = b= new(binode); - b->op = Assign; - b->left = $<1; - b->right = $<3; - }$ + $0 = b= new(binode); + b->op = Assign; + b->left = $<1; + b->right = $<3; + }$ | VariableDecl = Expression ${ - $0 = b= new(binode); - b->op = Declare; - b->left = $<1; - b->right =$<3; - }$ + $0 = b= new(binode); + b->op = Declare; + b->left = $<1; + b->right =$<3; + }$ | VariableDecl ${ - if ($1->var->where_set == NULL) { - type_err(c, - "Variable declared with no type or value: %v", - $1, NULL, 0, NULL); - free_var($1); - } else { - $0 = b = new(binode); - b->op = Declare; - b->left = $<1; - b->right = NULL; - } - }$ + if ($1->var->where_set == NULL) { + type_err(c, + "Variable declared with no type or value: %v", + $1, NULL, 0, NULL); + free_var($1); + } else { + $0 = b = new(binode); + b->op = Declare; + b->left = $<1; + b->right = NULL; + } + }$ ###### print binode cases @@ -4151,23 +4278,23 @@ it is declared, and error will be raised as the name is created as * For Assign, left must not be constant. * result is Tnone */ - t = propagate_types(b->left, c, ok, NULL, + t = propagate_types(b->left, c, perr, NULL, Rnolabel | (b->op == Assign ? Rnoconstant : 0)); if (!b->right) return Tnone; if (t) { - if (propagate_types(b->right, c, ok, t, 0) != 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); } else { - t = propagate_types(b->right, c, ok, NULL, Rnolabel); + t = propagate_types(b->right, c, perr, NULL, Rnolabel); if (t) - propagate_types(b->left, c, ok, t, + propagate_types(b->left, c, perr, t, (b->op == Assign ? Rnoconstant : 0)); } - if (t && t->dup == NULL && t->name.txt[0] != ' ') // HACK + if (t && t->dup == NULL && !(*perr & Emaycopy)) type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL); return Tnone; @@ -4241,7 +4368,7 @@ function which has a return type, and the "condition" code blocks in case Use: /* result matches value */ - return propagate_types(b->right, c, ok, type, 0); + return propagate_types(b->right, c, perr, type, 0); ###### interp binode cases @@ -4387,136 +4514,136 @@ casepart` to track a list of case parts. // ForPart, SwitchPart, and IfPart open scopes, o we have to close // them. WhilePart opens and closes its own scope. CondStatement -> ForPart OptNL ThenPart OptNL WhilePart CondSuffix ${ - $0 = $forpart = $thenpart = $looppart = $forpart = $looppart = $looppart = $condpart = $next = $0->casepart; - $0->casepart = $condpart = $next = $0->casepart; - $0->casepart = $condpart = $IP.condpart; $IP.condpart = NULL; - $0->thenpart = $IP.thenpart; $IP.thenpart = NULL; - // This is where we close an "if" statement - var_block_close(c, CloseSequential, $0); - }$ + $0 = $forpart = $thenpart = $looppart = $forpart = $looppart = $looppart = $condpart = $next = $0->casepart; + $0->casepart = $condpart = $next = $0->casepart; + $0->casepart = $condpart = $IP.condpart; $IP.condpart = NULL; + $0->thenpart = $IP.thenpart; $IP.thenpart = NULL; + // This is where we close an "if" statement + var_block_close(c, CloseSequential, $0); + }$ CondSuffix -> IfSuffix ${ - $0 = $<1; - }$ - | Newlines CasePart CondSuffix ${ - $0 = $next = $0->casepart; - $0->casepart = $next = $0->casepart; - $0->casepart = $next = $0->casepart; + $0->casepart = $next = $0->casepart; + $0->casepart = $ Newlines ${ $0 = new(cond_statement); }$ - | Newlines ElsePart ${ $0 = $ else OpenBlock Newlines ${ - $0 = new(cond_statement); - $0->elsepart = $elsepart); - }$ - | else OpenScope CondStatement ${ - $0 = new(cond_statement); - $0->elsepart = $elsepart); - }$ + $0 = new(cond_statement); + $0->elsepart = $elsepart); + }$ + | else OpenScope CondStatement ${ + $0 = new(cond_statement); + $0->elsepart = $elsepart); + }$ $*casepart CasePart -> case Expression OpenScope ColonBlock ${ - $0 = calloc(1,sizeof(struct casepart)); - $0->value = $action = $action); - }$ + $0 = calloc(1,sizeof(struct casepart)); + $0->value = $action = $action); + }$ $*exec // These scopes are closed in CondStatement ForPart -> for OpenBlock ${ - $0 = $ then OpenBlock ${ - $0 = $ while UseBlock OptNL do OpenBlock ${ - $0 = new(binode); - $0->op = Loop; - $0->left = $right = $right); - var_block_close(c, CloseSequential, $0); - }$ - | while OpenScope Expression OpenScope ColonBlock ${ - $0 = new(binode); - $0->op = Loop; - $0->left = $right = $right); - var_block_close(c, CloseSequential, $0); - }$ + $0 = new(binode); + $0->op = Loop; + $0->left = $right = $right); + var_block_close(c, CloseSequential, $0); + }$ + | while OpenScope Expression OpenScope ColonBlock ${ + $0 = new(binode); + $0->op = Loop; + $0->left = $right = $right); + var_block_close(c, CloseSequential, $0); + }$ $cond_statement IfPart -> if UseBlock OptNL then OpenBlock ${ - $0.condpart = $ switch OpenScope Expression ${ - $0 = $right, c, ok, Tnone, 0); + t = propagate_types(b->right, c, perr, Tnone, 0); if (!type_compat(Tnone, t, 0)) - *ok = 0; // UNTESTED - return propagate_types(b->left, c, ok, type, rules); + *perr |= Efail; // UNTESTED + return propagate_types(b->left, c, perr, type, rules); ###### propagate exec cases case Xcond_statement: @@ -4649,51 +4776,51 @@ casepart` to track a list of case parts. struct cond_statement *cs = cast(cond_statement, prog); struct casepart *cp; - t = propagate_types(cs->forpart, c, ok, Tnone, 0); + t = propagate_types(cs->forpart, c, perr, Tnone, 0); if (!type_compat(Tnone, t, 0)) - *ok = 0; // UNTESTED + *perr |= Efail; // UNTESTED if (cs->looppart) { - t = propagate_types(cs->thenpart, c, ok, Tnone, 0); + t = propagate_types(cs->thenpart, c, perr, Tnone, 0); if (!type_compat(Tnone, t, 0)) - *ok = 0; // UNTESTED + *perr |= Efail; // UNTESTED } if (cs->casepart == NULL) { - propagate_types(cs->condpart, c, ok, Tbool, 0); - propagate_types(cs->looppart, c, ok, Tbool, 0); + propagate_types(cs->condpart, c, perr, Tbool, 0); + propagate_types(cs->looppart, c, perr, Tbool, 0); } else { /* Condpart must match case values, with bool permitted */ t = NULL; for (cp = cs->casepart; cp && !t; cp = cp->next) - t = propagate_types(cp->value, c, ok, NULL, 0); + t = propagate_types(cp->value, c, perr, NULL, 0); if (!t && cs->condpart) - t = propagate_types(cs->condpart, c, ok, NULL, Rboolok); // UNTESTED + t = propagate_types(cs->condpart, c, perr, NULL, Rboolok); // UNTESTED if (!t && cs->looppart) - t = propagate_types(cs->looppart, c, ok, NULL, Rboolok); // UNTESTED + t = propagate_types(cs->looppart, c, perr, NULL, Rboolok); // UNTESTED // Now we have a type (I hope) push it down if (t) { for (cp = cs->casepart; cp; cp = cp->next) - propagate_types(cp->value, c, ok, t, 0); - propagate_types(cs->condpart, c, ok, t, Rboolok); - propagate_types(cs->looppart, c, ok, t, Rboolok); + propagate_types(cp->value, c, perr, t, 0); + propagate_types(cs->condpart, c, perr, t, Rboolok); + propagate_types(cs->looppart, c, perr, t, Rboolok); } } // (if)then, else, and case parts must return expected type. if (!cs->looppart && !type) - type = propagate_types(cs->thenpart, c, ok, NULL, rules); + type = propagate_types(cs->thenpart, c, perr, NULL, rules); if (!type) - type = propagate_types(cs->elsepart, c, ok, NULL, rules); + type = propagate_types(cs->elsepart, c, perr, NULL, rules); for (cp = cs->casepart; cp && !type; cp = cp->next) // UNTESTED - type = propagate_types(cp->action, c, ok, NULL, rules); // UNTESTED + type = propagate_types(cp->action, c, perr, NULL, rules); // UNTESTED if (type) { if (!cs->looppart) - propagate_types(cs->thenpart, c, ok, type, rules); - propagate_types(cs->elsepart, c, ok, type, rules); + propagate_types(cs->thenpart, c, perr, type, rules); + propagate_types(cs->elsepart, c, perr, type, rules); for (cp = cs->casepart; cp ; cp = cp->next) - propagate_types(cp->action, c, ok, type, rules); + propagate_types(cp->action, c, perr, type, rules); return type; } else return NULL; @@ -4776,20 +4903,21 @@ various declarations in the parse context. ## declare terminals OptNL -> - | OptNL NEWLINE + | OptNL NEWLINE + Newlines -> NEWLINE - | Newlines NEWLINE + | Newlines NEWLINE DeclarationList -> Declaration - | DeclarationList Declaration + | DeclarationList Declaration Declaration -> ERROR Newlines ${ - tok_err(c, // UNTESTED - "error: unhandled parse error", &$1); - }$ - | DeclareConstant - | DeclareFunction - | DeclareStruct + tok_err(c, // UNTESTED + "error: unhandled parse error", &$1); + }$ + | DeclareConstant + | DeclareFunction + | DeclareStruct ## top level grammar @@ -4797,13 +4925,15 @@ various declarations in the parse context. ### The `const` section -As well as being defined in with the code that uses them, constants -can be declared at the top level. These have full-file scope, so they -are always `InScope`. The value of a top level constant can be given -as an expression, and this is evaluated immediately rather than in the -later interpretation stage. Once we add functions to the language, we -will need rules concern which, if any, can be used to define a top -level constant. +As well as being defined in with the code that uses them, constants can +be declared at the top level. These have full-file scope, so they are +always `InScope`, even before(!) they have been declared. The value of +a top level constant can be given as an expression, and this is +evaluated after parsing and before execution. + +A function call can be used to evaluate a constant, but it will not have +access to any program state, once such statement becomes meaningful. +e.g. arguments and filesystem will not be visible. Constants are defined in a section that starts with the reserved word `const` and then has a block with a list of assignment statements. @@ -4812,97 +4942,147 @@ make it clear that they are constants. Type can also be given: if not, the type will be determined during analysis, as with other constants. -As the types constants are inserted at the head of a list, printing -them in the same order that they were read is not straight forward. -We take a quadratic approach here and count the number of constants -(variables of depth 0), then count down from there, each time -searching through for the Nth constant for decreasing N. +###### parse context + struct binode *constlist; ###### top level grammar $TERM const DeclareConstant -> const { IN OptNL ConstList OUT OptNL } Newlines - | const { SimpleConstList } Newlines - | const IN OptNL ConstList OUT Newlines - | const SimpleConstList Newlines + | const { SimpleConstList } Newlines + | const IN OptNL ConstList OUT Newlines + | const SimpleConstList Newlines ConstList -> ConstList SimpleConstLine - | SimpleConstLine + | SimpleConstLine + SimpleConstList -> SimpleConstList ; Const - | Const - | SimpleConstList ; + | Const + | SimpleConstList ; + SimpleConstLine -> SimpleConstList Newlines - | ERROR Newlines ${ tok_err(c, "Syntax error in constant", &$1); }$ + | ERROR Newlines ${ tok_err(c, "Syntax error in constant", &$1); }$ $*type CType -> Type ${ $0 = $<1; }$ - | ${ $0 = NULL; }$ + | ${ $0 = NULL; }$ + $void Const -> IDENTIFIER :: CType = Expression ${ { - int ok; struct variable *v; + struct binode *bl, *bv; + struct var *var = new_pos(var, $ID); - v = var_decl(c, $1.txt); + v = var_decl(c, $ID.txt); if (v) { - struct var *var = new_pos(var, $1); v->where_decl = var; v->where_set = var; - var->var = v; + v->type = $constant = 1; v->global = 1; } else { - struct variable *vorig = var_ref(c, $1.txt); - tok_err(c, "error: name already declared", &$1); - type_err(c, "info: this is where '%v' was first declared", - vorig->where_decl, NULL, 0, NULL); - } - do { - ok = 1; - propagate_types($5, c, &ok, $3, 0); - } while (ok == 2); - if (!ok) - c->parse_error = 1; - else if (v) { - struct value res = interp_exec(c, $5, &v->type); - global_alloc(c, v->type, v, &res); + v = var_ref(c, $1.txt); + 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; + + bv = new(binode); + bv->op = Declare; + bv->left = var; + bv->right= $op = List; + bl->left = c->constlist; + bl->right = bv; + c->constlist = bl; } }$ -###### print const decls +###### core functions + static void resolve_consts(struct parse_context *c) { - struct variable *v; - int target = -1; - - while (target != 0) { - int i = 0; - for (v = context.in_scope; v; v=v->in_scope) - if (v->depth == 0 && v->constant) { - i += 1; - if (i == target) - break; + struct binode *b; + int retry = 1; + enum { none, some, cannot } progress = none; + + c->constlist = reorder_bilist(c->constlist); + 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; } - - if (target == -1) { - if (i) - printf("const\n"); - target = i; - } else { - struct value *val = var_value(&context, v); - printf(" %.*s :: ", v->name->name.len, v->name->name.txt); - type_print(v->type, stdout); - printf(" = "); - if (v->type == Tstr) - printf("\""); - print_value(v->type, val, stdout); - if (v->type == Tstr) - printf("\""); - printf("\n"); - target -= 1; + } + switch (progress) { + case cannot: + retry = 0; break; + case none: + progress = cannot; break; + case some: + progress = none; break; } } } +###### print const decls + { + struct binode *b; + int first = 1; + + for (b = cast(binode, context.constlist); b; + b = cast(binode, b->right)) { + struct binode *vb = cast(binode, b->left); + struct var *vr = cast(var, vb->left); + struct variable *v = vr->var; + + if (first) + printf("const\n"); + first = 0; + + printf(" %.*s :: ", v->name->name.len, v->name->name.txt); + type_print(v->type, stdout); + printf(" = "); + print_exec(vb->right, -1, 0); + printf("\n"); + } + } + +###### free const decls + free_binode(context.constlist); + ### Function declarations The code in an Ocean program is all stored in function declarations. @@ -4922,8 +5102,8 @@ is a bit more interesting at this level. /* Create a 'struct' type from the results list, which * is a list for 'struct var' */ - struct text result_type_name = { " function_result", 5 }; - struct type *t = add_type(c, result_type_name, &structure_prototype); + struct type *t = add_anon_type(c, &structure_prototype, + "function result"); int cnt = 0; struct binode *b; @@ -4957,12 +5137,13 @@ is a bit more interesting at this level. struct binode *results, struct exec *code) { - struct text funcname = {" func", 5}; if (name) { struct value fn = {.function = code}; struct type *t; var_block_close(c, CloseFunction, code); - t = add_type(c, funcname, &function_prototype); + 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); if (!ret) { @@ -4990,32 +5171,32 @@ is a bit more interesting at this level. $*variable DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${ - $0 = declare_function(c, $in_scope; v; v = v->in_scope) { struct value *val; struct type *ret; - int ok = 1; + enum prop_err perr; if (v->depth != 0 || !v->type || !v->type->check_args) continue; ret = v->type->function.inline_result ? Tnone : v->type->function.return_type; val = var_value(c, v); do { - ok = 1; - propagate_types(val->function, c, &ok, ret, 0); - } while (ok == 2); - if (ok) + perr = 0; + propagate_types(val->function, c, &perr, ret, 0); + } while (!(perr & Efail) && (perr & Eretry)); + if (!(perr & Efail)) /* Make sure everything is still consistent */ - propagate_types(val->function, c, &ok, ret, 0); - if (!ok) + propagate_types(val->function, c, &perr, ret, 0); + if (perr & Efail) all_ok = 0; if (!v->type->function.inline_result && !v->type->function.return_type->dup) { @@ -5086,26 +5267,25 @@ is a bit more interesting at this level. { struct binode *bp = type->function.params; struct binode *b; - int ok = 1; + enum prop_err perr; int arg = 0; struct type *argv_type; - struct text argv_type_name = { " argv", 5 }; - argv_type = add_type(c, argv_type_name, &array_prototype); + argv_type = add_anon_type(c, &array_prototype, "argv"); argv_type->array.member = Tstr; argv_type->array.unspec = 1; for (b = bp; b; b = cast(binode, b->right)) { - ok = 1; + perr = 0; switch (arg++) { case 0: /* argv */ - propagate_types(b->left, c, &ok, argv_type, 0); + propagate_types(b->left, c, &perr, argv_type, 0); break; default: /* invalid */ // NOTEST - propagate_types(b->left, c, &ok, Tnone, 0); // NOTEST + propagate_types(b->left, c, &perr, Tnone, 0); // NOTEST } - if (!ok) - c->parse_error = 1; + if (perr & Efail) + c->parse_error += 1; } return !c->parse_error; @@ -5126,12 +5306,12 @@ is a bit more interesting at this level. progp = var_value(c, mainv); if (!progp || !progp->function) { fprintf(stderr, "oceani: no main function found.\n"); - c->parse_error = 1; + c->parse_error += 1; return; } if (!analyse_main(mainv->type, c)) { fprintf(stderr, "oceani: main has wrong type.\n"); - c->parse_error = 1; + c->parse_error += 1; return; } al = mainv->type->function.params;