}
}
- 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
###### 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':
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
#### Functions
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.
+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.
struct {
struct binode *params;
+ struct type *return_type;
int local_size;
} function;
if (b->right)
fprintf(f, "; ");
}
- fprintf(f, ")\n");
+ 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)
}
} }$
-
$*binode
- Args -> ${ $0 = NULL; }$
+ 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 NEWLINE ${ $0 = $<1; }$
- Varlist -> Varlist ; ArgDecl ${ // UNTESTED
+ Varlist -> Varlist ; ArgDecl ${
$0 = new(binode);
$0->op = List;
$0->left = $<Vl;
case Funcall: {
/* Every arg must match formal parameter, and result
- * is return type of function (currently Tnone).
+ * is return type of function
*/
struct binode *args = cast(binode, b->right);
struct var *v = cast(var, b->left);
return NULL;
}
v->var->type->check_args(c, ok, v->var->type, args);
- return Tnone;
+ return v->var->type->function.return_type;
}
###### interp binode cases
arg = cast(binode, arg->right);
}
c->local = local; c->local_size = t->function.local_size;
- right = interp_exec(c, fbody->function, &rtype);
+ rv = interp_exec(c, fbody->function, &rvtype);
c->local = oldlocal; c->local_size = old_size;
free(local);
break;
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)
### 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 ${
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.
+
+###### 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
+ var_block_close(c, CloseSequential, NULL);
+ return name;
+ }
-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.
+###### declare terminals
+ $TERM return
###### top level grammar
$*variable
- DeclareFunction -> func FuncName ( OpenScope Args ) Block Newlines ${ {
- struct text funcname = { " func", 5};
- $0 = $<FN;
- if ($0) {
- struct value fn = {.function = $<Bl};
- $0->type = add_type(c, funcname, &function_prototype);
- $0->type->function.params = reorder_bilist($<Ar);
- global_alloc(c, $0->type, $0, &fn);
- var_block_close(c, CloseSequential, fn.function);
- } else
- var_block_close(c, CloseSequential, NULL);
- } }$
- | func FuncName then IN OpenScope OptNL Args OUT OptNL do Block Newlines ${ {
- // FIXME that 'then' should not be there.
- struct text funcname = { " func", 5};
- $0 = $<FN;
- if ($0) {
- struct value fn = {.function = $<Bl};
- $0->type = add_type(c, funcname, &function_prototype);
- $0->type->function.params = reorder_bilist($<Ar);
- global_alloc(c, $0->type, $0, &fn);
- var_block_close(c, CloseSequential, fn.function);
- } else
- var_block_close(c, CloseSequential, NULL);
- } }$
- | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${ {
- struct text funcname = { " func", 5};
- $0 = $<FN;
- if ($0) {
- struct value fn = {.function = $<Bl};
- $0->type = add_type(c, funcname, &function_prototype);
- $0->type->function.params = NULL;
- global_alloc(c, $0->type, $0, &fn);
- var_block_close(c, CloseSequential, fn.function);
- } else
- var_block_close(c, CloseSequential, NULL);
- } }$
+ 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 func decls
{
static int analyse_funcs(struct parse_context *c)
{
struct variable *v;
- int ok = 1;
- for (v = c->in_scope; ok && v; v = v->in_scope) {
+ 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);
+ 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, Tnone, 0);
+ 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 ok;
+ return all_ok;
}
static int analyse_main(struct type *type, struct parse_context *c)
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]);