-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.
+ 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;
+ }
+ }
+ }
+
- $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
+ 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;
- 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 = $<FB;
+ t->first_use = $ID;
+ } }$
- 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 = $<Type;
+ $0->f.init = NULL;
+ $0->init = $<Expr;
+ } }$
+ | IDENTIFIER : Type ${
+ $0 = calloc(1, sizeof(struct fieldlist));
+ $0->f.name = $ID.txt;
+ $0->f.type = $<Type;
+ }$
- 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);
+ }
+ } }$
- 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);
- $0 = $<CS;
- $0->forpart = $<FP;
- $0->thenpart = $<TP;
- $0->looppart = $<WP;
- var_block_close(c, CloseSequential, $0);
- }$
- | ForPart OptNL WhilePart CondSuffix ${
- $0 = $<CS;
- $0->forpart = $<FP;
- $0->looppart = $<WP;
- var_block_close(c, CloseSequential, $0);
- }$
- | WhilePart CondSuffix ${
- $0 = $<CS;
- $0->looppart = $<WP;
- }$
- | SwitchPart OptNL CasePart CondSuffix ${
- $0 = $<CS;
- $0->condpart = $<SP;
- $CP->next = $0->casepart;
- $0->casepart = $<CP;
- var_block_close(c, CloseSequential, $0);
- }$
- | SwitchPart : IN OptNL CasePart CondSuffix OUT Newlines ${
- $0 = $<CS;
- $0->condpart = $<SP;
- $CP->next = $0->casepart;
- $0->casepart = $<CP;
- var_block_close(c, CloseSequential, $0);
- }$
- | IfPart IfSuffix ${
- $0 = $<IS;
- $0->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 = $<CS;
+ $0->forpart = $<FP;
+ $0->thenpart = $<TP;
+ $0->looppart = $<WP;
+ var_block_close(c, CloseSequential, $0);
+ }$
+ | ForPart OptNL WhilePart CondSuffix ${
+ $0 = $<CS;
+ $0->forpart = $<FP;
+ $0->looppart = $<WP;
+ var_block_close(c, CloseSequential, $0);
+ }$
+ | WhilePart CondSuffix ${
+ $0 = $<CS;
+ $0->looppart = $<WP;
+ }$
+ | SwitchPart OptNL CasePart CondSuffix ${
+ $0 = $<CS;
+ $0->condpart = $<SP;
+ $CP->next = $0->casepart;
+ $0->casepart = $<CP;
+ var_block_close(c, CloseSequential, $0);
+ }$
+ | SwitchPart : IN OptNL CasePart CondSuffix OUT Newlines ${
+ $0 = $<CS;
+ $0->condpart = $<SP;
+ $CP->next = $0->casepart;
+ $0->casepart = $<CP;
+ var_block_close(c, CloseSequential, $0);
+ }$
+ | IfPart IfSuffix ${
+ $0 = $<IS;
+ $0->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 = new(cond_statement);
- $0->elsepart = $<OB;
- var_block_close(c, CloseElse, $0->elsepart);
- }$
- | else OpenScope CondStatement ${
- $0 = new(cond_statement);
- $0->elsepart = $<CS;
- var_block_close(c, CloseElse, $0->elsepart);
- }$
+ $0 = new(cond_statement);
+ $0->elsepart = $<OB;
+ var_block_close(c, CloseElse, $0->elsepart);
+ }$
+ | else OpenScope CondStatement ${
+ $0 = new(cond_statement);
+ $0->elsepart = $<CS;
+ var_block_close(c, CloseElse, $0->elsepart);
+ }$
- $0 = new(binode);
- $0->op = Loop;
- $0->left = $<UB;
- $0->right = $<OB;
- var_block_close(c, CloseSequential, $0->right);
- var_block_close(c, CloseSequential, $0);
- }$
- | while OpenScope Expression OpenScope ColonBlock ${
- $0 = new(binode);
- $0->op = Loop;
- $0->left = $<Exp;
- $0->right = $<CB;
- var_block_close(c, CloseSequential, $0->right);
- var_block_close(c, CloseSequential, $0);
- }$
+ $0 = new(binode);
+ $0->op = Loop;
+ $0->left = $<UB;
+ $0->right = $<OB;
+ var_block_close(c, CloseSequential, $0->right);
+ var_block_close(c, CloseSequential, $0);
+ }$
+ | while OpenScope Expression OpenScope ColonBlock ${
+ $0 = new(binode);
+ $0->op = Loop;
+ $0->left = $<Exp;
+ $0->right = $<CB;
+ var_block_close(c, CloseSequential, $0->right);
+ var_block_close(c, CloseSequential, $0);
+ }$
- $0.condpart = $<UB;
- $0.thenpart = $<OB;
- var_block_close(c, CloseParallel, $0.thenpart);
- }$
- | if OpenScope Expression OpenScope ColonBlock ${
- $0.condpart = $<Ex;
- $0.thenpart = $<CB;
- var_block_close(c, CloseParallel, $0.thenpart);
- }$
- | if OpenScope Expression OpenScope OptNL then Block ${
- $0.condpart = $<Ex;
- $0.thenpart = $<Bl;
- var_block_close(c, CloseParallel, $0.thenpart);
- }$
+ $0.condpart = $<UB;
+ $0.thenpart = $<OB;
+ var_block_close(c, CloseParallel, $0.thenpart);
+ }$
+ | if OpenScope Expression OpenScope ColonBlock ${
+ $0.condpart = $<Ex;
+ $0.thenpart = $<CB;
+ var_block_close(c, CloseParallel, $0.thenpart);
+ }$
+ | if OpenScope Expression OpenScope OptNL then Block ${
+ $0.condpart = $<Ex;
+ $0.thenpart = $<Bl;
+ var_block_close(c, CloseParallel, $0.thenpart);
+ }$
- 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);
-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.
- 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 = $<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);
+ }
- 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;
+###### 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);
+
- $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
- }$
- | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
- $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
- }$
- | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
- $0 = declare_function(c, $<FN, NULL, Tnone, NULL, $<Bl);
- }$
- | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
- $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
- }$
- | func FuncName ( OpenScope ArgsLine ) : ( ArgsLine ) Block Newlines ${
- $0 = declare_function(c, $<FN, $<AL, NULL, $<AL2, $<Bl);
- }$
- | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
- $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
- }$
- | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
- $0 = declare_function(c, $<FN, NULL, $<Ty, NULL, $<Bl);
- }$
- | func FuncName IN OpenScope Args OUT OptNL return IN Args OUT OptNL do Block Newlines ${
- $0 = declare_function(c, $<FN, $<Ar, NULL, $<Ar2, $<Bl);
- }$
- | func FuncName NEWLINE OpenScope return IN Args OUT OptNL do Block Newlines ${
- $0 = declare_function(c, $<FN, NULL, NULL, $<Ar, $<Bl);
- }$
+ $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
+ }$
+ | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
+ }$
+ | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
+ $0 = declare_function(c, $<FN, NULL, Tnone, NULL, $<Bl);
+ }$
+ | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
+ }$
+ | func FuncName ( OpenScope ArgsLine ) : ( ArgsLine ) Block Newlines ${
+ $0 = declare_function(c, $<FN, $<AL, NULL, $<AL2, $<Bl);
+ }$
+ | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
+ }$
+ | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
+ $0 = declare_function(c, $<FN, NULL, $<Ty, NULL, $<Bl);
+ }$
+ | func FuncName IN OpenScope Args OUT OptNL return IN Args OUT OptNL do Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, NULL, $<Ar2, $<Bl);
+ }$
+ | func FuncName NEWLINE OpenScope return IN Args OUT OptNL do Block Newlines ${
+ $0 = declare_function(c, $<FN, NULL, NULL, $<Ar, $<Bl);
+ }$