###### test: consts
const
+ tau ::= pi * 2
pi ::= 3.141 592 653
four ::= 2 + 2 ; five ::= 10/2
const pie ::= "I like Pie";
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
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
.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"
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':
{
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
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;
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;
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);
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;
}
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;
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;
}
}
}