struct parse_context *c = config2context(config);
###### Parser: code
-
+ #define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
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) {
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);
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
}
}
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.
if (loc->type == Xbinode)
return __fput_loc(cast(binode,loc)->left, f) ||
__fput_loc(cast(binode,loc)->right, f); // NOTEST
- return 0;
+ return 0; // NOTEST
}
static void fput_loc(struct exec *loc, FILE *f)
{
if (!__fput_loc(loc, f))
- fprintf(f, "??:??: ");
+ fprintf(f, "??:??: "); // NOTEST
}
Each different type of `exec` node needs a number of functions defined,
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':
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;
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;
}
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 {
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 {
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,
int (*cmp_eq)(struct type *t1, struct type *t2,
struct value *v1, struct value *v2);
void (*dup)(struct type *type, struct value *vold, struct value *vnew);
+ 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);
struct type *typelist;
+###### includes
+ #include <stdarg.h>
+
###### 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(t.txt);
+ return _add_type(c, t, proto, 1);
+ }
+
static void free_type(struct type *t)
{
/* The type is always a reference to something in the
{
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 if (type->name.len && type->anon)
+ fprintf(f, "\"%.*s\"", type->name.len, type->name.txt);
else
fputs("*invalid*type*", f); // NOTEST
}
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);
context.typelist = t->next;
if (t->free_type)
t->free_type(t);
+ if (t->anon)
+ free(t->name.txt);
free(t);
}
$*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
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;
}
static void _free_value(struct type *type, struct value *v);
+ static int bool_test(struct type *type, struct value *v)
+ {
+ return v->bool;
+ }
+
static struct type base_prototype = {
.init = _val_init,
.print = _print_value,
###### context initialization
Tbool = add_base_type(&context, "Boolean", Vbool, sizeof(char));
+ Tbool->test = bool_test;
Tstr = add_base_type(&context, "string", Vstr, sizeof(struct text));
Tnum = add_base_type(&context, "number", Vnum, sizeof(mpq_t));
Tnone = add_base_type(&context, "none", Vnone, 0);
$*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:
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("\"");
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);
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
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;
v->where_decl, NULL, 0, NULL);
}
} }$
- | IDENTIFIER :: ${ {
+ | IDENTIFIER :: ${ {
struct variable *v = var_decl(c, $1.txt);
$0 = new_pos(var, $1);
$0->var = v;
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;
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;
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;
}
}
cast(var, $0)->var = v;
- } }$
+ } }$
###### print exec cases
case Xvar:
} else
fputs("???", stderr); // NOTEST
} else
- fputs("NOTVAR", stderr);
+ fputs("NOTVAR", stderr); // NOTEST
break;
###### propagate exec cases
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;
###### 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.
###### 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; // UNTESTED
+
+ 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)
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);
}
| [ 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);
&$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;
| [ IDENTIFIER :: OptType ] Type ${ {
struct variable *v = var_decl(c, $ID.txt);
- struct text noname = { "", 0 };
v->type = $<OT;
v->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;
/* 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;
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
}
}
+ 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;
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 = {
.free = structure_free,
.free_type = structure_free_type,
.print_type_decl = structure_print_type,
+ .prepare_type = structure_prepare_type,
};
###### exec type
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
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 = $<FB;
+ t->first_use = $ID;
+ } }$
$*fieldlist
FieldBlock -> { IN OptNL FieldLines OUT OptNL } ${ $0 = $<FL; }$
- | { SimpleFieldList } ${ $0 = $<SFL; }$
- | IN OptNL FieldLines OUT ${ $0 = $<FL; }$
- | SimpleFieldList EOL ${ $0 = $<SFL; }$
+ | { SimpleFieldList } ${ $0 = $<SFL; }$
+ | IN OptNL FieldLines OUT ${ $0 = $<FL; }$
+ | SimpleFieldList EOL ${ $0 = $<SFL; }$
FieldLines -> SimpleFieldList Newlines ${ $0 = $<SFL; }$
- | FieldLines SimpleFieldList Newlines ${
- $SFL->prev = $<FL;
- $0 = $<SFL;
- }$
+ | FieldLines SimpleFieldList Newlines ${
+ $SFL->prev = $<FL;
+ $0 = $<SFL;
+ }$
SimpleFieldList -> Field ${ $0 = $<F; }$
- | SimpleFieldList ; Field ${
- $F->prev = $<SFL;
- $0 = $<F;
- }$
- | SimpleFieldList ; ${
- $0 = $<SFL;
- }$
- | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
+ | SimpleFieldList ; Field ${
+ $F->prev = $<SFL;
+ $0 = $<F;
+ }$
+ | SimpleFieldList ; ${
+ $0 = $<SFL;
+ }$
+ | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
Field -> 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 = $<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;
+ }$
###### forward decls
static void structure_print_type(struct type *t, FILE *f);
while (target != 0) {
int i = 0;
for (t = context.typelist; t ; t=t->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;
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
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
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);
}
$*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;
+ v->where_set = 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 = $<AL; }$
- | Args ArgsLine NEWLINE ${ {
- struct binode *b = $<AL;
- struct binode **bp = &b;
- while (*bp)
- bp = (struct binode **)&(*bp)->left;
- *bp = $<A;
- $0 = b;
- } }$
+ | Args ArgsLine NEWLINE ${ {
+ struct binode *b = $<AL;
+ struct binode **bp = &b;
+ while (*bp)
+ bp = (struct binode **)&(*bp)->left;
+ *bp = $<A;
+ $0 = b;
+ } }$
ArgsLine -> ${ $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 = $<Vl;
- $0->right = $<AD;
- }$
- | ArgDecl ${
- $0 = new(binode);
- $0->op = List;
- $0->left = NULL;
- $0->right = $<AD;
- }$
+ $0 = new_pos(binode, $2);
+ $0->op = List;
+ $0->left = $<Vl;
+ $0->right = $<AD;
+ }$
+ | ArgDecl ${
+ $0 = new(binode);
+ $0->op = List;
+ $0->left = NULL;
+ $0->right = $<AD;
+ }$
$*var
ArgDecl -> IDENTIFIER : FormalType ${ {
- struct variable *v = var_decl(c, $1.txt);
- $0 = new(var);
+ struct variable *v = var_decl(c, $ID.txt);
+ $0 = new_pos(var, $ID);
$0->var = v;
+ v->where_decl = $0;
+ v->where_set = $0;
v->type = $<FT;
} }$
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;
}
$*exec
Expression -> Term ${ $0 = $<Term; }$
- ## expression grammar
+ ## expression grammar
### Expressions: Conditional
struct binode *b2 = cast(binode, b->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;
}
$*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
$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:
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);
###### 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
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,
### Expressions: Arithmetic etc.
The remaining expressions with the highest precedence are arithmetic,
-string concatenation, and string conversion. String concatenation
+string concatenation, string conversion, and testing. String concatenation
(`++`) has the same precedence as multiplication and division, but lower
than the uniary.
+Testing comes in two forms. A single question mark (`?`) is a uniary
+operator which converts come types into Boolean. The general meaning is
+"is this a value value" and there will be more uses as the language
+develops. A double questionmark (`??`) is a binary operator (Choose),
+with same precedence as multiplication, which returns the LHS if it
+tests successfully, else returns the RHS.
+
String conversion is a temporary feature until I get a better type
system. `$` is a prefix operator which expects a string and returns
a number.
###### Binode types
Plus, Minus,
Times, Divide, Rem,
- Concat,
- Absolute, Negate,
+ Concat, Choose,
+ Absolute, Negate, Test,
StringConv,
Bracket,
###### declare terminals
$LEFT + - Eop
- $LEFT * / % ++ Top
- $LEFT Uop $
+ $LEFT * / % ++ ?? Top
+ $LEFT Uop $ ?
$TERM ( )
###### expression grammar
###### 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; }$
+ | ? ${ $0.op = Test; }$
- 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; }$
+ | ?? ${ $0.op = Choose; }$
###### print binode cases
case Plus:
case Divide:
case Concat:
case Rem:
+ case Choose:
if (bracket) printf("(");
print_exec(b->left, indent, bracket);
switch(b->op) {
case Divide: fputs(" / ", stdout); break;
case Rem: fputs(" % ", stdout); break;
case Concat: fputs(" ++ ", stdout); break;
+ case Choose: fputs(" ?? ", stdout); break;
default: abort(); // NOTEST
} // NOTEST
print_exec(b->right, indent, bracket);
case Absolute:
case Negate:
case StringConv:
+ case Test:
if (bracket) printf("(");
switch (b->op) {
case Absolute: fputs("+", stdout); break;
case Negate: fputs("-", stdout); break;
case StringConv: fputs("$", stdout); break;
+ case Test: fputs("?", stdout); break;
default: abort(); // NOTEST
} // NOTEST
print_exec(b->right, indent, bracket);
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);
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);
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",
prog, type, 0, NULL);
return Tnum;
+ case Test:
+ /* LHS must support ->test, result is Tbool */
+ t = propagate_types(b->right, c, perr, NULL, 0);
+ if (!t || !t->test)
+ type_err(c, "error: '?' requires a testable value, not %1",
+ prog, t, 0, NULL);
+ return Tbool;
+
+ case Choose:
+ /* LHS and RHS must match and are returned. Must support
+ * ->test
+ */
+ t = propagate_types(b->left, c, perr, type, rules);
+ t = propagate_types(b->right, c, perr, t, rules);
+ if (t && t->test == NULL)
+ type_err(c, "error: \"??\" requires a testable value, not %1",
+ prog, t, 0, NULL);
+ return t;
+
case Bracket:
- return propagate_types(b->right, c, ok, type, 0);
+ return propagate_types(b->right, c, perr, type, 0);
###### interp binode cases
printf("Unsupported suffix: %.*s\n", tx.len, tx.txt); // UNTESTED
break;
+ case Test:
+ right = interp_exec(c, b->right, &rtype);
+ rvtype = Tbool;
+ rv.bool = !!rtype->test(rtype, &right);
+ break;
+ case Choose:
+ left = interp_exec(c, b->left, <ype);
+ if (ltype->test(ltype, &left)) {
+ rv = left;
+ rvtype = ltype;
+ ltype = NULL;
+ } else
+ rv = interp_exec(c, b->right, &rvtype);
+ break;
###### value functions
$*binode
Block -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
- | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
- | SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
- | SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
- | IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
+ | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+ | SimpleStatements ; ${ $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 } ${ $0 = reorder_bilist($<SS); }$
- | OpenScope SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
- | OpenScope SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
- | IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
+ | OpenScope { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+ | OpenScope SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
+ | OpenScope SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+ | IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
UseBlock -> { OpenScope IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
- | { OpenScope SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
- | IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
+ | { OpenScope SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+ | IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
ColonBlock -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
- | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
- | : SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
- | : SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
- | : IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
+ | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+ | : SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
+ | : SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+ | : IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
Statementlist -> ComplexStatements ${ $0 = reorder_bilist($<CS); }$
ComplexStatements -> 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($<SS);
- }$
- | SimpleStatements ; Newlines ${
- $0 = reorder_bilist($<SS);
- }$
- ## ComplexStatement Grammar
-
- $*binode
- SimpleStatements -> 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($<SS);
+ }$
+ | SimpleStatements ; Newlines ${
+ $0 = reorder_bilist($<SS);
+ }$
+ ## ComplexStatement Grammar
+
+ $*binode
+ SimpleStatements -> 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:
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)
###### SimpleStatement Grammar
| print ExpressionList ${
- $0 = b = new(binode);
+ $0 = b = new_pos(binode, $1);
b->op = Print;
b->right = NULL;
b->left = reorder_bilist($<EL);
}$
| print ExpressionList , ${ {
- $0 = b = new(binode);
+ $0 = b = new_pos(binode, $1);
b->op = Print;
b->right = reorder_bilist($<EL);
b->left = NULL;
} }$
| print ${
- $0 = b = new(binode);
+ $0 = b = new_pos(binode, $1);
b->op = Print;
b->left = NULL;
b->right = NULL;
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;
###### 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
case Assign:
do_indent(indent, "");
- print_exec(b->left, indent, bracket);
+ print_exec(b->left, -1, bracket);
printf(" = ");
- print_exec(b->right, indent, bracket);
+ print_exec(b->right, -1, bracket);
if (indent >= 0)
printf("\n");
break;
{
struct variable *v = cast(var, b->left)->var;
do_indent(indent, "");
- print_exec(b->left, indent, bracket);
+ print_exec(b->left, -1, bracket);
if (cast(var, b->left)->var->constant) {
printf("::");
if (v->explicit_type) {
}
if (b->right) {
printf("= ");
- print_exec(b->right, indent, bracket);
+ print_exec(b->right, -1, bracket);
}
if (indent >= 0)
printf("\n");
* 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;
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
// 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 = $<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);
+ }$
CondSuffix -> IfSuffix ${
- $0 = $<1;
- }$
- | Newlines CasePart CondSuffix ${
- $0 = $<CS;
- $CP->next = $0->casepart;
- $0->casepart = $<CP;
- }$
- | CasePart CondSuffix ${
- $0 = $<CS;
- $CP->next = $0->casepart;
- $0->casepart = $<CP;
- }$
+ $0 = $<1;
+ }$
+ | Newlines CasePart CondSuffix ${
+ $0 = $<CS;
+ $CP->next = $0->casepart;
+ $0->casepart = $<CP;
+ }$
+ | CasePart CondSuffix ${
+ $0 = $<CS;
+ $CP->next = $0->casepart;
+ $0->casepart = $<CP;
+ }$
IfSuffix -> Newlines ${ $0 = new(cond_statement); }$
- | Newlines ElsePart ${ $0 = $<EP; }$
- | ElsePart ${$0 = $<EP; }$
+ | Newlines ElsePart ${ $0 = $<EP; }$
+ | ElsePart ${$0 = $<EP; }$
ElsePart -> else OpenBlock Newlines ${
- $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);
+ }$
$*casepart
CasePart -> case Expression OpenScope ColonBlock ${
- $0 = calloc(1,sizeof(struct casepart));
- $0->value = $<Ex;
- $0->action = $<Bl;
- var_block_close(c, CloseParallel, $0->action);
- }$
+ $0 = calloc(1,sizeof(struct casepart));
+ $0->value = $<Ex;
+ $0->action = $<Bl;
+ var_block_close(c, CloseParallel, $0->action);
+ }$
$*exec
// These scopes are closed in CondStatement
ForPart -> for OpenBlock ${
- $0 = $<Bl;
- }$
+ $0 = $<Bl;
+ }$
ThenPart -> then OpenBlock ${
- $0 = $<OB;
- var_block_close(c, CloseSequential, $0);
- }$
+ $0 = $<OB;
+ var_block_close(c, CloseSequential, $0);
+ }$
$*binode
// This scope is closed in CondStatement
WhilePart -> while UseBlock OptNL do OpenBlock ${
- $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);
+ }$
$cond_statement
IfPart -> if UseBlock OptNL then OpenBlock ${
- $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);
+ }$
$*exec
// This scope is closed in CondStatement
SwitchPart -> switch OpenScope Expression ${
- $0 = $<Ex;
- }$
- | switch UseBlock ${
- $0 = $<Bl;
- }$
+ $0 = $<Ex;
+ }$
+ | switch UseBlock ${
+ $0 = $<Bl;
+ }$
###### print binode cases
case Loop:
###### propagate binode cases
case Loop:
- t = propagate_types(b->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:
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;
## 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
### 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.
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 = $<CT;
v->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 = $<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;
+
+ bv = new(binode);
+ bv->op = Declare;
+ bv->left = var;
+ bv->right= $<Exp;
+
+ bl = new(binode);
+ bl->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.
/* 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;
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) {
$*variable
DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${
- $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);
+ }$
###### print func decls
{
for (v = 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) {
{
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;
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;