Some operators that have only recently been added, and so have not
generated all that much experience yet are "and then" and "or else" as
-short-circuit Boolean operators, and the "if ... else" trinary
-operator which can select between two expressions based on a third
-(which appears syntactically in the middle).
+short-circuit Boolean operators (which have since been remove), and the
+"if ... else" trinary operator which can select between two expressions
+based on a third (which appears syntactically in the middle).
The "func" clause currently only allows a "main" function to be
declared. That will be extended when proper function support is added.
## macros
struct parse_context;
## ast
+ ## ast late
struct parse_context {
struct token_config config;
char *file_name;
static void fput_loc(struct exec *loc, FILE *f);
static void type_err(struct parse_context *c,
char *fmt, struct exec *loc,
- struct type *t1, int rules, struct type *t2);
+ struct type *t1, enum val_rules rules, struct type *t2);
static void tok_err(struct parse_context *c, char *fmt, struct token *t);
###### core functions
static void type_err(struct parse_context *c,
char *fmt, struct exec *loc,
- struct type *t1, int rules, struct type *t2)
+ struct type *t1, enum val_rules rules, struct type *t2)
{
fprintf(stderr, "%s:", c->file_name);
fput_loc(loc, stderr);
executable is something quite specific like a constant or variable name.
So we define a `struct exec` to be a general executable with a type, and
a `struct binode` which is a subclass of `exec`, forms a node in a
-binary tree, and holds an operation. There will be other subclasses,
-and to access these we need to be able to `cast` the `exec` into the
-various other types. The first field in any `struct exec` is the type
-from the `exec_types` enum.
+binary tree, and holds an operation. The simplest operation is "List"
+which can be used to combine several execs together.
+
+There will be other subclasses, and to access these we need to be able
+to `cast` the `exec` into the various other types. The first field in
+any `struct exec` is the type from the `exec_types` enum.
###### macros
#define cast(structname, pointer) ({ \
struct binode {
struct exec;
enum Btype {
+ List,
## Binode types
} op;
struct exec *left, *right;
Printing an `exec` requires that we know the current indent level for
printing line-oriented components. As will become clear later, we
-also want to know what sort of bracketing to use.
+also want to know what sort of bracketing to use. It will also be used
+to sometime print comments after an exec to explain some of the results
+of analysis.
###### ast functions
{
struct binode *b2;
switch(b->op) {
+ case List: abort(); // must be handled by parent NOTEST
## print binode cases
}
}
static void print_exec(struct exec *e, int indent, int bracket)
{
if (!e)
- return;
+ return; // NOTEST
switch (e->type) {
case Xbinode:
print_binode(cast(binode, e), indent, bracket); break;
## print exec cases
}
- if (e->to_free) {
- struct variable *v;
- do_indent(indent, "/* FREE");
- for (v = e->to_free; v; v = v->next_free) {
- printf(" %.*s", v->name->name.len, v->name->name.txt);
- printf("[%d,%d]", v->scope_start, v->scope_end);
- if (v->frame_pos >= 0)
- printf("(%d+%d)", v->frame_pos,
- v->type ? v->type->size:0);
- }
- printf(" */\n");
- }
+ ## print exec extras
}
###### forward decls
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.
+any expression cannot be evaluated a compile time, `Eruntime` is set.
If the expression can be copied, `Emaycopy` is set.
-If it remains unchanged at `0`, then no more propagation is needed.
+If `Erval` is set, then the value cannot be assigned to because it is
+a temporary result. If `Erval` is clear but `Econst` is set, then
+the value can only be assigned once, when the variable is declared.
###### 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':
- if (rules & Rnolabel)
- fputs(" (labels not permitted)", stderr);
- break;
+ enum val_rules {Rboolok = 1<<0, Rrefok = 1<<1,};
+ enum prop_err {Efail = 1<<0, Eretry = 1<<1, Eruntime = 1<<2,
+ Emaycopy = 1<<3, Erval = 1<<4, Econst = 1<<5};
###### forward decls
static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
- struct type *type, int rules);
+ struct type *type, enum val_rules rules);
###### core functions
static struct type *__propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
- struct type *type, int rules)
+ enum prop_err *perr_local,
+ struct type *type, enum val_rules rules)
{
struct type *t;
{
struct binode *b = cast(binode, prog);
switch (b->op) {
+ case List: abort(); // NOTEST
## propagate binode cases
}
break;
}
static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
- struct type *type, int rules)
+ struct type *type, enum val_rules rules)
{
int pre_err = c->parse_error;
- struct type *ret = __propagate_types(prog, c, perr, type, rules);
+ enum prop_err perr_local = 0;
+ struct type *ret = __propagate_types(prog, c, perr, &perr_local, type, rules);
+ *perr |= perr_local & (Efail | Eretry);
if (c->parse_error > pre_err)
*perr |= Efail;
return ret;
struct type *ltype, *rtype;
ltype = rtype = Tnone;
switch (b->op) {
+ case List: abort(); // NOTEST
## interp binode cases
}
free_value(ltype, &left);
freed, though eventually such manipulations will be better integrated
into the language.
-Rather than requiring every numeric type to support all numeric
-operations (add, multiply, etc), we allow types to be able to present
-as one of a few standard types: integer, float, and fraction. The
-existence of these conversion functions eventually enable types to
-determine if they are compatible with other types, though such types
-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.
};
};
+###### ast late
struct type {
struct text name;
struct type *next;
int (*test)(struct type *type, struct value *val);
void (*free)(struct type *type, struct value *val);
void (*free_type)(struct type *t);
- long long (*to_int)(struct value *v);
- double (*to_float)(struct value *v);
- int (*to_mpq)(mpq_t *q, struct value *v);
## type functions
union {
## type union fields
###### forward decls
static void free_value(struct type *type, struct value *v);
- static int type_compat(struct type *require, struct type *have, int rules);
+ static int type_compat(struct type *require, struct type *have, enum val_rules rules);
static void type_print(struct type *type, FILE *f);
static void val_init(struct type *type, struct value *v);
static void dup_value(struct type *type,
###### type functions
- int (*compat)(struct type *this, struct type *other);
+ int (*compat)(struct type *this, struct type *other, enum val_rules rules);
###### ast functions
- static int type_compat(struct type *require, struct type *have, int rules)
+ static int type_compat(struct type *require, struct type *have,
+ enum val_rules rules)
{
if ((rules & Rboolok) && have == Tbool)
return 1; // NOTEST
- if ((rules & Rnolabel) && have == Tlabel)
- return 0; // NOTEST
if (!require || !have)
return 1;
if (require->compat)
- return require->compat(require, have);
+ return require->compat(require, have, rules);
return require == have;
}
struct text str;
mpq_t num;
unsigned char bool;
- void *label;
+ int label;
###### ast functions
static void _free_value(struct type *type, struct value *v)
val->bool = 0;
break;
case Vlabel:
- val->label = NULL;
- break;
+ val->label = 0; // NOTEST
+ break; // NOTEST
}
}
case Vnone: // NOTEST
break; // NOTEST
case Vlabel:
- vnew->label = vold->label;
- break;
+ vnew->label = vold->label; // NOTEST
+ break; // NOTEST
case Vbool:
vnew->bool = vold->bool;
break;
{
int cmp;
if (tl != tr)
- return tl - tr; // NOTEST
+ return tl - tr;
switch (tl->vtype) {
case Vlabel: cmp = left->label == right->label ? 0 : 1; break;
case Vnum: cmp = mpq_cmp(left->num, right->num); break;
case Vnone: // NOTEST
fprintf(f, "*no-value*"); break; // NOTEST
case Vlabel: // NOTEST
- fprintf(f, "*label-%p*", v->label); break; // NOTEST
+ fprintf(f, "*label-%d*", v->label); break; // NOTEST
case Vstr:
fprintf(f, "%.*s", v->str.len, v->str.txt); break;
case Vbool:
return v;
}
-###### Grammar
-
+###### declare terminals
$TERM True False
+###### Grammar
+
$*val
Value -> True ${
$0 = new_val(Tbool, $1);
$0->val.bool = 0;
}$
| NUMBER ${ {
- char tail[3];
+ 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 number suffix",
- &$1);
+ if (number_parse($0->val.num, tail, $1.txt) == 0) {
+ mpq_init($0->val.num);
+ tok_err(c, "error: unsupported number format", &$NUM);
+ } else if (tail[0])
+ tok_err(c, "error: unsupported number suffix", &$1);
} }$
| STRING ${ {
char tail[3];
{
struct val *val = cast(val, prog);
if (!type_compat(type, val->vtype, rules))
- type_err(c, "error: expected %1%r found %2",
+ type_err(c, "error: expected %1 found %2",
prog, type, rules, val->vtype);
+ *perr |= Erval;
return val->vtype;
}
return rv;
}
+#### Labels
+
+Labels are a temporary concept until I implement enums. There are an
+anonymous enum which is declared by usage. Thet are only allowed in
+`use` statements and corresponding `case` entries. They appear as a
+period followed by an identifier. All identifiers that are "used" must
+have a "case".
+
+For now, we have a global list of labels, and don't check that all "use"
+match "case".
+
+###### exec type
+ Xlabel,
+
+###### ast
+ struct label {
+ struct exec;
+ struct text name;
+ int value;
+ };
+###### free exec cases
+ case Xlabel:
+ free(e);
+ break;
+###### print exec cases
+ case Xlabel: {
+ struct label *l = cast(label, e);
+ printf(".%.*s", l->name.len, l->name.txt);
+ break;
+ }
+
+###### ast
+ struct labels {
+ struct labels *next;
+ struct text name;
+ int value;
+ };
+###### parse context
+ struct labels *labels;
+ int next_label;
+###### ast functions
+ static int label_lookup(struct parse_context *c, struct text name)
+ {
+ struct labels *l, **lp = &c->labels;
+ while (*lp && text_cmp((*lp)->name, name) < 0)
+ lp = &(*lp)->next;
+ if (*lp && text_cmp((*lp)->name, name) == 0)
+ return (*lp)->value;
+ l = calloc(1, sizeof(*l));
+ l->next = *lp;
+ l->name = name;
+ if (c->next_label == 0)
+ c->next_label = 2;
+ l->value = c->next_label;
+ c->next_label += 1;
+ *lp = l;
+ return l->value;
+ }
+
+###### free context storage
+ while (context.labels) {
+ struct labels *l = context.labels;
+ context.labels = l->next;
+ free(l);
+ }
+
+###### declare terminals
+ $TERM .
+###### term grammar
+ | . IDENTIFIER ${ {
+ struct label *l = new_pos(label, $ID);
+ l->name = $ID.txt;
+ $0 = l;
+ } }$
+###### propagate exec cases
+ case Xlabel: {
+ struct label *l = cast(label, prog);
+ l->value = label_lookup(c, l->name);
+ if (!type_compat(type, Tlabel, rules))
+ type_err(c, "error: expected %1 found %2",
+ prog, type, rules, Tlabel);
+ *perr |= Erval;
+ return Tlabel;
+ }
+###### interp exec cases
+ case Xlabel : {
+ struct label *l = cast(label, e);
+ rv.label = l->value;
+ rvtype = Tlabel;
+ break;
+ }
+
+
### Variables
Variables are scoped named values. We store the names in a linked list
When a scope closes, the values of the variables might need to be freed.
This happens in the context of some `struct exec` and each `exec` will
-need to know which variables need to be freed when it completes.
+need to know which variables need to be freed when it completes. To
+improve visibility, we add a comment when printing any `exec` that
+embodies a scope to list the variables that must be freed when it ends.
####### exec fields
struct variable *to_free;
}
}
+###### print exec extras
+ if (e->to_free) {
+ struct variable *v;
+ do_indent(indent, "/* FREE");
+ for (v = e->to_free; v; v = v->next_free) {
+ printf(" %.*s", v->name->name.len, v->name->name.txt);
+ printf("[%d,%d]", v->scope_start, v->scope_end);
+ if (v->frame_pos >= 0)
+ printf("(%d+%d)", v->frame_pos,
+ v->type ? v->type->size:0);
+ }
+ printf(" */\n");
+ }
+
###### ast functions
static void variable_unlink_exec(struct variable *v)
{
v->previous->scope == PendingScope)
/* all previous branches used name */
v->scope = PendingScope;
- else if (v->type == Tlabel)
- /* Labels remain pending even when not used */
- v->scope = PendingScope; // UNTESTED
else
v->scope = OutScope;
if (ct == CloseElse) {
v->scope = InScope;
/* fallthrough */
case CloseSequential:
- if (v->type == Tlabel)
- v->scope = PendingScope;
switch (v->scope) {
case InScope:
v->scope = OutScope;
for (v2 = v;
v2 && v2->scope == PendingScope;
v2 = v2->previous)
- if (v2->type == Tlabel)
- v2->scope = CondScope;
- else
- v2->scope = OutScope;
+ v2->scope = OutScope;
break;
case CondScope:
case OutScope: break;
{
if (!v->global) {
if (!c->local || !v->type)
- return NULL; // UNTESTED
+ return NULL; // NOTEST
if (v->frame_pos + v->type->size > c->local_size) {
printf("INVALID frame_pos\n"); // NOTEST
exit(2); // NOTEST
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); // NOTEST
+ c->global_size = (c->global_size + t->align) & ~(t->align-1);
if (!v) {
v = &scratch;
v->type = t;
if (init)
memcpy(ret, init, t->size);
else
- val_init(t, ret);
+ val_init(t, ret); // NOTEST
return ret;
}
if (v->merged != v)
continue;
if (!t)
- continue;
+ continue; // NOTEST
if (v->frame_pos >= 0)
continue;
while (done && done->scope_end < v->scope_start)
return Tnone; // NOTEST
}
v = v->merged;
- if (v->constant && (rules & Rnoconstant)) {
- type_err(c, "error: Cannot assign to a constant: %v",
- prog, NULL, 0, NULL);
- type_err(c, "info: name was defined as a constant here",
- v->where_decl, NULL, 0, NULL);
- return v->type;
- }
if (v->type == Tnone && v->where_decl == prog)
type_err(c, "error: variable used but not declared: %v",
prog, NULL, 0, NULL);
*perr |= Eretry;
}
} else if (!type_compat(type, v->type, rules)) {
- type_err(c, "error: expected %1%r but variable '%v' is %2", prog,
+ type_err(c, "error: expected %1 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;
+ *perr |= Eruntime;
+ if (v->constant)
+ *perr |= Econst;
+ return v->type;
}
###### interp exec cases
Being "complex" the language will naturally have syntax to access
specifics of objects of these types. These will fit into the grammar as
"Terms" which are the things that are combined with various operators to
-form "Expression". Where a Term is formed by some operation on another
+form an "Expression". Where a Term is formed by some operation on another
Term, the subordinate Term will always come first, so for example a
member of an array will be expressed as the Term for the array followed
by an index in square brackets. The strict rule of using postfix
operations makes precedence irrelevant within terms. To provide a place
-to put the grammar for each terms of each type, we will start out by
+to put the grammar for terms of each type, we will start out by
introducing the "Term" grammar production, with contains at least a
simple "Value" (to be explained later).
+We also take this opportunity to introduce the "ExpressionsList" which
+is a simple comma-separated list of expressions - it may be used in
+various places.
+
+###### declare terminals
+ $TERM ,
+
###### Grammar
$*exec
Term -> Value ${ $0 = $<1; }$
| Variable ${ $0 = $<1; }$
## term grammar
+ $*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;
+ }$
+
Thus far the complex types we have are arrays and structs.
#### Arrays
struct value *vsize;
mpz_t q;
if (type->array.static_size)
- return 1; // UNTESTED
+ return 1; // NOTEST - guard against reentry
if (type->array.unspec && parse_time)
- return 1; // UNTESTED
+ return 1; // NOTEST - unspec is still incomplete
if (parse_time && type->array.vsize && !type->array.vsize->global)
- return 1; // UNTESTED
+ return 1; // NOTEST - should be impossible
if (type->array.vsize) {
vsize = var_value(c, type->array.vsize);
if (!vsize)
- return 1; // UNTESTED
+ return 1; // NOTEST - should be impossible
mpz_init(q);
mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
type->array.size = mpz_get_si(q);
if (!parse_time)
return 1;
if (type->array.member->size <= 0)
- return 0; // UNTESTED
+ return 0; // NOTEST - error caught before here
type->array.static_size = 1;
type->size = type->array.size * type->array.member->size;
free(ptr);
}
- static int array_compat(struct type *require, struct type *have)
+ static int array_compat(struct type *require, struct type *have,
+ enum val_rules rules)
{
if (have->compat != require->compat)
return 0;
/* Both are arrays, so we can look at details */
if (!type_compat(require->array.member, have->array.member, 0))
return 0;
- if (have->array.unspec && require->array.unspec) {
- if (have->array.vsize && require->array.vsize &&
- have->array.vsize != require->array.vsize) // UNTESTED
- /* sizes might not be the same */
- return 0; // UNTESTED
- return 1;
- }
+ if (have->array.unspec && require->array.unspec &&
+ have->array.size != require->array.size)
+ return 0; // NOTEST
if (have->array.unspec || require->array.unspec)
- return 1; // UNTESTED
+ return 1;
if (require->array.vsize == NULL && have->array.vsize == NULL)
return require->array.size == have->array.size;
- return require->array.vsize == have->array.vsize; // UNTESTED
+ return require->array.vsize == have->array.vsize;
}
static void array_print_type(struct type *type, FILE *f)
$0->array.vsize = v;
} }$
-###### Grammar
- $*type
- OptType -> Type ${ $0 = $<1; }$
- | ${ $0 = NULL; }$
-
###### formal type grammar
- | [ IDENTIFIER :: OptType ] Type ${ {
- struct variable *v = var_decl(c, $ID.txt);
-
- v->type = $<OT;
- v->constant = 1;
- if (!v->type)
- v->type = Tnum;
- $0 = add_anon_type(c, &array_prototype, "array[var]");
- $0->array.member = $<6;
+ | [ ] Type ${ {
+ $0 = add_anon_type(c, &array_prototype, "array[]");
+ $0->array.member = $<Type;
$0->array.size = 0;
$0->array.unspec = 1;
- $0->array.vsize = v;
+ $0->array.vsize = NULL;
} }$
###### Binode types
- Index,
+ Index, Length,
###### term grammar
$0 = b;
} }$
+ | Term [ ] ${ {
+ struct binode *b = new(binode);
+ b->op = Length;
+ b->left = $<Term;
+ $0 = b;
+ } }$
+
###### print binode cases
case Index:
print_exec(b->left, -1, bracket);
printf("]");
break;
+ case Length:
+ print_exec(b->left, -1, bracket);
+ printf("[]");
+ break;
+
###### propagate binode cases
case Index:
/* left must be an array, right must be a number,
* result is the member type of the array
*/
- propagate_types(b->right, c, perr, Tnum, 0);
- t = propagate_types(b->left, c, perr, NULL, rules & Rnoconstant);
+ propagate_types(b->right, c, perr_local, Tnum, 0);
+ t = propagate_types(b->left, c, perr, NULL, 0);
if (!t || t->compat != array_compat) {
type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL);
return NULL;
}
break;
+ case Length:
+ /* left must be an array, result is a number
+ */
+ t = propagate_types(b->left, c, perr, NULL, 0);
+ if (!t || t->compat != array_compat) {
+ type_err(c, "error: %1 cannot provide length", prog, t, 0, NULL);
+ return NULL;
+ }
+ if (!type_compat(type, Tnum, rules))
+ type_err(c, "error: have %1 but need %2", prog,
+ Tnum, rules, type);
+ return Tnum;
+ break;
+
###### interp binode cases
case Index: {
mpz_t q;
ltype = NULL;
break;
}
+ case Length: {
+ lleft = linterp_exec(c, b->left, <ype);
+ mpq_set_ui(rv.num, ltype->array.size, 1);
+ ltype = NULL;
+ rvtype = Tnum;
+ break;
+ }
#### Structs
###### type functions
void (*print_type_decl)(struct type *type, FILE *f);
+ struct type *(*fieldref)(struct type *t, struct parse_context *c,
+ struct fieldref *f, struct value **vp);
###### value functions
return 1;
}
+ static int find_struct_index(struct type *type, struct text field)
+ {
+ int i;
+ for (i = 0; i < type->structure.nfields; i++)
+ if (text_cmp(type->structure.fields[i].name, field) == 0)
+ return i;
+ return IndexInvalid;
+ }
+
+ static struct type *structure_fieldref(struct type *t, struct parse_context *c,
+ struct fieldref *f, struct value **vp)
+ {
+ if (f->index == IndexUnknown) {
+ f->index = find_struct_index(t, f->name);
+ if (f->index < 0)
+ type_err(c, "error: cannot find requested field in %1",
+ f->left, t, 0, NULL);
+ }
+ if (f->index < 0)
+ return NULL;
+ if (vp) {
+ struct value *v = *vp;
+ v = (void*)v->ptr + t->structure.fields[f->index].offset;
+ *vp = v;
+ }
+ return t->structure.fields[f->index].type;
+ }
+
static struct type structure_prototype = {
.init = structure_init,
.free = structure_free,
.free_type = structure_free_type,
.print_type_decl = structure_print_type,
.prepare_type = structure_prepare_type,
+ .fieldref = structure_fieldref,
};
###### exec type
int index;
struct text name;
};
+ enum { IndexUnknown = -1, IndexInvalid = -2 };
###### free exec cases
case Xfieldref:
break;
###### declare terminals
- $TERM struct .
+ $TERM struct
###### term grammar
struct fieldref *fr = new_pos(fieldref, $2);
fr->left = $<1;
fr->name = $3.txt;
- fr->index = -2;
+ fr->index = IndexUnknown;
$0 = fr;
} }$
break;
}
-###### ast functions
- static int find_struct_index(struct type *type, struct text field)
- {
- int i;
- for (i = 0; i < type->structure.nfields; i++)
- if (text_cmp(type->structure.fields[i].name, field) == 0)
- return i;
- return -1;
- }
-
###### propagate exec cases
case Xfieldref:
struct fieldref *f = cast(fieldref, prog);
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
- NULL, 0, NULL);
- else if (st->init != structure_init)
- type_err(c, "error: field reference attempted on %1, not a struct",
+ if (!st || !st->fieldref)
+ type_err(c, "error: field reference on %1 is not supported",
f->left, st, 0, NULL);
- else if (f->index == -2) {
- f->index = find_struct_index(st, f->name);
- if (f->index < 0)
- type_err(c, "error: cannot find requested field in %1",
- f->left, st, 0, NULL);
- }
- if (f->index >= 0) {
- struct type *ft = st->structure.fields[f->index].type;
- if (!type_compat(type, ft, rules))
+ else {
+ t = st->fieldref(st, c, f, NULL);
+ if (t && !type_compat(type, t, rules))
type_err(c, "error: have %1 but need %2", prog,
- ft, rules, type);
- return ft;
+ t, rules, type);
+ return t;
}
break;
}
struct fieldref *f = cast(fieldref, e);
struct type *ltype;
struct value *lleft = linterp_exec(c, f->left, <ype);
- lrv = (void*)lleft->ptr + ltype->structure.fields[f->index].offset;
- rvtype = ltype->structure.fields[f->index].type;
+ lrv = lleft;
+ rvtype = ltype->fieldref(ltype, c, f, &lrv);
break;
}
###### top level grammar
- DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ {
- struct type *t;
- t = find_type(c, $ID.txt);
- if (!t)
- t = add_type(c, $ID.txt, &structure_prototype);
- else if (t->size >= 0) {
+ $*type
+ StructName -> IDENTIFIER ${ {
+ struct type *t = find_type(c, $ID.txt);
+
+ if (t && 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 = NULL;
}
- t->structure.field_list = $<FB;
+ if (!t)
+ t = add_type(c, $ID.txt, NULL);
t->first_use = $ID;
+ $0 = t;
+ } }$
+ $void
+ DeclareStruct -> struct StructName FieldBlock Newlines ${ {
+ struct type *t = $<SN;
+ struct type tmp = *t;
+
+ *t = structure_prototype;
+ t->name = tmp.name;
+ t->next = tmp.next;
+ t->first_use = tmp.first_use;
+
+ t->structure.field_list = $<FB;
} }$
$*fieldlist
| SimpleFieldList EOL ${ $0 = $<SFL; }$
FieldLines -> SimpleFieldList Newlines ${ $0 = $<SFL; }$
- | FieldLines SimpleFieldList Newlines ${
- $SFL->prev = $<FL;
- $0 = $<SFL;
- }$
+ | FieldLines SimpleFieldList Newlines ${ {
+ struct fieldlist *f = $<SFL;
+
+ if (f) {
+ $0 = f;
+ while (f->prev)
+ f = f->prev;
+ f->prev = $<FL;
+ } else
+ $0 = $<FL;
+ } }$
SimpleFieldList -> Field ${ $0 = $<F; }$
| SimpleFieldList ; Field ${
if (fl->type->print && fl->init) {
fprintf(f, " = ");
if (fl->type == Tstr)
- fprintf(f, "\""); // UNTESTED
+ fprintf(f, "\"");
print_value(fl->type, fl->init, f);
if (fl->type == Tstr)
- fprintf(f, "\""); // UNTESTED
+ fprintf(f, "\"");
}
fprintf(f, "\n");
}
/* Nothing to do here */
}
- static int reference_compat(struct type *require, struct type *have)
+ static int reference_compat(struct type *require, struct type *have,
+ enum val_rules rules)
{
+ if (rules & Rrefok)
+ if (require->reference.referent == have)
+ return 1;
if (have->compat != require->compat)
return 0;
if (have->reference.referent != require->reference.referent)
return val->ref != NULL;
}
+ static struct type *reference_fieldref(struct type *t, struct parse_context *c,
+ struct fieldref *f, struct value **vp)
+ {
+ struct type *rt = t->reference.referent;
+
+ if (rt->fieldref) {
+ if (vp)
+ *vp = (*vp)->ref;
+ return rt->fieldref(rt, c, f, vp);
+ }
+ type_err(c, "error: field reference on %1 is not supported",
+ f->left, rt, 0, NULL);
+ return Tnone;
+ }
+
static struct type reference_prototype = {
.print_type = reference_print_type,
.cmp_eq = reference_cmp,
.test = reference_test,
.free = reference_free,
.compat = reference_compat,
+ .fieldref = reference_fieldref,
.size = sizeof(void*),
.align = sizeof(void*),
};
r->reftype = type;
*perr |= Eretry;
}
+ *perr |= Erval;
return type;
case RefNil:
if (type && type->free != reference_free)
r->reftype = type;
*perr |= Eretry;
}
+ *perr |= Erval;
return type;
case RefFree:
- t = propagate_types(r->right, c, perr, NULL, 0);
+ t = propagate_types(r->right, c, perr_local, NULL, 0);
if (t && t->free != reference_free)
type_err(c, "error: @free can only be assigned a reference, not %1",
prog, t, 0, NULL);
###### Expressions: dereference
###### Binode types
- Deref,
+ Deref, AddressOf,
###### term grammar
print_exec(b->left, -1, bracket);
printf("@");
break;
+ case AddressOf:
+ print_exec(b->left, -1, bracket);
+ break;
###### propagate binode cases
case Deref:
/* left must be a reference, and we return what it refers to */
/* FIXME how can I pass the expected type down? */
t = propagate_types(b->left, c, perr, NULL, 0);
+ *perr &= ~Erval;
if (!t || t->free != reference_free)
type_err(c, "error: Cannot dereference %1", b, t, 0, NULL);
else
return t->reference.referent;
break;
+ case AddressOf:
+ /* left must be lval, we create reference to it */
+ if (!type || type->free != reference_free)
+ t = propagate_types(b->left, c, perr, type, 0); // NOTEST impossible
+ else
+ t = propagate_types(b->left, c, perr,
+ type->reference.referent, 0);
+ if (t)
+ t = find_anon_type(c, &reference_prototype, "@%.*s",
+ t->name.len, t->name.txt);
+ return t;
+
###### interp binode cases
- case Deref: {
+ case Deref:
left = interp_exec(c, b->left, <ype);
lrv = left.ref;
rvtype = ltype->reference.referent;
break;
- }
+
+ case AddressOf:
+ rv.ref = linterp_exec(c, b->left, &rvtype);
+ rvtype = find_anon_type(c, &reference_prototype, "@%.*s",
+ rvtype->name.len, rvtype->name.txt);
+ break;
#### Functions
val->function = NULL;
}
- static int function_compat(struct type *require, struct type *have)
+ static int function_compat(struct type *require, struct type *have,
+ enum val_rules rules)
{
// FIXME can I do anything here yet?
return 0;
}
+ static struct exec *take_addr(struct exec *e)
+ {
+ struct binode *rv = new(binode);
+ rv->op = AddressOf;
+ rv->left = e;
+ return rv;
+ }
+
static void function_check_args(struct parse_context *c, enum prop_err *perr,
struct type *require, struct exec *args)
{
while (param) {
struct var *pv = cast(var, param->left);
+ struct type *t = pv->var->type, *t2;
if (!arg) {
type_err(c, "error: insufficient arguments to function.",
args, NULL, 0, NULL);
break;
}
*perr = 0;
- propagate_types(arg->left, c, perr, pv->var->type, 0);
+ t2 = propagate_types(arg->left, c, perr, t, Rrefok);
+ if (t->free == reference_free &&
+ t->reference.referent == t2 &&
+ !(*perr & Erval)) {
+ arg->left = take_addr(arg->left);
+ } else if (!(*perr & Efail) && !type_compat(t2, t, 0)) {
+ type_err(c, "error: cannot pass rval when reference expected",
+ arg->left, NULL, 0, NULL);
+ }
param = cast(binode, param->right);
arg = cast(binode, arg->right);
}
static void function_print(struct type *type, struct value *val, FILE *f)
{
+ fprintf(f, "\n");
print_exec(val->function, 1, 0);
}
} else
type_print(type->function.return_type, f);
}
- fprintf(f, "\n");
}
static void function_free_type(struct type *t)
$TERM func
-###### Binode types
- List,
-
###### Grammar
$*variable
prog, NULL, 0, NULL);
return NULL;
}
- *perr |= Enoconst;
- v->var->type->check_args(c, perr, v->var->type, args);
+ *perr |= Eruntime;
+ v->var->type->check_args(c, perr_local, v->var->type, args);
if (v->var->type->function.inline_result)
*perr |= Emaycopy;
+ *perr |= Erval;
return v->var->type->function.return_type;
}
struct binode *b2 = cast(binode, b->right);
struct type *t2;
- 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);
+ propagate_types(b->left, c, perr_local, Tbool, 0);
+ t = propagate_types(b2->left, c, perr, type, 0);
+ t2 = propagate_types(b2->right, c, perr, type ?: t, 0);
return t ?: t2;
}
struct binode *b2 = cast(binode, b->right);
left = interp_exec(c, b->left, <ype);
if (left.bool)
- rv = interp_exec(c, b2->left, &rvtype); // UNTESTED
+ rv = interp_exec(c, b2->left, &rvtype);
else
rv = interp_exec(c, b2->right, &rvtype);
}
break;
-### Expression list
-
-We take a brief detour, now that we have expressions, to describe lists
-of expressions. These will be needed for function parameters and
-possibly other situations. They seem generic enough to introduce here
-to be used elsewhere.
-
-And ExpressionList will use the `List` type of `binode`, building up at
-the end. And place where they are used will probably call
-`reorder_bilist()` to get a more normal first/next arrangement.
-
-###### declare terminals
- $TERM ,
-
-`List` execs have no implicit semantics, so they are never propagated or
-interpreted. The can be printed as a comma separate list, which is how
-they are parsed. Note they are also used for function formal parameter
-lists. In that case a separate function is used to print them.
-
-###### print binode cases
- case List:
- while (b) {
- printf(" ");
- print_exec(b->left, -1, bracket);
- if (b->right)
- printf(",");
- b = cast(binode, b->right);
- }
- break;
-
-###### propagate binode cases
- case List: abort(); // NOTEST
-###### interp binode cases
- case List: abort(); // NOTEST
-
-###### Grammar
-
- $*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;
- }$
-
### Expressions: Boolean
The next class of expressions to use the `binode` will be Boolean
-expressions. "`and then`" and "`or else`" are similar to `and` and `or`
-have same corresponding precendence. The difference is that they don't
+expressions. `and` and `or` are short-circuit operators that don't
evaluate the second expression if not necessary.
###### Binode types
And,
- AndThen,
Or,
- OrElse,
Not,
###### declare terminals
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->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;
print_exec(b->right, -1, bracket);
if (bracket) printf(")");
break;
- case AndThen:
- if (bracket) printf("(");
- print_exec(b->left, -1, bracket);
- printf(" and then ");
- print_exec(b->right, -1, bracket);
- if (bracket) printf(")");
- break;
case Or:
if (bracket) printf("(");
print_exec(b->left, -1, bracket);
print_exec(b->right, -1, bracket);
if (bracket) printf(")");
break;
- case OrElse:
- if (bracket) printf("(");
- print_exec(b->left, -1, bracket);
- printf(" or else ");
- print_exec(b->right, -1, bracket);
- if (bracket) printf(")");
- break;
case Not:
if (bracket) printf("(");
printf("not ");
###### propagate binode cases
case And:
- case AndThen:
case Or:
- case OrElse:
case Not:
/* both must be Tbool, result is Tbool */
propagate_types(b->left, c, perr, Tbool, 0);
if (type && type != Tbool)
type_err(c, "error: %1 operation found where %2 expected", prog,
Tbool, 0, type);
+ *perr |= Erval;
return Tbool;
###### interp binode cases
case And:
- rv = interp_exec(c, b->left, &rvtype);
- right = interp_exec(c, b->right, &rtype);
- rv.bool = rv.bool && right.bool;
- break;
- case AndThen:
rv = interp_exec(c, b->left, &rvtype);
if (rv.bool)
rv = interp_exec(c, b->right, NULL);
break;
case Or:
- rv = interp_exec(c, b->left, &rvtype);
- right = interp_exec(c, b->right, &rtype);
- rv.bool = rv.bool || right.bool;
- break;
- case OrElse:
rv = interp_exec(c, b->left, &rvtype);
if (!rv.bool)
rv = interp_exec(c, b->right, NULL);
case Eql:
case NEql:
/* Both must match but not be labels, result is Tbool */
- t = propagate_types(b->left, c, perr, NULL, Rnolabel);
+ t = propagate_types(b->left, c, perr, NULL, 0);
if (t)
propagate_types(b->right, c, perr, t, 0);
else {
- t = propagate_types(b->right, c, perr, NULL, Rnolabel); // UNTESTED
- if (t) // UNTESTED
- t = propagate_types(b->left, c, perr, t, 0); // UNTESTED
+ t = propagate_types(b->right, c, perr, NULL, 0); // NOTEST
+ if (t) // NOTEST
+ t = propagate_types(b->left, c, perr, t, 0); // NOTEST
}
if (!type_compat(type, Tbool, 0))
type_err(c, "error: Comparison returns %1 but %2 expected", prog,
Tbool, rules, type);
+ *perr |= Erval;
return Tbool;
###### interp binode cases
if (bracket) printf(")");
break;
case Bracket:
- printf("(");
+ /* Avoid double brackets... */
+ if (!bracket) printf("(");
print_exec(b->right, indent, bracket);
- printf(")");
+ if (!bracket) printf(")");
break;
###### propagate binode cases
if (!type_compat(type, Tnum, 0))
type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
Tnum, rules, type);
+ *perr |= Erval;
return Tnum;
case Concat:
if (!type_compat(type, Tstr, 0))
type_err(c, "error: Concat returns %1 but %2 expected", prog,
Tstr, rules, type);
+ *perr |= Erval;
return Tstr;
case StringConv:
/* op must be string, result is number */
propagate_types(b->left, c, perr, Tstr, 0);
if (!type_compat(type, Tnum, 0))
- type_err(c, // UNTESTED
+ type_err(c,
"error: Can only convert string to number, not %1",
prog, type, 0, NULL);
+ *perr |= Erval;
return Tnum;
case Test:
if (!t || !t->test)
type_err(c, "error: '?' requires a testable value, not %1",
prog, t, 0, NULL);
+ *perr |= Erval;
return Tbool;
case Choose:
if (t && t->test == NULL)
type_err(c, "error: \"??\" requires a testable value, not %1",
prog, t, 0, NULL);
+ *perr |= Erval;
return t;
case Bracket:
- return propagate_types(b->right, c, perr, type, 0);
+ return propagate_types(b->right, c, perr, type, rules);
###### interp binode cases
rvtype = Tnum;
struct text tx = right.str;
- char tail[3];
+ char tail[3] = "";
int neg = 0;
if (tx.txt[0] == '-') {
- neg = 1; // UNTESTED
- tx.txt++; // UNTESTED
- tx.len--; // UNTESTED
+ neg = 1;
+ tx.txt++;
+ tx.len--;
}
if (number_parse(rv.num, tail, tx) == 0)
- mpq_init(rv.num); // UNTESTED
+ mpq_init(rv.num);
else if (neg)
- mpq_neg(rv.num, rv.num); // UNTESTED
+ mpq_neg(rv.num, rv.num);
if (tail[0])
- printf("Unsupported suffix: %.*s\n", tx.len, tx.txt); // UNTESTED
+ printf("Unsupported suffix: %.*s\n", tx.len, tx.txt);
break;
case Test:
Block -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
| { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
| SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
- | SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+ | SimpleStatements EOL ${ $0 = reorder_bilist($<SS);
+ }$
| IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
OpenBlock -> OpenScope { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
| OpenScope SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
| IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
- UseBlock -> { OpenScope IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
+ UseBlock -> { IN OpenScope OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
| { OpenScope SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
| IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
ComplexStatements -> ComplexStatements ComplexStatement ${
if ($2 == NULL) {
- $0 = $<1;
+ $0 = $<1; // NOTEST - impossible
} else {
$0 = new(binode);
$0->op = Block;
}$
| ComplexStatement ${
if ($1 == NULL) {
- $0 = NULL;
+ $0 = NULL; // NOTEST - impossible
} else {
$0 = new(binode);
$0->op = Block;
###### print binode cases
case Block:
- if (indent < 0) {
- // simple statement
- if (b->left == NULL) // UNTESTED
- printf("pass"); // UNTESTED
- else
- print_exec(b->left, indent, bracket); // UNTESTED
- if (b->right) { // UNTESTED
- printf("; "); // UNTESTED
- print_exec(b->right, indent, bracket); // UNTESTED
- }
- } else {
- // block, one per line
- if (b->left == NULL)
- do_indent(indent, "pass\n");
- else
- print_exec(b->left, indent, bracket);
- if (b->right)
- print_exec(b->right, indent, bracket);
- }
+ // block, one per line
+ if (b->left == NULL)
+ do_indent(indent, "pass\n");
+ else
+ print_exec(b->left, indent, bracket);
+ if (b->right)
+ print_exec(b->right, indent, bracket);
break;
###### propagate binode cases
struct binode *e;
for (e = b; e; e = cast(binode, e->right)) {
- t = propagate_types(e->left, c, perr, NULL, rules);
+ *perr |= *perr_local;
+ *perr_local = 0;
+ t = propagate_types(e->left, c, perr_local, NULL, rules);
if ((rules & Rboolok) && (t == Tbool || t == Tnone))
t = NULL;
if (t == Tnone && e->right)
if (!type)
type = t;
else if (t != type)
- type_err(c, "error: expected %1%r, found %2",
+ type_err(c, "error: expected %1, found %2",
e->left, type, rules, t);
}
}
case Print:
do_indent(indent, "print");
- if (b->right) {
- print_exec(b->right, -1, bracket);
+ b2 = cast(binode, b->left ?: b->right);
+ while (b2) {
+ printf(" ");
+ print_exec(b2->left, -1, bracket);
+ if (b2->right)
+ printf(",");
+ b2 = cast(binode, b2->right);
+ }
+ if (b->right)
printf(",");
- } else
- print_exec(b->left, -1, bracket);
if (indent >= 0)
printf("\n");
break;
else
b = cast(binode, b->right);
while (b) {
- propagate_types(b->left, c, perr, NULL, Rnolabel);
+ propagate_types(b->left, c, perr_local, NULL, 0);
b = cast(binode, b->right);
}
break;
been declared as a constant. The analysis phase ensures that the type
will be correct so the interpreter just needs to perform the
calculation. There is a form of assignment which declares a new
-variable as well as assigning a value. If a name is assigned before
-it is declared, and error will be raised as the name is created as
-`Tlabel` and it is illegal to assign to such names.
+variable as well as assigning a value. If a name is used before
+it is declared, it is assumed to be a global constant which are allowed to
+be declared at any time.
###### Binode types
Assign,
case Assign:
case Declare:
- /* Both must match and not be labels,
+ /* Both must match, or left may be ref and right an lval
* Type must support 'dup',
* For Assign, left must not be constant.
* result is Tnone
*/
- t = propagate_types(b->left, c, perr, NULL,
- Rnolabel | (b->op == Assign ? Rnoconstant : 0));
+ *perr &= ~(Erval | Econst);
+ t = propagate_types(b->left, c, perr, NULL, 0);
if (!b->right)
return Tnone;
if (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);
+ struct type *t2 = propagate_types(b->right, c, perr_local,
+ t, Rrefok);
+ if (!t2 || t2 == t || (*perr_local & Efail))
+ ; // No more effort needed
+ else if (t->free == reference_free &&
+ t->reference.referent == t2 &&
+ !(*perr_local & Erval))
+ b->right = take_addr(b->right);
+ else if (t->free == reference_free &&
+ t->reference.referent == t2 &&
+ (*perr_local & Erval))
+ type_err(c, "error: Cannot assign an rval to a reference.",
+ b, NULL, 0, NULL);
} else {
- t = propagate_types(b->right, c, perr, NULL, Rnolabel);
+ t = propagate_types(b->right, c, perr_local, NULL, 0);
if (t)
- propagate_types(b->left, c, perr, t,
- (b->op == Assign ? Rnoconstant : 0));
+ propagate_types(b->left, c, perr, t, 0);
}
- if (t && t->dup == NULL && !(*perr & Emaycopy))
+ if (*perr & Erval)
+ type_err(c, "error: cannot assign to an rval", b,
+ NULL, 0, NULL);
+ else if (b->op == Assign && (*perr & Econst)) {
+ type_err(c, "error: Cannot assign to a constant: %v",
+ b->left, NULL, 0, NULL);
+ if (b->left->type == Xvar) {
+ struct var *var = cast(var, b->left);
+ struct variable *v = var->var;
+ type_err(c, "info: name was defined as a constant here",
+ v->where_decl, NULL, 0, NULL);
+ }
+ }
+ if (t && t->dup == NULL && !(*perr_local & Emaycopy))
type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL);
+ if (b->left->type == Xvar && (*perr_local & Efail))
+ type_err(c, "info: variable '%v' was set as %1 here.",
+ cast(var, b->left)->var->where_set, t, rules, NULL);
return Tnone;
break;
val = var_value(c, v);
if (v->type->prepare_type)
v->type->prepare_type(c, v->type, 0);
- if (b->right)
- dinterp_exec(c, b->right, val, v->type, 0);
- else
+ if (!b->right)
val_init(v->type, val);
+ else
+ dinterp_exec(c, b->right, val, v->type, 0);
break;
}
$0 = b = new_pos(binode, $1);
b->op = Use;
b->right = $<2;
- if (b->right->type == Xvar) {
- struct var *v = cast(var, b->right);
- if (v->var->type == Tnone) {
- /* Convert this to a label */
- struct value *val;
-
- v->var->type = Tlabel;
- val = global_alloc(c, Tlabel, v->var, NULL);
- val->label = val;
- }
- }
}$
###### print binode cases
###### propagate binode cases
case Loop:
- t = propagate_types(b->right, c, perr, Tnone, 0);
- if (!type_compat(Tnone, t, 0))
- *perr |= Efail; // UNTESTED
+ propagate_types(b->right, c, perr_local, Tnone, 0);
return propagate_types(b->left, c, perr, type, rules);
###### propagate exec cases
struct casepart *cp;
t = propagate_types(cs->forpart, c, perr, Tnone, 0);
- if (!type_compat(Tnone, t, 0))
- *perr |= Efail; // UNTESTED
if (cs->looppart) {
t = propagate_types(cs->thenpart, c, perr, Tnone, 0);
- if (!type_compat(Tnone, t, 0))
- *perr |= Efail; // UNTESTED
}
if (cs->casepart == NULL) {
propagate_types(cs->condpart, c, perr, Tbool, 0);
cp && !t; cp = cp->next)
t = propagate_types(cp->value, c, perr, NULL, 0);
if (!t && cs->condpart)
- t = propagate_types(cs->condpart, c, perr, NULL, Rboolok); // UNTESTED
+ t = propagate_types(cs->condpart, c, perr, NULL, Rboolok); // NOTEST
if (!t && cs->looppart)
- t = propagate_types(cs->looppart, c, perr, NULL, Rboolok); // UNTESTED
+ t = propagate_types(cs->looppart, c, perr, NULL, Rboolok); // NOTEST
// Now we have a type (I hope) push it down
if (t) {
for (cp = cs->casepart; cp; cp = cp->next)
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, perr, NULL, rules); // UNTESTED
+ cp = cp->next) // NOTEST
+ type = propagate_types(cp->action, c, perr, NULL, rules); // NOTEST
if (type) {
if (!cs->looppart)
propagate_types(cs->thenpart, c, perr, type, rules);
| DeclarationList Declaration
Declaration -> ERROR Newlines ${
- tok_err(c, // UNTESTED
+ tok_err(c, // NOTEST
"error: unhandled parse error", &$1);
}$
| DeclareConstant
} while (perr & Eretry);
if (perr & Efail)
c->parse_error += 1;
- else if (!(perr & Enoconst)) {
+ else if (!(perr & Eruntime)) {
progress = some;
struct value res = interp_exec(
c, vb->right, &v->var->type);
struct value fn = {.function = code};
struct type *t;
var_block_close(c, CloseFunction, code);
- t = add_anon_type(c, &function_prototype,
- "func %.*s", name->name->name.len,
+ 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);
struct value *val = var_value(&context, v);
printf("func %.*s", v->name->name.len, v->name->name.txt);
v->type->print_type_decl(v->type, stdout);
- if (brackets)
- print_exec(val->function, 0, brackets);
- else
+ if (brackets) {
+ printf(" {\n");
+ print_exec(val->function, 1, brackets);
+ printf("}\n");
+ } else {
print_value(v->type, val, stdout);
+ }
printf("/* frame size %d */\n", v->type->function.local_size);
target -= 1;
}
all_ok = 0;
if (!v->type->function.inline_result &&
!v->type->function.return_type->dup) {
- type_err(c, "error: function cannot return value of type %1",
+ type_err(c, "error: function cannot return value of type %1",
v->where_decl, v->type->function.return_type, 0, NULL);
}
struct value *vl = var_value(c, v->var);
struct value arg;
struct type *t;
- mpq_t argcq;
int i;
switch (anum++) {
case 0: /* argv */
t = v->var->type;
- mpq_init(argcq);
- mpq_set_ui(argcq, argc, 1);
- memcpy(var_value(c, t->array.vsize), &argcq, sizeof(argcq));
+ t->array.size = argc;
t->prepare_type(c, t, 0);
array_init(v->var->type, vl);
for (i = 0; i < argc; i++) {
name:string
alive:Boolean
- func main(argv:[argc::]string)
+ func main(argv:[]string)
print "Hello World, what lovely oceans you have!"
print "Are there", five, "?"
print pi, pie, "but", cake
a : number
a = A;
b:number = B
- if a > 0 and then b > 0:
+ if a > 0 and b > 0:
while a != b:
if a < b:
b = b - a
while
mid := (lo + hi) / 2
if mid == target:
- use Found
+ use .Found
if mid < target:
lo = mid
else
hi = mid
if hi - lo < 1:
lo = mid
- use GiveUp
+ use .GiveUp
use True
do pass
- case Found:
+ case .Found:
print "Yay, I found", target
- case GiveUp:
+ case .GiveUp:
print "Closest I found was", lo
size::= 10