operator which can select between two expressions based on a third
(which appears syntactically in the middle).
-Elements that are present purely to make a usable language, and
-without any expectation that they will remain, are the "program'
-clause, which provides a list of variables to received command-line
-arguments, and the "print" statement which performs simple output.
+The "func" clause currently only allows a "main" function to be
+declared. That will be extended when proper function support is added.
+
+An element that is present purely to make a usable language, and
+without any expectation that they will remain, is the "print" statement
+which performs simple output.
The current scalar types are "number", "Boolean", and "string".
Boolean will likely stay in its current form, the other two might, but
- Parse the program, possibly with tracing,
- Analyse the parsed program to ensure consistency,
- Print the program,
-- Execute the program, if no parsing or consistency errors were found.
+- Execute the "main" function in the program, if no parsing or
+ consistency errors were found.
This is all performed by a single C program extracted with
`parsergen`.
struct token_config config;
char *file_name;
int parse_error;
- struct exec *prog;
## parse context
};
{NULL, 0, NULL, 0},
};
const char *options = "tpnbs";
+
+ static void pr_err(char *msg) // NOTEST
+ {
+ fprintf(stderr, "%s\n", msg); // NOTEST
+ } // NOTEST
+
int main(int argc, char *argv[])
{
int fd;
int len;
char *file;
- struct section *s, *ss;
+ struct section *s = NULL, *ss;
char *section = NULL;
struct parse_context context = {
.config = {
context.file_name = argv[optind];
len = lseek(fd, 0, 2);
file = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
- s = code_extract(file, file+len, NULL);
+ s = code_extract(file, file+len, pr_err);
if (!s) {
fprintf(stderr, "oceani: could not find any code in %s\n",
argv[optind]);
if (!ss) {
fprintf(stderr, "oceani: cannot find section %s\n",
section);
- exit(1);
+ goto cleanup;
}
} else
- ss = s;
+ ss = s; // NOTEST
+ if (!ss->code) {
+ fprintf(stderr, "oceani: no code found in requested section\n"); // NOTEST
+ goto cleanup; // NOTEST
+ }
+
parse_oceani(ss->code, &context.config, dotrace ? stderr : NULL);
- if (!context.prog) {
- fprintf(stderr, "oceani: no program found.\n");
+ if (!context.parse_error && !analyse_funcs(&context)) {
+ fprintf(stderr, "oceani: type error in program - not running.\n");
context.parse_error = 1;
}
- if (context.prog && doprint) {
+
+ if (doprint) {
## print const decls
## print type decls
- print_exec(context.prog, 0, brackets);
+ ## print func decls
}
- if (context.prog && doexec && !context.parse_error) {
- if (!analyse_prog(context.prog, &context)) {
- fprintf(stderr, "oceani: type error in program - not running.\n");
- exit(1);
- }
- interp_prog(&context, context.prog, argv+optind+1);
- }
- free_exec(context.prog);
-
+ if (doexec && !context.parse_error)
+ interp_main(&context, argc - optind, argv + optind);
+ cleanup:
while (s) {
struct section *t = s->next;
code_free(s->code);
free(s);
s = t;
}
- ## free context vars
+ // FIXME parser should pop scope even on error
+ while (context.scope_depth > 0)
+ scope_pop(&context);
+ ## free global vars
## free context types
+ ## free context storage
exit(context.parse_error ? 1 : 0);
}
###### forward decls
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);
###### core functions
static void free_value(struct type *type, struct value *v)
{
- if (type && v)
+ if (type && v) {
type->free(type, v);
+ memset(v, 0x5a, type->size);
+ }
}
static void type_print(struct type *type, FILE *f)
{
if (!type)
- fputs("*unknown*type*", f);
+ fputs("*unknown*type*", f); // NOTEST
else if (type->name.len)
fprintf(f, "%.*s", type->name.len, type->name.txt);
else if (type->print_type)
type->init(type, val);
}
- static void dup_value(struct type *type,
+ static void dup_value(struct type *type,
struct value *vold, struct value *vnew)
{
if (type && type->dup)
{
if (tl && tl->cmp_order)
return tl->cmp_order(tl, tr, left, right);
- if (tl && tl->cmp_eq)
- return tl->cmp_eq(tl, tr, left, right);
- return -1;
+ if (tl && tl->cmp_eq) // NOTEST
+ return tl->cmp_eq(tl, tr, left, right); // NOTEST
+ return -1; // NOTEST
}
static void print_value(struct type *type, struct value *v)
free(t);
}
+Type can be specified for local variables, for fields in a structure,
+for formal parameters to functions, and possibly elsewhere. Different
+rules may apply in different contexts. As a minimum, a named type may
+always be used. Currently the type of a formal parameter can be
+different from types in other contexts, so we have a separate grammar
+symbol for those.
+
+###### Grammar
+
+ $*type
+ Type -> IDENTIFIER ${
+ $0 = find_type(c, $1.txt);
+ if (!$0) {
+ tok_err(c,
+ "error: undefined type", &$1);
+
+ $0 = Tnone;
+ }
+ }$
+ ## type grammar
+
+ FormalType -> Type ${ $0 = $<1; }$
+ ## formal type grammar
+
#### Base Types
Values of the base types can be numbers, which we represent as
static int type_compat(struct type *require, struct type *have, int rules)
{
if ((rules & Rboolok) && have == Tbool)
- return 1;
+ return 1; // NOTEST
if ((rules & Rnolabel) && have == Tlabel)
- return 0;
+ return 0; // NOTEST
if (!require || !have)
return 1;
static void _free_value(struct type *type, struct value *v)
{
if (!v)
- return;
+ return; // NOTEST
switch (type->vtype) {
case Vnone: break;
case Vstr: free(v->str.txt); break;
case Vbool:
val->bool = 0;
break;
- case Vlabel: // NOTEST
- val->label = NULL; // NOTEST
- break; // NOTEST
+ case Vlabel:
+ val->label = NULL;
+ break;
}
}
- static void _dup_value(struct type *type,
+ static void _dup_value(struct type *type,
struct value *vold, struct value *vnew)
{
switch (type->vtype) {
t->size = size;
t->align = size > sizeof(void*) ? sizeof(void*) : size;
if (t->size & (t->align - 1))
- t->size = (t->size | (t->align - 1)) + 1;
+ t->size = (t->size | (t->align - 1)) + 1; // NOTEST
return t;
}
## variable fields
};
+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.
+
+####### exec fields
+ struct variable *to_free;
+
+####### variable fields
+ struct exec *cleanup_exec;
+ struct variable *next_free;
+
+####### interp exec cleanup
+ {
+ struct variable *v;
+ for (v = e->to_free; v; v = v->next_free) {
+ struct value *val = var_value(c, v);
+ free_value(v->type, val);
+ }
+ }
+
+###### ast functions
+ static void variable_unlink_exec(struct variable *v)
+ {
+ struct variable **vp;
+ if (!v->cleanup_exec)
+ return;
+ for (vp = &v->cleanup_exec->to_free;
+ *vp; vp = &(*vp)->next_free) {
+ if (*vp != v)
+ continue;
+ *vp = v->next_free;
+ v->cleanup_exec = NULL;
+ break;
+ }
+ }
+
While the naming seems strange, we include local constants in the
definition of variables. A name declared `var := value` can
subsequently be changed, but a name declared `var ::= value` cannot -
in which declared variables must not be in scope, and subsequent
branches, whether they may already be conditionally scoped.
+We need a total ordering of scopes so we can easily compare to variables
+to see if they are concurrently in scope. To achieve this we record a
+`scope_count` which is actually a count of both beginnings and endings
+of scopes. Then each variable has a record of the scope count where it
+enters scope, and where it leaves.
+
To push a new frame *before* any code in the frame is parsed, we need a
grammar reduction. This is most easily achieved with a grammar
element which derives the empty string, and creates the new scope when
###### parse context
int scope_depth;
+ int scope_count;
struct scope *scope_stack;
+###### variable fields
+ int scope_start, scope_end;
+
###### ast functions
static void scope_pop(struct parse_context *c)
{
c->scope_stack = s->parent;
free(s);
c->scope_depth -= 1;
+ c->scope_count += 1;
}
static void scope_push(struct parse_context *c)
s->parent = c->scope_stack;
c->scope_stack = s;
c->scope_depth += 1;
+ c->scope_count += 1;
}
###### Grammar
$void
OpenScope -> ${ scope_push(c); }$
- ClosePara -> ${ var_block_close(c, CloseParallel); }$
Each variable records a scope depth and is in one of four states:
enclosed the declaration, and that has closed.
- "conditionally in scope". The "in scope" block and all parallel
- scopes have closed, and no further mention of the name has been
- seen. This state includes a secondary nest depth which records the
- outermost scope seen since the variable became conditionally in
- scope. If a use of the name is found, the variable becomes "in
- scope" and that secondary depth becomes the recorded scope depth.
- If the name is declared as a new variable, the old variable becomes
- "out of scope" and the recorded scope depth stays unchanged.
+ scopes have closed, and no further mention of the name has been seen.
+ This state includes a secondary nest depth (`min_depth`) which records
+ the outermost scope seen since the variable became conditionally in
+ scope. If a use of the name is found, the variable becomes "in scope"
+ and that secondary depth becomes the recorded scope depth. If the
+ name is declared as a new variable, the old variable becomes "out of
+ scope" and the recorded scope depth stays unchanged.
- "out of scope". The variable is neither in scope nor conditionally
in scope. It is permanently out of scope now and can be removed from
- the "in scope" stack.
+ the "in scope" stack. When a variable becomes out-of-scope it is
+ moved to a separate list (`out_scope`) of variables which have fully
+ known scope. This will be used at the end of each function to assign
+ each variable a place in the stack frame.
###### variable fields
int depth, min_depth;
###### parse context
struct variable *in_scope;
+ struct variable *out_scope;
All variables with the same name are linked together using the
'previous' link. Those variable that have been affirmatively merged all
The storage of the value of a variable will be described later. For now
we just need to know that when a variable goes out of scope, it might
-need to be freed. For this we need to be able to find it, so assume that
+need to be freed. For this we need to be able to find it, so assume that
`var_value()` will provide that.
###### variable fields
{
struct variable *v;
- if (primary->merged)
- // shouldn't happen
- primary = primary->merged;
+ primary = primary->merged;
for (v = primary->previous; v; v=v->previous)
if (v == secondary || v == secondary->merged ||
v->merged == secondary ||
- (v->merged && v->merged == secondary->merged)) {
+ v->merged == secondary->merged) {
v->scope = OutScope;
v->merged = primary;
+ if (v->scope_start < primary->scope_start)
+ primary->scope_start = v->scope_start;
+ if (v->scope_end > primary->scope_end)
+ primary->scope_end = v->scope_end; // NOTEST
+ variable_unlink_exec(v);
}
}
###### forward decls
static struct value *var_value(struct parse_context *c, struct variable *v);
-###### free context vars
+###### free global vars
while (context.varlist) {
struct binding *b = context.varlist;
context.varlist = b->next;
free(b);
while (v) {
- struct variable *t = v;
-
- v = t->previous;
- free_value(t->type, var_value(&context, t));
- if (t->depth == 0)
- // This is a global constant
- free_exec(t->where_decl);
- free(t);
+ struct variable *next = v->previous;
+
+ if (v->global) {
+ free_value(v->type, var_value(&context, v));
+ if (v->depth == 0)
+ // This is a global constant
+ free_exec(v->where_decl);
+ }
+ free(v);
+ v = next;
}
}
#### Manipulating Bindings
-When a name is conditionally visible, a new declaration discards the
-old binding - the condition lapses. Conversely a usage of the name
-affirms the visibility and extends it to the end of the containing
-block - i.e. the block that contains both the original declaration and
-the latest usage. This is determined from `min_depth`. When a
-conditionally visible variable gets affirmed like this, it is also
-merged with other conditionally visible variables with the same name.
+When a name is conditionally visible, a new declaration discards the old
+binding - the condition lapses. Similarly when we reach the end of a
+function (outermost non-global scope) any conditional scope must lapse.
+Conversely a usage of the name affirms the visibility and extends it to
+the end of the containing block - i.e. the block that contains both the
+original declaration and the latest usage. This is determined from
+`min_depth`. When a conditionally visible variable gets affirmed like
+this, it is also merged with other conditionally visible variables with
+the same name.
When we parse a variable declaration we either report an error if the
name is currently bound, or create a new variable at the current nest
When exiting a parallel scope we check if there are any variables that
were previously pending and are still visible. If there are, then
-there weren't redeclared in the most recent scope, so they cannot be
+they weren't redeclared in the most recent scope, so they cannot be
merged and must become out-of-scope. If it is not the first of
parallel scopes (based on `child_count`), we check that there was a
previous binding that is still pending-scope. If there isn't, the new
all pending-scope variables become conditionally scoped.
###### ast
- enum closetype { CloseSequential, CloseParallel, CloseElse };
+ enum closetype { CloseSequential, CloseFunction, CloseParallel, CloseElse };
###### ast functions
v->previous = b->var;
b->var = v;
v->name = b;
+ v->merged = v;
v->min_depth = v->depth = c->scope_depth;
v->scope = InScope;
v->in_scope = c->in_scope;
+ v->scope_start = c->scope_count;
c->in_scope = v;
+ ## variable init
return v;
}
return v;
}
- static void var_block_close(struct parse_context *c, enum closetype ct)
+ static int var_refile(struct parse_context *c, struct variable *v)
+ {
+ /* Variable just went out of scope. Add it to the out_scope
+ * list, sorted by ->scope_start
+ */
+ struct variable **vp = &c->out_scope;
+ while ((*vp) && (*vp)->scope_start < v->scope_start)
+ vp = &(*vp)->in_scope;
+ v->in_scope = *vp;
+ *vp = v;
+ return 0;
+ }
+
+ static void var_block_close(struct parse_context *c, enum closetype ct,
+ struct exec *e)
{
- /* Close off all variables that are in_scope */
+ /* Close off all variables that are in_scope.
+ * Some variables in c->scope may already be not-in-scope,
+ * such as when a PendingScope variable is hidden by a new
+ * variable with the same name.
+ * So we check for v->name->var != v and drop them.
+ * If we choose to make a variable OutScope, we drop it
+ * immediately too.
+ */
struct variable *v, **vp, *v2;
scope_pop(c);
for (vp = &c->in_scope;
- v = *vp, v && v->depth > c->scope_depth && v->min_depth > c->scope_depth;
- ) {
- if (v->name->var == v) switch (ct) {
+ (v = *vp) && v->min_depth > c->scope_depth;
+ (v->scope == OutScope || v->name->var != v)
+ ? (*vp = v->in_scope, var_refile(c, v))
+ : ( vp = &v->in_scope, 0)) {
+ v->min_depth = c->scope_depth;
+ if (v->name->var != v)
+ /* This is still in scope, but we haven't just
+ * closed the scope.
+ */
+ continue;
+ v->min_depth = c->scope_depth;
+ if (v->scope == InScope)
+ v->scope_end = c->scope_count;
+ if (v->scope == InScope && e && !v->global) {
+ /* This variable gets cleaned up when 'e' finishes */
+ variable_unlink_exec(v);
+ v->cleanup_exec = e;
+ v->next_free = e->to_free;
+ e->to_free = v;
+ }
+ switch (ct) {
case CloseElse:
case CloseParallel: /* handle PendingScope */
switch(v->scope) {
case InScope:
case CondScope:
if (c->scope_stack->child_count == 1)
+ /* first among parallel branches */
v->scope = PendingScope;
else if (v->previous &&
v->previous->scope == PendingScope)
+ /* all previous branches used name */
v->scope = PendingScope;
else if (v->type == Tlabel)
- v->scope = PendingScope;
- else if (v->name->var == v)
+ /* Labels remain pending even when not used */
+ v->scope = PendingScope; // UNTESTED
+ else
v->scope = OutScope;
if (ct == CloseElse) {
/* All Pending variables with this name
}
break;
case PendingScope:
- for (v2 = v;
- v2 && v2->scope == PendingScope;
- v2 = v2->previous)
- if (v2->type != Tlabel)
- v2->scope = OutScope;
- break;
- case OutScope: break;
+ /* Not possible as it would require
+ * parallel scope to be nested immediately
+ * in a parallel scope, and that never
+ * happens.
+ */ // NOTEST
+ case OutScope:
+ /* Not possible as we already tested for
+ * OutScope
+ */
+ abort(); // NOTEST
}
break;
+ case CloseFunction:
+ if (v->scope == CondScope)
+ /* Condition cannot continue past end of function */
+ v->scope = InScope;
+ /* fallthrough */
case CloseSequential:
if (v->type == Tlabel)
v->scope = PendingScope;
for (v2 = v;
v2 && v2->scope == PendingScope;
v2 = v2->previous)
- if (v2->type == Tlabel) {
+ if (v2->type == Tlabel)
v2->scope = CondScope;
- v2->min_depth = c->scope_depth;
- } else
+ else
v2->scope = OutScope;
break;
case CondScope:
}
break;
}
- if (v->scope == OutScope || v->name->var != v)
- *vp = v->in_scope;
- else
- vp = &v->in_scope;
}
}
values. The local frame doesn't get values until the interpreted phase
is started, so there is no need to allocate until the size is known.
+We initialize the `frame_pos` to an impossible value, so that we can
+tell if it was set or not later.
+
###### variable fields
- short frame_pos;
- short global;
+ short frame_pos;
+ short global;
+
+###### variable init
+ v->frame_pos = -1;
###### parse context
{
if (!v->global) {
if (!c->local || !v->type)
- return NULL;
+ return NULL; // NOTEST
if (v->frame_pos + v->type->size > c->local_size) {
- printf("INVALID frame_pos\n"); // NOTEST
- exit(2);
+ printf("INVALID frame_pos\n"); // NOTEST
+ exit(2); // NOTEST
}
return c->local + v->frame_pos;
}
struct variable scratch;
if (t->prepare_type)
- t->prepare_type(c, t, 1);
+ 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);
As global values are found -- struct field initializers, labels etc --
`global_alloc()` is called to record the value in the global frame.
-When the program is fully parsed, we need to walk the list of variables
-to find any that weren't merged away and that aren't global, and to
-calculate the frame size and assign a frame position for each variable.
-For this we have `scope_finalize()`.
+When the program is fully parsed, each function is analysed, we need to
+walk the list of variables local to that function and assign them an
+offset in the stack frame. For this we have `scope_finalize()`.
+
+We keep the stack from dense by re-using space for between variables
+that are not in scope at the same time. The `out_scope` list is sorted
+by `scope_start` and as we process a varible, we move it to an FIFO
+stack. For each variable we consider, we first discard any from the
+stack anything that went out of scope before the new variable came in.
+Then we place the new variable just after the one at the top of the
+stack.
###### ast functions
- static void scope_finalize(struct parse_context *c)
+ static void scope_finalize(struct parse_context *c, struct type *ft)
{
- struct binding *b;
-
- for (b = c->varlist; b; b = b->next) {
- struct variable *v;
- for (v = b->var; v; v = v->previous) {
- struct type *t = v->type;
- if (v->merged && v->merged != v)
- continue;
- if (v->global)
- continue;
- if (c->local_size & (t->align - 1))
- c->local_size = (c->local_size + t->align) & ~(t->align-1);
- v->frame_pos = c->local_size;
- c->local_size += v->type->size;
- }
+ int size = 0;
+ struct variable *next = ft->function.scope;
+ struct variable *done = NULL;
+ while (next) {
+ struct variable *v = next;
+ struct type *t = v->type;
+ int pos;
+ next = v->in_scope;
+ if (v->merged != v)
+ continue;
+ if (!t)
+ continue;
+ while (done && done->scope_end < v->scope_start)
+ done = done->in_scope;
+ if (done)
+ pos = done->frame_pos + done->type->size;
+ else
+ pos = 0;
+ if (pos & (t->align - 1))
+ pos = (pos + t->align) & ~(t->align-1);
+ v->frame_pos = pos;
+ if (size < pos + v->type->size)
+ size = pos + v->type->size;
+ v->in_scope = done;
+ done = v;
}
- c->local = calloc(1, c->local_size);
+ c->out_scope = NULL;
+ ft->function.local_size = size;
}
-###### free context vars
+###### free context storage
free(context.global);
- free(context.local);
### Executables
struct exec {
enum exec_types type;
int line, column;
+ ## exec fields
};
struct binode {
struct exec;
static int __fput_loc(struct exec *loc, FILE *f)
{
if (!loc)
- return 0; // NOTEST
+ return 0;
if (loc->line >= 0) {
fprintf(f, "%d:%d: ", loc->line, loc->column);
return 1;
}
if (loc->type == Xbinode)
return __fput_loc(cast(binode,loc)->left, f) ||
- __fput_loc(cast(binode,loc)->right, f);
+ __fput_loc(cast(binode,loc)->right, f); // NOTEST
return 0;
}
static void fput_loc(struct exec *loc, FILE *f)
{
if (!__fput_loc(loc, f))
- fprintf(f, "??:??: "); // NOTEST
+ fprintf(f, "??:??: ");
}
Each different type of `exec` node needs a number of functions defined,
static void do_indent(int i, char *str)
{
- while (i--)
+ while (i-- > 0)
printf(" ");
printf("%s", str);
}
static void print_exec(struct exec *e, int indent, int bracket)
{
if (!e)
- return; // NOTEST
+ return;
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");
+ }
}
###### forward decls
###### ast
- enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1, Rnoconstant = 2<<1};
+ enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1, Rnoconstant = 1<<2};
###### format cases
case 'r':
fputs(" (labels not permitted)", stderr);
break;
-###### core functions
-
+###### forward decls
static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok,
struct type *type, int rules);
+###### core functions
+
static struct type *__propagate_types(struct exec *prog, struct parse_context *c, int *ok,
struct type *type, int rules)
{
Interpreting an `exec` doesn't require anything but the `exec`. State
is stored in variables and each variable will be directly linked from
-within the `exec` tree. The exception to this is the whole `program`
-which needs to look at command line arguments. The `program` will be
+within the `exec` tree. The exception to this is the `main` function
+which needs to look at command line arguments. This function will be
interpreted separately.
Each `exec` can return a value combined with a type in `struct lrval`.
set `lval` to NULL indicating that there is a value of appropriate type
in `rval`.
-
###### core functions
struct lrval {
struct value rval, *lval;
};
- static struct lrval _interp_exec(struct parse_context *c, struct exec *e);
+ /* If dest is passed, dtype must give the expected type, and
+ * result can go there, in which case type is returned as NULL.
+ */
+ static struct lrval _interp_exec(struct parse_context *c, struct exec *e,
+ struct value *dest, struct type *dtype);
static struct value interp_exec(struct parse_context *c, struct exec *e,
struct type **typeret)
{
- struct lrval ret = _interp_exec(c, e);
+ struct lrval ret = _interp_exec(c, e, NULL, NULL);
if (!ret.type) abort();
if (typeret)
static struct value *linterp_exec(struct parse_context *c, struct exec *e,
struct type **typeret)
{
- struct lrval ret = _interp_exec(c, e);
+ struct lrval ret = _interp_exec(c, e, NULL, NULL);
+ if (!ret.type) abort();
if (ret.lval)
*typeret = ret.type;
else
return ret.lval;
}
- static struct lrval _interp_exec(struct parse_context *c, struct exec *e)
+ /* dinterp_exec is used when the destination type is certain and
+ * the value has a place to go.
+ */
+ static void dinterp_exec(struct parse_context *c, struct exec *e,
+ struct value *dest, struct type *dtype,
+ int need_free)
+ {
+ struct lrval ret = _interp_exec(c, e, dest, dtype);
+ if (!ret.type)
+ return; // NOTEST
+ if (need_free)
+ free_value(dtype, dest);
+ if (ret.lval)
+ dup_value(dtype, ret.lval, dest);
+ else
+ memcpy(dest, &ret.rval, dtype->size);
+ }
+
+ static struct lrval _interp_exec(struct parse_context *c, struct exec *e,
+ struct value *dest, struct type *dtype)
{
+ /* If the result is copied to dest, ret.type is set to NULL */
struct lrval ret;
struct value rv = {}, *lrv = NULL;
struct type *rvtype;
}
## interp exec cases
}
- ret.lval = lrv;
- ret.rval = rv;
- ret.type = rvtype;
+ if (rvtype) {
+ ret.lval = lrv;
+ ret.rval = rv;
+ ret.type = rvtype;
+ }
+ ## interp exec cleanup
return ret;
}
size can be either a literal number, or a named constant. Some day an
arbitrary expression will be supported.
+As a formal parameter to a function, the array can be declared with a
+new variable as the size: `name:[size::number]string`. The `size`
+variable is set to the size of the array and must be a constant. As
+`number` is the only supported type, it can be left out:
+`name:[size::]string`.
+
Arrays cannot be assigned. When pointers are introduced we will also
introduce array slices which can refer to part or all of an array -
the assignment syntax will create a slice. For now, an array can only
###### type union fields
struct {
+ int unspec; // size is unspecified - vsize must be set.
short size;
short static_size;
struct variable *vsize;
void *ptr = val->ptr;
if (!val)
- return;
+ return; // NOTEST
if (!type->array.static_size) {
- val->array = calloc(type->array.size,
+ val->array = calloc(type->array.size,
type->array.member->size);
ptr = val->array;
}
/* 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)
+ return 1; // UNTESTED
if (require->array.vsize == NULL && have->array.vsize == NULL)
return require->array.size == have->array.size;
- return require->array.vsize == have->array.vsize;
+ return require->array.vsize == have->array.vsize; // UNTESTED
}
static void array_print_type(struct type *type, FILE *f)
fputs("[", f);
if (type->array.vsize) {
struct binding *b = type->array.vsize->name;
- fprintf(f, "%.*s]", b->name.len, b->name.txt);
+ fprintf(f, "%.*s%s]", b->name.len, b->name.txt,
+ type->array.unspec ? "::" : "");
} else
fprintf(f, "%d]", type->array.size);
type_print(type->array.member, f);
t->array.vsize = NULL;
if (number_parse(num, tail, $2.txt) == 0)
tok_err(c, "error: unrecognised number", &$2);
- else if (tail[0])
+ else if (tail[0]) {
tok_err(c, "error: unsupported number suffix", &$2);
- else {
+ mpq_clear(num);
+ } else {
t->array.size = 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",
$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);
+ struct text noname = { "", 0 };
+
+ v->type = $<OT;
+ v->constant = 1;
+ if (!v->type)
+ v->type = Tnum;
+ $0 = add_type(c, noname, &array_prototype);
+ $0->array.member = $<6;
+ $0->array.size = 0;
+ $0->array.unspec = 1;
+ $0->array.vsize = v;
+ } }$
+
###### Binode types
Index,
if (i >= 0 && i < ltype->array.size)
lrv = ptr + i * rvtype->size;
else
- val_init(ltype->array.member, &rv);
+ val_init(ltype->array.member, &rv); // UNSAFE
ltype = NULL;
break;
}
struct value *v;
v = (void*) val->ptr + type->structure.fields[i].offset;
if (type->structure.fields[i].init)
- dup_value(type->structure.fields[i].type,
+ dup_value(type->structure.fields[i].type,
type->structure.fields[i].init,
v);
else
struct type *st = propagate_types(f->left, c, ok, NULL, 0);
if (!st)
- type_err(c, "error: unknown type for field access", f->left,
+ 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",
return;
free_fieldlist(f->prev);
if (f->f.init) {
- free_value(f->f.type, f->f.init);
- free(f->f.init);
+ free_value(f->f.type, f->f.init); // UNTESTED
+ free(f->f.init); // UNTESTED
}
free(f);
}
propagate_types($<5, c, &ok, $3, 0);
} while (ok == 2);
if (!ok)
- c->parse_error = 1;
+ 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);
if (fl->type->print && fl->init) {
fprintf(f, " = ");
if (fl->type == Tstr)
- fprintf(f, "\"");
+ fprintf(f, "\""); // UNTESTED
print_value(fl->type, fl->init);
if (fl->type == Tstr)
- fprintf(f, "\"");
+ fprintf(f, "\""); // UNTESTED
}
printf("\n");
}
while (target != 0) {
int i = 0;
for (t = context.typelist; t ; t=t->next)
- if (t->print_type_decl) {
+ if (t->print_type_decl && !t->check_args) {
i += 1;
if (i == target)
break;
}
}
+#### Functions
+
+A function is a chunk of code which can be passed parameters and can
+return results. Each function has a type which includes the set of
+parameters and the return value. As yet these types cannot be declared
+separately from the function itself.
+
+The parameters can be specified either in parentheses as a ';' separated
+list, such as
+
+##### Example: function 1
+
+ func main(av:[ac::number]string; env:[envc::number]string)
+ code block
+
+or as an indented list of one parameter per line (though each line can
+be a ';' separated list)
+
+##### Example: function 2
+
+ func main
+ argv:[argc::number]string
+ env:[envc::number]string
+ do
+ code block
+
+In the first case a return type can follow the paentheses after a colon,
+in the second it is given on a line starting with the word `return`.
+
+##### Example: functions that return
+
+ func add(a:number; b:number): number
+ code block
+
+ func catenate
+ a: string
+ b: string
+ return string
+ do
+ code block
+
+
+For constructing these lists we use a `List` binode, which will be
+further detailed when Expression Lists are introduced.
+
+###### type union fields
+
+ struct {
+ struct binode *params;
+ struct type *return_type;
+ struct variable *scope;
+ int local_size;
+ } function;
+
+###### value union fields
+ struct exec *function;
+
+###### type functions
+ void (*check_args)(struct parse_context *c, int *ok,
+ struct type *require, struct exec *args);
+
+###### value functions
+
+ static void function_free(struct type *type, struct value *val)
+ {
+ free_exec(val->function);
+ val->function = NULL;
+ }
+
+ static int function_compat(struct type *require, struct type *have)
+ {
+ // FIXME can I do anything here yet?
+ return 0;
+ }
+
+ static void function_check_args(struct parse_context *c, int *ok,
+ struct type *require, struct exec *args)
+ {
+ /* This should be 'compat', but we don't have a 'tuple' type to
+ * hold the type of 'args'
+ */
+ struct binode *arg = cast(binode, args);
+ struct binode *param = require->function.params;
+
+ while (param) {
+ struct var *pv = cast(var, param->left);
+ if (!arg) {
+ type_err(c, "error: insufficient arguments to function.",
+ args, NULL, 0, NULL);
+ break;
+ }
+ *ok = 1;
+ propagate_types(arg->left, c, ok, pv->var->type, 0);
+ param = cast(binode, param->right);
+ arg = cast(binode, arg->right);
+ }
+ if (arg)
+ type_err(c, "error: too many arguments to function.",
+ args, NULL, 0, NULL);
+ }
+
+ static void function_print(struct type *type, struct value *val)
+ {
+ print_exec(val->function, 1, 0);
+ }
+
+ static void function_print_type_decl(struct type *type, FILE *f)
+ {
+ struct binode *b;
+ fprintf(f, "(");
+ for (b = type->function.params; b; b = cast(binode, b->right)) {
+ struct variable *v = cast(var, b->left)->var;
+ fprintf(f, "%.*s%s", v->name->name.len, v->name->name.txt,
+ v->constant ? "::" : ":");
+ type_print(v->type, f);
+ if (b->right)
+ fprintf(f, "; ");
+ }
+ fprintf(f, ")");
+ if (type->function.return_type != Tnone) {
+ fprintf(f, ":");
+ type_print(type->function.return_type, f);
+ }
+ fprintf(f, "\n");
+ }
+
+ static void function_free_type(struct type *t)
+ {
+ free_exec(t->function.params);
+ }
+
+ static struct type function_prototype = {
+ .size = sizeof(void*),
+ .align = sizeof(void*),
+ .free = function_free,
+ .compat = function_compat,
+ .check_args = function_check_args,
+ .print = function_print,
+ .print_type_decl = function_print_type_decl,
+ .free_type = function_free_type,
+ };
+
+###### declare terminals
+
+ $TERM func
+
+###### Binode types
+ List,
+
+###### Grammar
+
+ $*variable
+ FuncName -> IDENTIFIER ${ {
+ struct variable *v = var_decl(c, $1.txt);
+ struct var *e = new_pos(var, $1);
+ 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);
+ }
+ } }$
+
+ $*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;
+ } }$
+
+ ArgsLine -> ${ $0 = NULL; }$
+ | 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;
+ }$
+
+ $*var
+ ArgDecl -> IDENTIFIER : FormalType ${ {
+ struct variable *v = var_decl(c, $1.txt);
+ $0 = new(var);
+ $0->var = v;
+ v->type = $<FT;
+ } }$
+
## Executables: the elements of code
Each code element needs to be parsed, printed, analysed,
{
char tail[3];
if (number_parse($0->val.num, tail, $1.txt) == 0)
- mpq_init($0->val.num);
+ mpq_init($0->val.num); // UNTESTED
if (tail[0])
tok_err(c, "error: unsupported number suffix",
&$1);
} }$
## variable grammar
- $*type
- Type -> IDENTIFIER ${
- $0 = find_type(c, $1.txt);
- if (!$0) {
- tok_err(c,
- "error: undefined type", &$1);
-
- $0 = Tnone;
- }
- }$
- ## type grammar
-
###### print exec cases
case Xvar:
{
###### format cases
case 'v':
- if (loc->type == Xvar) {
+ if (loc && loc->type == Xvar) {
struct var *v = cast(var, loc);
if (v->var) {
struct binding *b = v->var->name;
} else
fputs("???", stderr); // NOTEST
} else
- fputs("NOTVAR", stderr); // NOTEST
+ fputs("NOTVAR", stderr);
break;
###### propagate exec cases
type_err(c, "%d:BUG: no variable!!", prog, NULL, 0, NULL); // NOTEST
return Tnone; // NOTEST
}
- if (v->merged)
- v = v->merged;
+ v = v->merged;
if (v->constant && (rules & Rnoconstant)) {
type_err(c, "error: Cannot assign to a constant: %v",
prog, NULL, 0, NULL);
struct var *var = cast(var, e);
struct variable *v = var->var;
- if (v->merged)
- v = v->merged;
+ v = v->merged;
lrv = var_value(c, v);
rvtype = v->type;
break;
struct binode *b2 = cast(binode, b->right);
left = interp_exec(c, b->left, <ype);
if (left.bool)
- rv = interp_exec(c, b2->left, &rvtype);
+ rv = interp_exec(c, b2->left, &rvtype); // UNTESTED
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
if (t)
propagate_types(b->right, c, ok, t, 0);
else {
- t = propagate_types(b->right, c, ok, NULL, Rnolabel);
- if (t)
- t = propagate_types(b->left, c, ok, t, 0);
+ t = propagate_types(b->right, c, ok, NULL, Rnolabel); // UNTESTED
+ if (t) // UNTESTED
+ t = propagate_types(b->left, c, ok, t, 0); // UNTESTED
}
if (!type_compat(type, Tbool, 0))
type_err(c, "error: Comparison returns %1 but %2 expected", prog,
break;
}
-### Expressions: The rest
+### Expressions: Arithmetic etc.
The remaining expressions with the highest precedence are arithmetic,
string concatenation, and string conversion. String concatenation
| Value ${ $0 = $<1; }$
| Variable ${ $0 = $<1; }$
+###### Grammar
+
$eop
Eop -> + ${ $0.op = Plus; }$
| - ${ $0.op = Minus; }$
/* op must be string, result is number */
propagate_types(b->left, c, ok, Tstr, 0);
if (!type_compat(type, Tnum, 0))
- type_err(c,
+ type_err(c, // UNTESTED
"error: Can only convert string to number, not %1",
prog, type, 0, NULL);
return Tnum;
char tail[3];
int neg = 0;
if (tx.txt[0] == '-') {
- neg = 1;
- tx.txt++;
- tx.len--;
+ neg = 1; // UNTESTED
+ tx.txt++; // UNTESTED
+ tx.len--; // UNTESTED
}
if (number_parse(rv.num, tail, tx) == 0)
- mpq_init(rv.num);
+ mpq_init(rv.num); // UNTESTED
else if (neg)
- mpq_neg(rv.num, rv.num);
+ mpq_neg(rv.num, rv.num); // UNTESTED
if (tail[0])
- printf("Unsupported suffix: %.*s\n", tx.len, tx.txt);
+ printf("Unsupported suffix: %.*s\n", tx.len, tx.txt); // UNTESTED
break;
return rv;
}
+### Function calls
+
+A function call can appear either as an expression or as a statement.
+As functions cannot yet return values, only the statement version will work.
+We use a new 'Funcall' binode type to link the function with a list of
+arguments, form with the 'List' nodes.
+
+###### Binode types
+ Funcall,
+
+###### expression grammar
+ | Variable ( ExpressionList ) ${ {
+ struct binode *b = new(binode);
+ b->op = Funcall;
+ b->left = $<V;
+ b->right = reorder_bilist($<EL);
+ $0 = b;
+ } }$
+ | Variable ( ) ${ {
+ struct binode *b = new(binode);
+ b->op = Funcall;
+ b->left = $<V;
+ b->right = NULL;
+ $0 = b;
+ } }$
+
+###### SimpleStatement Grammar
+
+ | Variable ( ExpressionList ) ${ {
+ struct binode *b = new(binode);
+ b->op = Funcall;
+ b->left = $<V;
+ b->right = reorder_bilist($<EL);
+ $0 = b;
+ } }$
+
+###### print binode cases
+
+ case Funcall:
+ do_indent(indent, "");
+ print_exec(b->left, -1, bracket);
+ printf("(");
+ for (b = cast(binode, b->right); b; b = cast(binode, b->right)) {
+ if (b->left) {
+ printf(" ");
+ print_exec(b->left, -1, bracket);
+ if (b->right)
+ printf(",");
+ }
+ }
+ printf(")");
+ if (indent >= 0)
+ printf("\n");
+ break;
+
+###### propagate binode cases
+
+ case Funcall: {
+ /* Every arg must match formal parameter, and result
+ * is return type of function
+ */
+ struct binode *args = cast(binode, b->right);
+ struct var *v = cast(var, b->left);
+
+ if (!v->var->type || v->var->type->check_args == NULL) {
+ type_err(c, "error: attempt to call a non-function.",
+ prog, NULL, 0, NULL);
+ return NULL;
+ }
+ v->var->type->check_args(c, ok, v->var->type, args);
+ return v->var->type->function.return_type;
+ }
+
+###### interp binode cases
+
+ case Funcall: {
+ struct var *v = cast(var, b->left);
+ struct type *t = v->var->type;
+ void *oldlocal = c->local;
+ int old_size = c->local_size;
+ void *local = calloc(1, t->function.local_size);
+ struct value *fbody = var_value(c, v->var);
+ struct binode *arg = cast(binode, b->right);
+ struct binode *param = t->function.params;
+
+ while (param) {
+ struct var *pv = cast(var, param->left);
+ struct type *vtype = NULL;
+ struct value val = interp_exec(c, arg->left, &vtype);
+ struct value *lval;
+ c->local = local; c->local_size = t->function.local_size;
+ lval = var_value(c, pv->var);
+ c->local = oldlocal; c->local_size = old_size;
+ memcpy(lval, &val, vtype->size);
+ param = cast(binode, param->right);
+ arg = cast(binode, arg->right);
+ }
+ c->local = local; c->local_size = t->function.local_size;
+ rv = interp_exec(c, fbody->function, &rvtype);
+ c->local = oldlocal; c->local_size = old_size;
+ free(local);
+ break;
+ }
+
### Blocks, Statements, and Statement lists.
Now that we have expressions out of the way we need to turn to
case Block:
if (indent < 0) {
// simple statement
- if (b->left == NULL)
- printf("pass");
+ if (b->left == NULL) // UNTESTED
+ printf("pass"); // UNTESTED
else
- print_exec(b->left, indent, bracket);
- if (b->right) {
- printf("; ");
- print_exec(b->right, indent, bracket);
+ 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
for (e = b; e; e = cast(binode, e->right)) {
t = propagate_types(e->left, c, ok, NULL, rules);
- if ((rules & Rboolok) && t == Tbool)
+ if ((rules & Rboolok) && (t == Tbool || t == Tnone))
+ t = NULL;
+ if (t == Tnone && e->right)
+ /* Only the final statement *must* return a value
+ * when not Rboolok
+ */
t = NULL;
- if (t && t != Tnone && t != Tbool) {
+ if (t) {
if (!type)
type = t;
else if (t != type)
expressions and prints the values separated by spaces and terminated
by a newline. No control of formatting is possible.
-`print` faces the same list-ordering issue as blocks, and uses the
-same solution.
+`print` uses `ExpressionList` to collect the expressions and stores them
+on the left side of a `Print` binode unlessthere is a trailing comma
+when the list is stored on the `right` side and no trailing newline is
+printed.
###### Binode types
Print,
##### expr precedence
- $TERM print ,
+ $TERM print
###### SimpleStatement Grammar
| print ExpressionList ${
- $0 = reorder_bilist($<2);
- }$
- | print ExpressionList , ${
$0 = new(binode);
$0->op = Print;
$0->right = NULL;
- $0->left = $<2;
- $0 = reorder_bilist($0);
+ $0->left = reorder_bilist($<EL);
}$
+ | print ExpressionList , ${ {
+ $0 = new(binode);
+ $0->op = Print;
+ $0->right = reorder_bilist($<EL);
+ $0->left = NULL;
+ } }$
| print ${
$0 = new(binode);
$0->op = Print;
+ $0->left = NULL;
$0->right = NULL;
}$
-###### Grammar
-
- $*binode
- ExpressionList -> ExpressionList , Expression ${
- $0 = new(binode);
- $0->op = Print;
- $0->left = $<1;
- $0->right = $<3;
- }$
- | Expression ${
- $0 = new(binode);
- $0->op = Print;
- $0->left = NULL;
- $0->right = $<1;
- }$
-
###### print binode cases
case Print:
do_indent(indent, "print");
- while (b) {
- if (b->left) {
- printf(" ");
- print_exec(b->left, -1, bracket);
- if (b->right)
- printf(",");
- }
- b = cast(binode, b->right);
- }
+ if (b->right) {
+ print_exec(b->right, -1, bracket);
+ printf(",");
+ } else
+ print_exec(b->left, -1, bracket);
if (indent >= 0)
printf("\n");
break;
case Print:
/* don't care but all must be consistent */
- propagate_types(b->left, c, ok, NULL, Rnolabel);
- propagate_types(b->right, c, ok, NULL, Rnolabel);
+ if (b->left)
+ b = cast(binode, b->left);
+ else
+ b = cast(binode, b->right);
+ while (b) {
+ propagate_types(b->left, c, ok, NULL, Rnolabel);
+ b = cast(binode, b->right);
+ }
break;
###### interp binode cases
case Print:
{
- char sep = 0;
- int eol = 1;
- for ( ; b; b = cast(binode, b->right))
- if (b->left) {
- if (sep)
- putchar(sep);
- left = interp_exec(c, b->left, <ype);
- print_value(ltype, &left);
- free_value(ltype, &left);
- if (b->right)
- sep = ' ';
- } else if (sep)
- eol = 0;
- ltype = Tnone;
- if (eol)
+ struct binode *b2 = cast(binode, b->left);
+ if (!b2)
+ b2 = cast(binode, b->right);
+ for (; b2; b2 = cast(binode, b2->right)) {
+ left = interp_exec(c, b2->left, <ype);
+ print_value(ltype, &left);
+ free_value(ltype, &left);
+ if (b2->right)
+ putchar(' ');
+ }
+ if (b->right == NULL)
printf("\n");
+ ltype = Tnone;
break;
}
type_err(c,
"Variable declared with no type or value: %v",
$1, NULL, 0, NULL);
+ free_var($1);
} else {
$0 = new(binode);
$0->op = Declare;
do_indent(indent, "");
print_exec(b->left, indent, bracket);
if (cast(var, b->left)->var->constant) {
+ printf("::");
if (v->where_decl == v->where_set) {
- printf("::");
type_print(v->type, stdout);
printf(" ");
- } else
- printf(" ::");
+ }
} else {
+ printf(":");
if (v->where_decl == v->where_set) {
- printf(":");
type_print(v->type, stdout);
printf(" ");
- } else
- printf(" :");
+ }
}
if (b->right) {
printf("= ");
case Assign:
lleft = linterp_exec(c, b->left, <ype);
- right = interp_exec(c, b->right, &rtype);
- if (lleft) {
- free_value(ltype, lleft);
- dup_value(ltype, &right, lleft);
- ltype = NULL;
- }
+ if (lleft)
+ dinterp_exec(c, b->right, lleft, ltype, 1);
+ ltype = Tnone;
break;
case Declare:
{
struct variable *v = cast(var, b->left)->var;
struct value *val;
- if (v->merged)
- v = v->merged;
+ v = v->merged;
val = var_value(c, v);
- free_value(v->type, val);
if (v->type->prepare_type)
v->type->prepare_type(c, v->type, 0);
- if (b->right) {
- right = interp_exec(c, b->right, &rtype);
- memcpy(val, &right, rtype->size);
- rtype = Tnone;
- } else {
+ if (b->right)
+ dinterp_exec(c, b->right, val, v->type, 0);
+ else
val_init(v->type, val);
- }
break;
}
### The `use` statement
-The `use` statement is the last "simple" statement. It is needed when
-the condition in a conditional statement is a block. `use` works much
-like `return` in C, but only completes the `condition`, not the whole
-function.
+The `use` statement is the last "simple" statement. It is needed when a
+statement block can return a value. This includes the body of a
+function which has a return type, and the "condition" code blocks in
+`if`, `while`, and `switch` statements.
###### Binode types
Use,
###### expr precedence
- $TERM use
+ $TERM use
###### SimpleStatement Grammar
| use Expression ${
`Rboolok` flag which is passed to `propagate_types()`.
The `cond_statement` cannot fit into a `binode` so a new `exec` is
-defined.
+defined. As there are two scopes which cover multiple parts - one for
+the whole statement and one for "while" and "do" - and as we will use
+the 'struct exec' to track scopes, we actually need two new types of
+exec. One is a `binode` for the looping part, the rest is the
+`cond_statement`. The `cond_statement` will use an auxilliary `struct
+casepart` to track a list of case parts.
+
+###### Binode types
+ Loop
###### exec type
Xcond_statement,
};
struct cond_statement {
struct exec;
- struct exec *forpart, *condpart, *dopart, *thenpart, *elsepart;
+ struct exec *forpart, *condpart, *thenpart, *elsepart;
+ struct binode *looppart;
struct casepart *casepart;
};
return;
free_exec(s->forpart);
free_exec(s->condpart);
- free_exec(s->dopart);
+ free_exec(s->looppart);
free_exec(s->thenpart);
free_exec(s->elsepart);
free_casepart(s->casepart);
// may or may not end with EOL
// WhilePart and IfPart include an appropriate Suffix
-
- // Both ForPart and Whilepart open scopes, and CondSuffix only
- // closes one - so in the first branch here we have another to close.
+ // 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->condpart = $WP.condpart; $WP.condpart = NULL;
- $0->dopart = $WP.dopart; $WP.dopart = NULL;
- var_block_close(c, CloseSequential);
+ $0->looppart = $<WP;
+ var_block_close(c, CloseSequential, $0);
}$
| ForPart OptNL WhilePart CondSuffix ${
$0 = $<CS;
$0->forpart = $<FP;
- $0->condpart = $WP.condpart; $WP.condpart = NULL;
- $0->dopart = $WP.dopart; $WP.dopart = NULL;
- var_block_close(c, CloseSequential);
+ $0->looppart = $<WP;
+ var_block_close(c, CloseSequential, $0);
}$
| WhilePart CondSuffix ${
$0 = $<CS;
- $0->condpart = $WP.condpart; $WP.condpart = NULL;
- $0->dopart = $WP.dopart; $WP.dopart = NULL;
+ $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);
+ var_block_close(c, CloseSequential, $0);
}$
CondSuffix -> IfSuffix ${
$0 = $<1;
- // This is where we close scope of the whole
- // "for" or "while" statement
- var_block_close(c, CloseSequential);
}$
| Newlines CasePart CondSuffix ${
$0 = $<CS;
ElsePart -> else OpenBlock Newlines ${
$0 = new(cond_statement);
$0->elsepart = $<OB;
- var_block_close(c, CloseElse);
+ var_block_close(c, CloseElse, $0->elsepart);
}$
| else OpenScope CondStatement ${
$0 = new(cond_statement);
$0->elsepart = $<CS;
- var_block_close(c, CloseElse);
+ var_block_close(c, CloseElse, $0->elsepart);
}$
$*casepart
$0 = calloc(1,sizeof(struct casepart));
$0->value = $<Ex;
$0->action = $<Bl;
- var_block_close(c, CloseParallel);
+ var_block_close(c, CloseParallel, $0->action);
}$
$*exec
- // These scopes are closed in CondSuffix
+ // These scopes are closed in CondStatement
ForPart -> for OpenBlock ${
$0 = $<Bl;
}$
ThenPart -> then OpenBlock ${
$0 = $<OB;
- var_block_close(c, CloseSequential);
+ var_block_close(c, CloseSequential, $0);
}$
- $cond_statement
- // This scope is closed in CondSuffix
- WhilePart -> while UseBlock OptNL do Block ${
- $0.condpart = $<UB;
- $0.dopart = $<Bl;
+ $*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 ColonBlock ${
- $0.condpart = $<Exp;
- $0.dopart = $<Bl;
+ | 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);
}$
- IfPart -> if UseBlock OptNL then OpenBlock ClosePara ${
+ $cond_statement
+ IfPart -> if UseBlock OptNL then OpenBlock ${
$0.condpart = $<UB;
- $0.thenpart = $<Bl;
+ $0.thenpart = $<OB;
+ var_block_close(c, CloseParallel, $0.thenpart);
}$
- | if OpenScope Expression OpenScope ColonBlock ClosePara ${
+ | if OpenScope Expression OpenScope ColonBlock ${
$0.condpart = $<Ex;
- $0.thenpart = $<Bl;
+ $0.thenpart = $<CB;
+ var_block_close(c, CloseParallel, $0.thenpart);
}$
- | if OpenScope Expression OpenScope OptNL then Block ClosePara ${
+ | 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 CondSuffix
+ // This scope is closed in CondStatement
SwitchPart -> switch OpenScope Expression ${
$0 = $<Ex;
}$
$0 = $<Bl;
}$
+###### print binode cases
+ case Loop:
+ if (b->left && b->left->type == Xbinode &&
+ cast(binode, b->left)->op == Block) {
+ if (bracket)
+ do_indent(indent, "while {\n");
+ else
+ do_indent(indent, "while\n");
+ print_exec(b->left, indent+1, bracket);
+ if (bracket)
+ do_indent(indent, "} do {\n");
+ else
+ do_indent(indent, "do\n");
+ print_exec(b->right, indent+1, bracket);
+ if (bracket)
+ do_indent(indent, "}\n");
+ } else {
+ do_indent(indent, "while ");
+ print_exec(b->left, 0, bracket);
+ if (bracket)
+ printf(" {\n");
+ else
+ printf(":\n");
+ print_exec(b->right, indent+1, bracket);
+ if (bracket)
+ do_indent(indent, "}\n");
+ }
+ break;
+
###### print exec cases
case Xcond_statement:
}
if (bracket) do_indent(indent, "}\n");
}
- if (cs->dopart) {
- // a loop
- if (cs->condpart && cs->condpart->type == Xbinode &&
- cast(binode, cs->condpart)->op == Block) {
- if (bracket)
- do_indent(indent, "while {\n");
- else
- do_indent(indent, "while\n");
- print_exec(cs->condpart, indent+1, bracket);
- if (bracket)
- do_indent(indent, "} do {\n");
- else
- do_indent(indent, "do\n");
- print_exec(cs->dopart, indent+1, bracket);
- if (bracket)
- do_indent(indent, "}\n");
- } else {
- do_indent(indent, "while ");
- print_exec(cs->condpart, 0, bracket);
- if (bracket)
- printf(" {\n");
- else
- printf(":\n");
- print_exec(cs->dopart, indent+1, bracket);
- if (bracket)
- do_indent(indent, "}\n");
- }
+ if (cs->looppart) {
+ print_exec(cs->looppart, indent, bracket);
} else {
// a condition
if (cs->casepart)
if (bracket)
printf(" {\n");
else
- printf(":\n");
+ printf("\n");
print_exec(cs->condpart, indent+1, bracket);
if (bracket)
do_indent(indent, "}\n");
if (cs->thenpart) {
- do_indent(indent, "then:\n");
+ do_indent(indent, "then\n");
print_exec(cs->thenpart, indent+1, bracket);
}
} else {
break;
}
+###### propagate binode cases
+ case Loop:
+ t = propagate_types(b->right, c, ok, Tnone, 0);
+ if (!type_compat(Tnone, t, 0))
+ *ok = 0; // UNTESTED
+ return propagate_types(b->left, c, ok, type, rules);
+
###### propagate exec cases
case Xcond_statement:
{
- // forpart and dopart must return Tnone
- // thenpart must return Tnone if there is a dopart,
+ // forpart and looppart->right must return Tnone
+ // thenpart must return Tnone if there is a loopart,
// otherwise it is like elsepart.
// condpart must:
// be bool if there is no casepart
t = propagate_types(cs->forpart, c, ok, Tnone, 0);
if (!type_compat(Tnone, t, 0))
- *ok = 0;
- t = propagate_types(cs->dopart, c, ok, Tnone, 0);
- if (!type_compat(Tnone, t, 0))
- *ok = 0;
- if (cs->dopart) {
+ *ok = 0; // UNTESTED
+
+ if (cs->looppart) {
t = propagate_types(cs->thenpart, c, ok, Tnone, 0);
if (!type_compat(Tnone, t, 0))
- *ok = 0;
+ *ok = 0; // UNTESTED
}
- if (cs->casepart == NULL)
+ if (cs->casepart == NULL) {
propagate_types(cs->condpart, c, ok, Tbool, 0);
- else {
+ propagate_types(cs->looppart, c, ok, 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);
if (!t && cs->condpart)
- t = propagate_types(cs->condpart, c, ok, NULL, Rboolok);
+ t = propagate_types(cs->condpart, c, ok, NULL, Rboolok); // UNTESTED
+ if (!t && cs->looppart)
+ t = propagate_types(cs->looppart, c, ok, 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);
}
}
// (if)then, else, and case parts must return expected type.
- if (!cs->dopart && !type)
+ if (!cs->looppart && !type)
type = propagate_types(cs->thenpart, c, ok, NULL, rules);
if (!type)
type = propagate_types(cs->elsepart, c, ok, NULL, rules);
for (cp = cs->casepart;
cp && !type;
- cp = cp->next)
- type = propagate_types(cp->action, c, ok, NULL, rules);
+ cp = cp->next) // UNTESTED
+ type = propagate_types(cp->action, c, ok, NULL, rules); // UNTESTED
if (type) {
- if (!cs->dopart)
+ if (!cs->looppart)
propagate_types(cs->thenpart, c, ok, type, rules);
propagate_types(cs->elsepart, c, ok, type, rules);
for (cp = cs->casepart; cp ; cp = cp->next)
return NULL;
}
+###### interp binode cases
+ case Loop:
+ // This just performs one iterration of the loop
+ rv = interp_exec(c, b->left, &rvtype);
+ if (rvtype == Tnone ||
+ (rvtype == Tbool && rv.bool != 0))
+ // rvtype is Tnone or Tbool, doesn't need to be freed
+ interp_exec(c, b->right, NULL);
+ break;
+
###### interp exec cases
case Xcond_statement:
{
if (cs->forpart)
interp_exec(c, cs->forpart, NULL);
- do {
- if (cs->condpart)
- cnd = interp_exec(c, cs->condpart, &cndtype);
- else
- cndtype = Tnone;
- if (!(cndtype == Tnone ||
- (cndtype == Tbool && cnd.bool != 0)))
- break;
- // cnd is Tnone or Tbool, doesn't need to be freed
- if (cs->dopart)
- interp_exec(c, cs->dopart, NULL);
-
- if (cs->thenpart) {
+ if (cs->looppart) {
+ while ((cnd = interp_exec(c, cs->looppart, &cndtype)),
+ cndtype == Tnone || (cndtype == Tbool && cnd.bool != 0))
+ interp_exec(c, cs->thenpart, NULL);
+ } else {
+ cnd = interp_exec(c, cs->condpart, &cndtype);
+ if ((cndtype == Tnone ||
+ (cndtype == Tbool && cnd.bool != 0))) {
+ // cnd is Tnone or Tbool, doesn't need to be freed
rv = interp_exec(c, cs->thenpart, &rvtype);
- if (rvtype != Tnone || !cs->dopart)
- goto Xcond_done;
- free_value(rvtype, &rv);
- rvtype = Tnone;
+ // skip else (and cases)
+ goto Xcond_done;
}
- } while (cs->dopart);
-
+ }
for (cp = cs->casepart; cp; cp = cp->next) {
v = interp_exec(c, cp->value, &vtype);
if (value_cmp(cndtype, vtype, &v, &cnd) == 0) {
Many of the things that can be declared haven't been described yet,
such as functions, procedures, imports, and probably more.
For now there are two sorts of things that can appear at the top
-level. They are predefined constants, `struct` types, and the main
-program. While the syntax will allow the main program to appear
+level. They are predefined constants, `struct` types, and the `main`
+function. While the syntax will allow the `main` function to appear
multiple times, that will trigger an error if it is actually attempted.
The various declarations do not return anything. They store the
| DeclarationList Declaration
Declaration -> ERROR Newlines ${
- tok_err(c,
+ tok_err(c, // UNTESTED
"error: unhandled parse error", &$1);
}$
| DeclareConstant
- | DeclareProgram
+ | DeclareFunction
| DeclareStruct
## top level grammar
+ ## Grammar
+
### The `const` section
As well as being defined in with the code that uses them, constants
v->where_set = var;
var->var = v;
v->constant = 1;
+ v->global = 1;
} else {
- v = var_ref(c, $1.txt);
+ 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",
- v->where_decl, NULL, 0, NULL);
+ vorig->where_decl, NULL, 0, NULL);
}
do {
ok = 1;
while (target != 0) {
int i = 0;
for (v = context.in_scope; v; v=v->in_scope)
- if (v->depth == 0) {
+ if (v->depth == 0 && v->constant) {
i += 1;
if (i == target)
break;
}
}
-### Finally the whole program.
+### Function declarations
-Somewhat reminiscent of Pascal a (current) Ocean program starts with
-the keyword "program" and a list of variable names which are assigned
-values from command line arguments. Following this is a `block` which
-is the code to execute. Unlike Pascal, constants and other
-declarations come *before* the program.
+The code in an Ocean program is all stored in function declarations.
+One of the functions must be named `main` and it must accept an array of
+strings as a parameter - the command line arguments.
-As this is the top level, several things are handled a bit
-differently.
-The whole program is not interpreted by `interp_exec` as that isn't
-passed the argument list which the program requires. Similarly type
-analysis is a bit more interesting at this level.
+As this is the top level, several things are handled a bit differently.
+The function is not interpreted by `interp_exec` as that isn't passed
+the argument list which the program requires. Similarly type analysis
+is a bit more interesting at this level.
-###### Binode types
- Program,
+###### ast functions
-###### top level grammar
+ static struct variable *declare_function(struct parse_context *c,
+ struct variable *name,
+ struct binode *args,
+ struct type *ret,
+ struct exec *code)
+ {
+ struct text funcname = {" func", 5};
+ if (name) {
+ struct value fn = {.function = code};
+ name->type = add_type(c, funcname, &function_prototype);
+ name->type->function.params = reorder_bilist(args);
+ name->type->function.return_type = ret;
+ global_alloc(c, name->type, name, &fn);
+ var_block_close(c, CloseFunction, code);
+ name->type->function.scope = c->out_scope;
+ } else {
+ free_binode(args);
+ free_type(ret);
+ free_exec(code);
+ var_block_close(c, CloseFunction, NULL);
+ }
+ c->out_scope = NULL;
+ return name;
+ }
- DeclareProgram -> Program ${ {
- if (c->prog)
- type_err(c, "Program defined a second time",
- $1, NULL, 0, NULL);
- else
- c->prog = $<1;
- } }$
+###### declare terminals
+ $TERM return
- $TERM program
+###### top level grammar
- $*binode
- Program -> program OpenScope Varlist ColonBlock Newlines ${
- $0 = new(binode);
- $0->op = Program;
- $0->left = reorder_bilist($<Vl);
- $0->right = $<Bl;
- var_block_close(c, CloseSequential);
- if (c->scope_stack && !c->parse_error) abort();
+ $*variable
+ DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
}$
-
- Varlist -> Varlist ArgDecl ${
- $0 = new(binode);
- $0->op = Program;
- $0->left = $<1;
- $0->right = $<2;
+ | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
+ }$
+ | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
+ $0 = declare_function(c, $<FN, NULL, Tnone, $<Bl);
+ }$
+ | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, $<Ty, $<Bl);
+ }$
+ | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, $<Ty, $<Bl);
+ }$
+ | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
+ $0 = declare_function(c, $<FN, NULL, $<Ty, $<Bl);
}$
- | ${ $0 = NULL; }$
- $*var
- ArgDecl -> IDENTIFIER ${ {
- struct variable *v = var_decl(c, $1.txt);
- $0 = new(var);
- $0->var = v;
- } }$
+###### print func decls
+ {
+ struct variable *v;
+ int target = -1;
- ## Grammar
+ while (target != 0) {
+ int i = 0;
+ for (v = context.in_scope; v; v=v->in_scope)
+ if (v->depth == 0 && v->type && v->type->check_args) {
+ i += 1;
+ if (i == target)
+ break;
+ }
-###### print binode cases
- case Program:
- do_indent(indent, "program");
- for (b2 = cast(binode, b->left); b2; b2 = cast(binode, b2->right)) {
- printf(" ");
- print_exec(b2->left, 0, 0);
+ if (target == -1) {
+ target = i;
+ } else {
+ 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
+ print_value(v->type, val);
+ printf("/* frame size %d */\n", v->type->function.local_size);
+ target -= 1;
+ }
}
- if (bracket)
- printf(" {\n");
- else
- printf(":\n");
- print_exec(b->right, indent+1, bracket);
- if (bracket)
- do_indent(indent, "}\n");
- break;
-
-###### propagate binode cases
- case Program: abort(); // NOTEST
+ }
###### core functions
- static int analyse_prog(struct exec *prog, struct parse_context *c)
+ static int analyse_funcs(struct parse_context *c)
+ {
+ struct variable *v;
+ int all_ok = 1;
+ for (v = c->in_scope; v; v = v->in_scope) {
+ struct value *val;
+ int ok = 1;
+ if (v->depth != 0 || !v->type || !v->type->check_args)
+ continue;
+ val = var_value(c, v);
+ do {
+ ok = 1;
+ propagate_types(val->function, c, &ok,
+ v->type->function.return_type, 0);
+ } while (ok == 2);
+ if (ok)
+ /* Make sure everything is still consistent */
+ propagate_types(val->function, c, &ok,
+ v->type->function.return_type, 0);
+ if (!ok)
+ all_ok = 0;
+ if (!v->type->function.return_type->dup) {
+ type_err(c, "error: function cannot return value of type %1",
+ v->where_decl, v->type->function.return_type, 0, NULL);
+ }
+
+ scope_finalize(c, v->type);
+ }
+ return all_ok;
+ }
+
+ static int analyse_main(struct type *type, struct parse_context *c)
{
- struct binode *b = cast(binode, prog);
+ struct binode *bp = type->function.params;
+ struct binode *b;
int ok = 1;
+ int arg = 0;
+ struct type *argv_type;
+ struct text argv_type_name = { " argv", 5 };
- if (!b)
- return 0; // NOTEST
- do {
- ok = 1;
- propagate_types(b->right, c, &ok, Tnone, 0);
- } while (ok == 2);
- if (!ok)
- return 0;
+ argv_type = add_type(c, argv_type_name, &array_prototype);
+ argv_type->array.member = Tstr;
+ argv_type->array.unspec = 1;
- for (b = cast(binode, b->left); b; b = cast(binode, b->right)) {
- struct var *v = cast(var, b->left);
- if (!v->var->type) {
- v->var->where_set = b;
- v->var->type = Tstr;
+ for (b = bp; b; b = cast(binode, b->right)) {
+ ok = 1;
+ switch (arg++) {
+ case 0: /* argv */
+ propagate_types(b->left, c, &ok, argv_type, 0);
+ break;
+ default: /* invalid */ // NOTEST
+ propagate_types(b->left, c, &ok, Tnone, 0); // NOTEST
}
+ if (!ok)
+ c->parse_error = 1;
}
- b = cast(binode, prog);
- do {
- ok = 1;
- propagate_types(b->right, c, &ok, Tnone, 0);
- } while (ok == 2);
- if (!ok)
- return 0;
- /* Make sure everything is still consistent */
- propagate_types(b->right, c, &ok, Tnone, 0);
- if (!ok)
- return 0;
- scope_finalize(c);
- return 1;
+ return !c->parse_error;
}
- static void interp_prog(struct parse_context *c, struct exec *prog, char **argv)
+ static void interp_main(struct parse_context *c, int argc, char **argv)
{
- struct binode *p = cast(binode, prog);
+ struct value *progp = NULL;
+ struct text main_name = { "main", 4 };
+ struct variable *mainv;
struct binode *al;
+ int anum = 0;
struct value v;
struct type *vtype;
- if (!prog)
- return; // NOTEST
- al = cast(binode, p->left);
+ mainv = var_ref(c, main_name);
+ if (mainv)
+ progp = var_value(c, mainv);
+ if (!progp || !progp->function) {
+ fprintf(stderr, "oceani: no main function found.\n");
+ c->parse_error = 1;
+ return;
+ }
+ if (!analyse_main(mainv->type, c)) {
+ fprintf(stderr, "oceani: main has wrong type.\n");
+ c->parse_error = 1;
+ return;
+ }
+ al = mainv->type->function.params;
+
+ c->local_size = mainv->type->function.local_size;
+ c->local = calloc(1, c->local_size);
while (al) {
struct var *v = cast(var, al->left);
struct value *vl = var_value(c, v->var);
struct value arg;
-
- if (argv[0] == NULL) {
- printf("Not enough args\n");
- exit(1);
- }
- if (v->var->type != Tstr) {
- printf("Arg not string!!\n"); // NOTEST
- exit(2); // NOTEST
+ 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->prepare_type(c, t, 0);
+ array_init(v->var->type, vl);
+ for (i = 0; i < argc; i++) {
+ struct value *vl2 = vl->array + i * v->var->type->array.member->size;
+
+ arg.str.txt = argv[i];
+ arg.str.len = strlen(argv[i]);
+ free_value(Tstr, vl2);
+ dup_value(Tstr, &arg, vl2);
+ }
+ break;
}
-
- arg.str.txt = argv[0];
- arg.str.len = strlen(argv[0]);
- free_value(Tstr, vl);
- dup_value(Tstr, &arg, vl);
-
al = cast(binode, al->right);
- argv++;
}
- v = interp_exec(c, p->right, &vtype);
+ v = interp_exec(c, progp->function, &vtype);
free_value(vtype, &v);
+ free(c->local);
+ c->local = NULL;
}
-###### interp binode cases
- case Program: abort(); // NOTEST
+###### ast functions
+ void free_variable(struct variable *v)
+ {
+ }
## And now to test it out.
name:string
alive:Boolean
- program Astr Bstr:
+ func main(argv:[argc::]string)
print "Hello World, what lovely oceans you have!"
print "Are there", five, "?"
print pi, pie, "but", cake
- A := $Astr; B := $Bstr
+ A := $argv[1]; B := $argv[2]
/* When a variable is defined in both branches of an 'if',
* and used afterwards, the variables are merged.
else
hi = mid
if hi - lo < 1:
+ lo = mid
use GiveUp
use True
do pass
case Found:
print "Yay, I found", target
case GiveUp:
- print "Closest I found was", mid
+ print "Closest I found was", lo
size::= 10
list:[size]number