struct token_config config;
char *file_name;
int parse_error;
- struct exec *prog;
## parse context
};
int fd;
int len;
char *file;
- struct section *s, *ss;
+ struct section *s = NULL, *ss;
char *section = NULL;
struct parse_context context = {
.config = {
if (!ss) {
fprintf(stderr, "oceani: cannot find section %s\n",
section);
- exit(1);
+ goto cleanup;
}
} else
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 main function 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 && !context.parse_error) {
- if (!analyse_prog(context.prog, &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)
- interp_prog(&context, context.prog, argc - optind, argv+optind);
- 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;
}
+ // 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
###### 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 _dup_value(struct type *type,
+ static void _dup_value(struct type *type,
struct value *vold, struct value *vnew)
{
switch (type->vtype) {
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
context.varlist = b->next;
free(b);
while (v) {
- struct variable *t = v;
+ struct variable *next = v->previous;
- v = t->previous;
- if (t->global) {
- free_value(t->type, var_value(&context, t));
- if (t->depth == 0)
- free_exec(t->where_decl);
+ 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(t);
+ free(v);
+ v = next;
}
}
v->scope = InScope;
v->in_scope = c->in_scope;
c->in_scope = v;
+ ## variable init
return v;
}
*/
continue;
v->min_depth = c->scope_depth;
- if (v->scope == InScope) {
+ if (v->scope == InScope && e) {
/* This variable gets cleaned up when 'e' finishes */
variable_unlink_exec(v);
v->cleanup_exec = e;
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
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); // UNTESTED
+ c->global_size = (c->global_size + t->align) & ~(t->align-1);
if (!v) {
v = &scratch;
v->type = t;
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()`.
+calculate the frame size and assign a frame position for each
+variable. For this we have `scope_finalize()`.
###### ast functions
- static void scope_finalize(struct parse_context *c)
+ static int scope_finalize(struct parse_context *c)
{
struct binding *b;
+ int size = 0;
for (b = c->varlist; b; b = b->next) {
struct variable *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;
+ if (!t)
+ continue;
+ if (size & (t->align - 1))
+ size = (size + t->align) & ~(t->align-1);
+ v->frame_pos = size;
+ size += v->type->size;
}
}
- c->local = calloc(1, c->local_size);
+ return size;
}
###### free context storage
free(context.global);
- free(context.local);
### Executables
if (loc->type == Xbinode)
return __fput_loc(cast(binode,loc)->left, f) ||
__fput_loc(cast(binode,loc)->right, f); // NOTEST
- return 0; // 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;
if (e->to_free) {
struct variable *v;
do_indent(indent, "/* FREE");
- for (v = e->to_free; v; v = v->next_free)
- printf(" %.*s(%c%d+%d)", v->name->name.len, v->name->name.txt,
- v->global ? 'G':'L',
- v->frame_pos, v->type ? v->type->size:0);
+ for (v = e->to_free; v; v = v->next_free) {
+ printf(" %.*s", v->name->name.len, v->name->name.txt);
+ if (v->frame_pos >= 0)
+ printf("(%d+%d)", v->frame_pos,
+ v->type ? v->type->size:0);
+ }
printf(" */\n");
}
}
###### 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)
{
static int array_compat(struct type *require, struct type *have)
{
if (have->compat != require->compat)
- return 0; // UNTESTED
+ return 0;
/* Both are arrays, so we can look at details */
if (!type_compat(require->array.member, have->array.member, 0))
return 0;
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",
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
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 named chunk of code which can be passed parameters and
-can return results. Each function has an implicit type which includes
-the set of parameters and the return value. As yet these types cannot
-be declared separate from the function itself.
+#### Functions
-In fact, only one function is currently possible - `main`. `main` is
-passed an array of strings together with the size of the array, and
-doesn't return anything. The strings are command line arguments.
+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 list, such as
+The parameters can be specified either in parentheses as a ';' separated
+list, such as
##### Example: function 1
- func main(av:[ac::number]string)
+ func main(av:[ac::number]string; env:[envc::number]string)
code block
-or as an indented list of one parameter per line
+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;
+ 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
- Func, List,
+ List,
###### Grammar
- $TERM func main
+ $*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
- MainFunction -> func main ( OpenScope Args ) Block Newlines ${
- $0 = new(binode);
- $0->op = Func;
- $0->left = reorder_bilist($<Ar);
- $0->right = $<Bl;
- var_block_close(c, CloseSequential, $0);
- if (c->scope_stack && !c->parse_error) abort();
- }$
- | func main IN OpenScope OptNL Args OUT OptNL do Block Newlines ${
- $0 = new(binode);
- $0->op = Func;
- $0->left = reorder_bilist($<Ar);
- $0->right = $<Bl;
- var_block_close(c, CloseSequential, $0);
- if (c->scope_stack && !c->parse_error) abort();
- }$
- | func main NEWLINE OpenScope OptNL do Block Newlines ${
- $0 = new(binode);
- $0->op = Func;
- $0->left = NULL;
- $0->right = $<Bl;
- var_block_close(c, CloseSequential, $0);
- if (c->scope_stack && !c->parse_error) abort();
- }$
+ 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 -> ${ $0 = NULL; }$
+ ArgsLine -> ${ $0 = NULL; }$
| Varlist ${ $0 = $<1; }$
| Varlist ; ${ $0 = $<1; }$
- | Varlist NEWLINE ${ $0 = $<1; }$
- Varlist -> Varlist ; ArgDecl ${ // UNTESTED
+ Varlist -> Varlist ; ArgDecl ${
$0 = new(binode);
$0->op = List;
$0->left = $<Vl;
} else
fputs("???", stderr); // NOTEST
} else
- fputs("NOTVAR", stderr); // NOTEST
+ fputs("NOTVAR", stderr);
break;
###### propagate exec cases
}
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
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; }$
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
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 && t != Tnone && t != Tbool) {
+ if (t == Tnone && e->right)
+ /* Only the final statement *must* return a value
+ * when not Rboolok
+ */
+ t = NULL;
+ 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;
### 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 ${
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 `main` function.
+### Function declarations
-An Ocean program can currently have only one function - `main` - and
-that must exist. It expects an array of strings with a provided size.
-Following this is a `block` which is the code to execute.
+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 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.
+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.
+
+###### ast functions
+
+ 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, CloseSequential, code);
+ } else {
+ free_binode(args);
+ free_type(ret);
+ free_exec(code);
+ var_block_close(c, CloseSequential, NULL);
+ }
+ return name;
+ }
+
+###### declare terminals
+ $TERM return
###### top level grammar
- DeclareFunction -> MainFunction ${ {
- if (c->prog)
- type_err(c, "\"main\" defined a second time",
- $1, NULL, 0, NULL);
- else
- c->prog = $<1;
- } }$
+ $*variable
+ DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
+ }$
+ | 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);
+ }$
-###### print binode cases
- case Func:
- case List:
- do_indent(indent, "func main(");
- for (b2 = cast(binode, b->left); b2; b2 = cast(binode, b2->right)) {
- struct variable *v = cast(var, b2->left)->var;
- printf(" ");
- print_exec(b2->left, 0, 0);
- printf(":");
- type_print(v->type, stdout);
- }
- if (bracket)
- printf(") {\n");
- else
- printf(")\n");
- print_exec(b->right, indent+1, bracket);
- if (bracket)
- do_indent(indent, "}\n");
- break;
+###### print func decls
+ {
+ struct variable *v;
+ int target = -1;
-###### propagate binode cases
- case List:
- case Func: abort(); // NOTEST
+ 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;
+ }
+
+ 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;
+ }
+ }
+ }
###### core functions
- static int analyse_prog(struct exec *prog, struct parse_context *c)
+ static int analyse_funcs(struct parse_context *c)
{
- struct binode *bp = cast(binode, prog);
+ 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;
+ v->type->function.local_size = scope_finalize(c);
+ }
+ return all_ok;
+ }
+
+ static int analyse_main(struct type *type, struct parse_context *c)
+ {
+ 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 (!bp)
- return 0; // NOTEST
-
argv_type = add_type(c, argv_type_name, &array_prototype);
argv_type->array.member = Tstr;
argv_type->array.unspec = 1;
- for (b = cast(binode, bp->left); b; b = cast(binode, b->right)) {
+ for (b = bp; b; b = cast(binode, b->right)) {
ok = 1;
switch (arg++) {
case 0: /* argv */
default: /* invalid */ // NOTEST
propagate_types(b->left, c, &ok, Tnone, 0); // NOTEST
}
+ if (!ok)
+ c->parse_error = 1;
}
- do {
- ok = 1;
- propagate_types(bp->right, c, &ok, Tnone, 0);
- } while (ok == 2);
- if (!ok)
- return 0;
-
- /* Make sure everything is still consistent */
- propagate_types(bp->right, c, &ok, Tnone, 0);
- if (!ok)
- return 0; // UNTESTED
- scope_finalize(c);
- return 1;
+ return !c->parse_error;
}
- static void interp_prog(struct parse_context *c, struct exec *prog,
- int argc, 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);
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]);
}
al = cast(binode, al->right);
}
- v = interp_exec(c, p, &vtype);
+ v = interp_exec(c, progp->function, &vtype);
free_value(vtype, &v);
+ free(c->local);
+ c->local = NULL;
}
-###### interp binode cases
- case List: abort(); // NOTEST
-
- case Func:
- rv = interp_exec(c, b->right, &rvtype);
- break;
+###### ast functions
+ void free_variable(struct variable *v)
+ {
+ }
## And now to test it out.
name:string
alive:Boolean
- func main
- argv:[argc::]string
- do
+ func main(argv:[argc::]string)
print "Hello World, what lovely oceans you have!"
print "Are there", five, "?"
print pi, pie, "but", cake