struct token_config config;
char *file_name;
int parse_error;
- struct exec *prog;
## parse context
};
}
} else
ss = s; // NOTEST
+ if (!ss->code) {
+ fprintf(stderr, "oceani: no code found in requested section\n"); // NOTEST
+ exit(1); // 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);
while (s) {
struct section *t = s->next;
free(s);
s = t;
}
- ## free context vars
+ if (!context.parse_error) {
+ ## 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)
## 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 -
v->merged == secondary->merged) {
v->scope = OutScope;
v->merged = primary;
+ 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;
}
}
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
v->scope = InScope;
v->in_scope = c->in_scope;
c->in_scope = v;
+ ## variable init
return v;
}
return v;
}
- static void var_block_close(struct parse_context *c, enum closetype ct)
+ static void var_block_close(struct parse_context *c, enum closetype ct,
+ struct exec *e)
{
/* Close off all variables that are in_scope.
* Some variables in c->scope may already be not-in-scope,
* closed the scope.
*/
continue;
+ v->min_depth = c->scope_depth;
+ if (v->scope == InScope && e) {
+ /* 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 */
v->previous->scope == PendingScope)
/* all previous branches used name */
v->scope = PendingScope;
- else if (v->type == Tlabel) // UNTESTED
+ else if (v->type == Tlabel)
/* Labels remain pending even when not used */
v->scope = PendingScope; // UNTESTED
+ else
+ v->scope = OutScope;
if (ct == CloseElse) {
/* All Pending variables with this name
* are now Conditional */
* 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
for (v2 = v;
v2 && v2->scope == PendingScope;
v2 = v2->previous)
- if (v2->type == Tlabel) {
+ if (v2->type == Tlabel)
v2->scope = CondScope;
- } else
+ else
v2->scope = OutScope;
break;
case CondScope:
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); // NOTEST
t->prepare_type(c, t, 1); // NOTEST
if (c->global_size & (t->align - 1))
- c->global_size = (c->global_size + t->align) & ~(t->align-1); // 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
struct exec {
enum exec_types type;
int line, column;
+ ## exec fields
};
struct binode {
struct exec;
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;
## 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);
+ 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)
{
ret.lval = lrv;
ret.rval = rv;
ret.type = rvtype;
+ ## interp exec cleanup
return ret;
}
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;
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 (though results are not yet implemented). 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
+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;
+ 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, ")\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);
- 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);
- 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);
- 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 (currently Tnone).
+ */
+ 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 Tnone;
+ }
+
+###### 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;
+ right = interp_exec(c, fbody->function, &rtype);
+ 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 == 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;
}
struct value *val;
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) {
$0->forpart = $<FP;
$0->thenpart = $<TP;
$0->looppart = $<WP;
- var_block_close(c, CloseSequential);
+ var_block_close(c, CloseSequential, $0);
}$
| ForPart OptNL WhilePart CondSuffix ${
$0 = $<CS;
$0->forpart = $<FP;
$0->looppart = $<WP;
- var_block_close(c, CloseSequential);
+ var_block_close(c, CloseSequential, $0);
}$
| WhilePart CondSuffix ${
$0 = $<CS;
$0->condpart = $<SP;
$CP->next = $0->casepart;
$0->casepart = $<CP;
- var_block_close(c, CloseSequential);
+ 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);
+ 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 ${
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
ThenPart -> then OpenBlock ${
$0 = $<OB;
- var_block_close(c, CloseSequential);
+ var_block_close(c, CloseSequential, $0);
}$
$*binode
$0->op = Loop;
$0->left = $<UB;
$0->right = $<OB;
- var_block_close(c, CloseSequential);
- var_block_close(c, CloseSequential);
+ var_block_close(c, CloseSequential, $0->right);
+ var_block_close(c, CloseSequential, $0);
}$
| while OpenScope Expression OpenScope ColonBlock ${
$0 = new(binode);
$0->op = Loop;
$0->left = $<Exp;
$0->right = $<CB;
- var_block_close(c, CloseSequential);
- var_block_close(c, CloseSequential);
+ var_block_close(c, CloseSequential, $0->right);
+ var_block_close(c, CloseSequential, $0);
}$
$cond_statement
IfPart -> if UseBlock OptNL then OpenBlock ${
$0.condpart = $<UB;
$0.thenpart = $<OB;
- var_block_close(c, CloseParallel);
+ var_block_close(c, CloseParallel, $0.thenpart);
}$
| if OpenScope Expression OpenScope ColonBlock ${
$0.condpart = $<Ex;
$0.thenpart = $<CB;
- var_block_close(c, CloseParallel);
+ var_block_close(c, CloseParallel, $0.thenpart);
}$
| if OpenScope Expression OpenScope OptNL then Block ${
$0.condpart = $<Ex;
$0.thenpart = $<Bl;
- var_block_close(c, CloseParallel);
+ var_block_close(c, CloseParallel, $0.thenpart);
}$
$*exec
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
+
+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.
-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.
As this is the top level, several things are handled a bit
differently.
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 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);
+ global_alloc(c, name->type, name, &fn);
+ var_block_close(c, CloseSequential, code);
+ } else
+ var_block_close(c, CloseSequential, NULL);
+ return name;
+ }
+
###### 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, $<Bl);
+ }$
+ | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
+ $0 = declare_function(c, $<FN, $<Ar, $<Bl);
+ }$
+ | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
+ $0 = declare_function(c, $<FN, NULL, $<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 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, Tnone, 0);
+ } while (ok == 2);
+ if (ok)
+ /* Make sure everything is still consistent */
+ propagate_types(val->function, c, &ok, Tnone, 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 = 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 (!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);
}
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