Instead Vnolabel as a type, change "bool_permitted" to
a set of rules (Rboolok) and add a new rule: Rnolabel.
This requires changes to type_err() and elsewhere.
Signed-off-by: NeilBrown <neil@brown.name>
language (a `struct exec` which has not yet been introduced), and 2
types. "`$1`" reports the first type, "`$2`" reports the second. We
will need a function to print the location, once we know how that is
language (a `struct exec` which has not yet been introduced), and 2
types. "`$1`" reports the first type, "`$2`" reports the second. We
will need a function to print the location, once we know how that is
+stored. As will be explained later, there are sometimes extra rules for
+type matching and they might affect error messages, we need to pass those
+in too.
static void type_err(struct parse_context *c,
char *fmt, struct exec *loc,
static void type_err(struct parse_context *c,
char *fmt, struct exec *loc,
- enum vtype t1, enum vtype t2)
+ enum vtype t1, int rules, enum vtype t2)
{
fprintf(stderr, "%s:", c->file_name);
fput_loc(loc, stderr);
{
fprintf(stderr, "%s:", c->file_name);
fput_loc(loc, stderr);
Values can be numbers, which we represent as multi-precision
fractions, strings, Booleans and labels. When analysing the program
we also need to allow for places where no value is meaningful
Values can be numbers, which we represent as multi-precision
fractions, strings, Booleans and labels. When analysing the program
we also need to allow for places where no value is meaningful
-(`Vnone`) and where we don't know what type to expect yet (`Vunknown`
-which can be anything and `Vnolabel` which can be anything except a
-label).
+(`Vnone`) and where we don't know what type to expect yet (`Vunknown`).
Values are never shared, they are always copied when used, and freed
when no longer needed.
When propagating type information around the program, we need to
determine if two types are compatible, where `Vunknown` is compatible
Values are never shared, they are always copied when used, and freed
when no longer needed.
When propagating type information around the program, we need to
determine if two types are compatible, where `Vunknown` is compatible
-with anything, and `Vnolabel` is compatible with anything except a
-label. A separate function to encode this rule will simplify some code
-later.
-
-There is an extra complication that this function needs to handle,
-which is described later when the Conditional Statement is introduced.
-In certain cases where a particular type is generally expected, a
-Boolean is also alway permitted. To handle those cases, we explicitly tell
-`vtype_compat()` is a Boolean is permitted.
+with anything. There are two special cases with type compatibility,
+both related to the Conditional Statement which will be described
+later. In some cases a Boolean can be accepted as well as some other
+primary type, and in others any type is acceptable except a label (`Vlabel`).
+A separate function encode these cases will simplify some code later.
When assigning command line arguments to variables, we need to be able
to parse each type from a string.
When assigning command line arguments to variables, we need to be able
to parse each type from a string.
###### ast
struct value {
###### ast
struct value {
- enum vtype {Vnolabel, Vunknown, Vnone, Vstr, Vnum, Vbool, Vlabel} vtype;
+ enum vtype {Vunknown, Vnone, Vstr, Vnum, Vbool, Vlabel} vtype;
union {
struct text str;
mpq_t num;
union {
struct text str;
mpq_t num;
- char *vtype_names[] = {"nolabel", "unknown", "none", "string",
+ enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1};
+
+ char *vtype_names[] = {"unknown", "none", "string",
"number", "Boolean", "label"};
"number", "Boolean", "label"};
+###### format cases
+ case 'r':
+ if (rules & Rnolabel)
+ fputs(" (labels not permitted)", stderr);
+ break;
+
###### ast functions
static void free_value(struct value v)
{
switch (v.vtype) {
case Vnone:
###### ast functions
static void free_value(struct value v)
{
switch (v.vtype) {
case Vnone:
case Vunknown: break;
case Vstr: free(v.str.txt); break;
case Vnum: mpq_clear(v.num); break;
case Vunknown: break;
case Vstr: free(v.str.txt); break;
case Vnum: mpq_clear(v.num); break;
- static int vtype_compat(enum vtype require, enum vtype have, int bool_permitted)
+ static int vtype_compat(enum vtype require, enum vtype have, int rules)
- if (bool_permitted && have == Vbool)
+ if ((rules & Rboolok) && have == Vbool)
- switch (require) {
- case Vnolabel:
- return have != Vlabel;
- case Vunknown:
+ if ((rules & Rnolabel) && have == Vlabel)
+ return 0;
+ if (require == Vunknown || have == Vunknown)
- default:
- return have == Vunknown || require == have;
- }
+
+ return require == have;
val->vtype = type;
switch(type) {
case Vnone:abort();
val->vtype = type;
switch(type) {
case Vnone:abort();
case Vunknown: break;
case Vnum:
mpq_init(val->num); break;
case Vunknown: break;
case Vnum:
mpq_init(val->num); break;
rv.vtype = v.vtype;
switch (rv.vtype) {
case Vnone:
rv.vtype = v.vtype;
switch (rv.vtype) {
case Vnone:
case Vunknown: break;
case Vlabel:
rv.label = v.label;
case Vunknown: break;
case Vlabel:
rv.label = v.label;
case Vstr: cmp = text_cmp(left.str, right.str); break;
case Vbool: cmp = left.bool - right.bool; break;
case Vnone:
case Vstr: cmp = text_cmp(left.str, right.str); break;
case Vbool: cmp = left.bool - right.bool; break;
case Vnone:
case Vunknown: cmp = 0;
}
return cmp;
case Vunknown: cmp = 0;
}
return cmp;
case Vunknown:
printf("*Unknown*"); break;
case Vnone:
case Vunknown:
printf("*Unknown*"); break;
case Vnone:
printf("*no-value*"); break;
case Vlabel:
printf("*label-%p*", v.label); break;
printf("*no-value*"); break;
case Vlabel:
printf("*label-%p*", v.label); break;
char tail[3] = "";
switch(vl->vtype) {
char tail[3] = "";
switch(vl->vtype) {
case Vlabel:
case Vunknown:
case Vnone:
case Vlabel:
case Vunknown:
case Vnone:
the program and looking for errors.
So `propagate_types` is passed an expected type (being a `vtype`
the program and looking for errors.
So `propagate_types` is passed an expected type (being a `vtype`
-together with a `bool_permitted` flag) that the `exec` is expected to
+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 `Vunknown`. 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
return, and returns the type that it does return, either of which can
be `Vunknown`. 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
###### core functions
static enum vtype propagate_types(struct exec *prog, struct parse_context *c, int *ok,
###### core functions
static enum vtype propagate_types(struct exec *prog, struct parse_context *c, int *ok,
- enum vtype type, int bool_permitted)
+ enum vtype type, int rules)
case Xval:
{
struct val *val = cast(val, prog);
case Xval:
{
struct val *val = cast(val, prog);
- if (!vtype_compat(type, val->val.vtype, bool_permitted)) {
- type_err(c, "error: expected %1 found %2",
- prog, type, val->val.vtype);
+ if (!vtype_compat(type, val->val.vtype, rules)) {
+ type_err(c, "error: expected %1%r found %2",
+ prog, type, rules, val->val.vtype);
*ok = 0;
}
return val->val.vtype;
*ok = 0;
}
return val->val.vtype;
v = var_ref(config2context(config), $1.txt);
$0->var = v;
type_err(config2context(config), "error: variable '%v' redeclared",
v = var_ref(config2context(config), $1.txt);
$0->var = v;
type_err(config2context(config), "error: variable '%v' redeclared",
type_err(config2context(config), "info: this is where '%v' was first declared",
type_err(config2context(config), "info: this is where '%v' was first declared",
- v->where_decl, Vnone, Vnone);
+ v->where_decl, Vnone, 0, Vnone);
}
} }$
| IDENTIFIER ::= ${ {
}
} }$
| IDENTIFIER ::= ${ {
v = var_ref(config2context(config), $1.txt);
$0->var = v;
type_err(config2context(config), "error: variable '%v' redeclared",
v = var_ref(config2context(config), $1.txt);
$0->var = v;
type_err(config2context(config), "error: variable '%v' redeclared",
type_err(config2context(config), "info: this is where '%v' was first declared",
type_err(config2context(config), "info: this is where '%v' was first declared",
- v->where_decl, Vnone, Vnone);
+ v->where_decl, Vnone, 0, Vnone);
struct var *var = cast(var, prog);
struct variable *v = var->var;
if (!v) {
struct var *var = cast(var, prog);
struct variable *v = var->var;
if (!v) {
- type_err(c, "%d:BUG: no variable!!", prog, Vnone, Vnone);
+ type_err(c, "%d:BUG: no variable!!", prog, Vnone, 0, Vnone);
*ok = 0;
return Vnone;
}
if (v->merged)
v = v->merged;
if (v->val.vtype == Vunknown) {
*ok = 0;
return Vnone;
}
if (v->merged)
v = v->merged;
if (v->val.vtype == Vunknown) {
- if (type > Vunknown && *ok != 0) {
+ if (type != Vunknown && *ok != 0) {
val_init(&v->val, type);
v->where_set = prog;
*ok = 2;
}
return type;
}
val_init(&v->val, type);
v->where_set = prog;
*ok = 2;
}
return type;
}
- if (!vtype_compat(type, v->val.vtype, bool_permitted)) {
- type_err(c, "error: expected %1 but variable '%v' is %2", prog,
- type, v->val.vtype);
+ if (!vtype_compat(type, v->val.vtype, rules)) {
+ type_err(c, "error: expected %1%r but variable '%v' is %2", prog,
+ type, rules, v->val.vtype);
type_err(c, "info: this is where '%v' was set to %1", v->where_set,
type_err(c, "info: this is where '%v' was set to %1", v->where_set,
+ v->val.vtype, rules, Vnone);
return v->val.vtype;
return type;
}
return v->val.vtype;
return type;
}
/* both must be Vbool, result is Vbool */
propagate_types(b->left, c, ok, Vbool, 0);
propagate_types(b->right, c, ok, Vbool, 0);
/* both must be Vbool, result is Vbool */
propagate_types(b->left, c, ok, Vbool, 0);
propagate_types(b->right, c, ok, Vbool, 0);
- if (type != Vbool && type > Vunknown) {
+ if (type != Vbool && type != Vunknown) {
type_err(c, "error: %1 operation found where %2 expected", prog,
type_err(c, "error: %1 operation found where %2 expected", prog,
case Eql:
case NEql:
/* Both must match but not labels, result is Vbool */
case Eql:
case NEql:
/* Both must match but not labels, result is Vbool */
- t = propagate_types(b->left, c, ok, Vnolabel, 0);
- if (t > Vunknown)
+ t = propagate_types(b->left, c, ok, Vunknown, Rnolabel);
+ if (t != Vunknown)
propagate_types(b->right, c, ok, t, 0);
else {
propagate_types(b->right, c, ok, t, 0);
else {
- t = propagate_types(b->right, c, ok, Vnolabel, 0);
- if (t > Vunknown)
+ t = propagate_types(b->right, c, ok, Vunknown, Rnolabel);
+ if (t != Vunknown)
t = propagate_types(b->left, c, ok, t, 0);
}
if (!vtype_compat(type, Vbool, 0)) {
type_err(c, "error: Comparison returns %1 but %2 expected", prog,
t = propagate_types(b->left, c, ok, t, 0);
}
if (!vtype_compat(type, Vbool, 0)) {
type_err(c, "error: Comparison returns %1 but %2 expected", prog,
propagate_types(b->right, c, ok, Vnum, 0);
if (!vtype_compat(type, Vnum, 0)) {
type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
propagate_types(b->right, c, ok, Vnum, 0);
if (!vtype_compat(type, Vnum, 0)) {
type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
propagate_types(b->right, c, ok, Vstr, 0);
if (!vtype_compat(type, Vstr, 0)) {
type_err(c, "error: Concat returns %1 but %2 expected", prog,
propagate_types(b->right, c, ok, Vstr, 0);
if (!vtype_compat(type, Vstr, 0)) {
type_err(c, "error: Concat returns %1 but %2 expected", prog,
struct binode *e;
for (e = b; e; e = cast(binode, e->right)) {
struct binode *e;
for (e = b; e; e = cast(binode, e->right)) {
- t = propagate_types(e->left, c, ok, Vunknown, bool_permitted);
- if (bool_permitted && t == Vbool)
+ t = propagate_types(e->left, c, ok, Vunknown, rules);
+ if ((rules & Rboolok) && t == Vbool)
t = Vunknown;
if (t != Vunknown && t != Vnone && t != Vbool) {
if (type == Vunknown)
type = t;
else if (t != type) {
t = Vunknown;
if (t != Vunknown && t != Vnone && t != Vbool) {
if (type == Vunknown)
type = t;
else if (t != type) {
- type_err(c, "error: expected %1, found %2",
- e->left, type, t);
+ type_err(c, "error: expected %1%r, found %2",
+ e->left, type, rules, t);
case Print:
/* don't care but all must be consistent */
case Print:
/* don't care but all must be consistent */
- propagate_types(b->left, c, ok, Vnolabel, 0);
- propagate_types(b->right, c, ok, Vnolabel, 0);
+ propagate_types(b->left, c, ok, Vunknown, Rnolabel);
+ propagate_types(b->right, c, ok, Vunknown, Rnolabel);
break;
###### interp binode cases
break;
###### interp binode cases
case Assign:
case Declare:
/* Both must match and not be labels, result is Vnone */
case Assign:
case Declare:
/* Both must match and not be labels, result is Vnone */
- t = propagate_types(b->left, c, ok, Vnolabel, 0);
- if (t > Vunknown) {
+ t = propagate_types(b->left, c, ok, Vunknown, Rnolabel);
+ if (t != Vunknown) {
if (propagate_types(b->right, c, ok, t, 0) != t)
if (b->left->type == Xvar)
type_err(c, "info: variable '%v' was set as %1 here.",
if (propagate_types(b->right, c, ok, 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, Vnone);
+ cast(var, b->left)->var->where_set, t, rules, Vnone);
- t = propagate_types(b->right, c, ok, Vnolabel, 0);
- if (t > Vunknown)
+ t = propagate_types(b->right, c, ok, Vunknown, Rnolabel);
+ if (t != Vunknown)
propagate_types(b->left, c, ok, t, 0);
}
return Vnone;
propagate_types(b->left, c, ok, t, 0);
}
return Vnone;
return a Boolean, or the `switchpart` code block which is expected to
return the same type as the casepart values. The correct analysis of
the type of the `whilepart` code block is the reason for the
return a Boolean, or the `switchpart` code block which is expected to
return the same type as the casepart values. The correct analysis of
the type of the `whilepart` code block is the reason for the
-`bool_permitted` flag which is passed to `propagate_types()`.
+`Rboolok` flag which is passed to `propagate_types()`.
The `cond_statement` cannot fit into a `binode` so a new `exec` is
defined.
The `cond_statement` cannot fit into a `binode` so a new `exec` is
defined.
cp && (t == Vunknown); cp = cp->next)
t = propagate_types(cp->value, c, ok, Vunknown, 0);
if (t == Vunknown && cs->condpart)
cp && (t == Vunknown); cp = cp->next)
t = propagate_types(cp->value, c, ok, Vunknown, 0);
if (t == Vunknown && cs->condpart)
- t = propagate_types(cs->condpart, c, ok, Vunknown, 1);
+ t = propagate_types(cs->condpart, c, ok, Vunknown, Rboolok);
// Now we have a type (I hope) push it down
if (t != Vunknown) {
for (cp = cs->casepart; cp; cp = cp->next)
propagate_types(cp->value, c, ok, t, 0);
// Now we have a type (I hope) push it down
if (t != Vunknown) {
for (cp = cs->casepart; cp; cp = cp->next)
propagate_types(cp->value, c, ok, t, 0);
- propagate_types(cs->condpart, c, ok, t, 1);
+ propagate_types(cs->condpart, c, ok, t, Rboolok);
}
}
// (if)then, else, and case parts must return expected type.
if (!cs->dopart && type == Vunknown)
}
}
// (if)then, else, and case parts must return expected type.
if (!cs->dopart && type == Vunknown)
- type = propagate_types(cs->thenpart, c, ok, Vunknown, bool_permitted);
+ type = propagate_types(cs->thenpart, c, ok, Vunknown, rules);
- type = propagate_types(cs->elsepart, c, ok, Vunknown, bool_permitted);
+ type = propagate_types(cs->elsepart, c, ok, Vunknown, rules);
for (cp = cs->casepart;
cp && type == Vunknown;
cp = cp->next)
for (cp = cs->casepart;
cp && type == Vunknown;
cp = cp->next)
- type = propagate_types(cp->action, c, ok, Vunknown, bool_permitted);
- if (type > Vunknown) {
+ type = propagate_types(cp->action, c, ok, Vunknown, rules);
+ if (type != Vunknown) {
- propagate_types(cs->thenpart, c, ok, type, bool_permitted);
- propagate_types(cs->elsepart, c, ok, type, bool_permitted);
+ propagate_types(cs->thenpart, c, ok, type, rules);
+ propagate_types(cs->elsepart, c, ok, type, rules);
for (cp = cs->casepart; cp ; cp = cp->next)
for (cp = cs->casepart; cp ; cp = cp->next)
- propagate_types(cp->action, c, ok, type, bool_permitted);
+ propagate_types(cp->action, c, ok, type, rules);
return type;
} else
return Vunknown;
return type;
} else
return Vunknown;