struct token_config config;
char *file_name;
int parse_error;
+ struct exec *prog;
## parse context
};
},
};
int doprint=0, dotrace=0, doexec=1, brackets=0;
- struct exec **prog;
int opt;
while ((opt = getopt_long(argc, argv, options, long_options, NULL))
!= -1) {
break;
}
if (ss)
- prog = parse_oceani(ss->code, &context.config,
- dotrace ? stderr : NULL);
+ parse_oceani(ss->code, &context.config,
+ dotrace ? stderr : NULL);
else {
fprintf(stderr, "oceani: cannot find section %s\n",
section);
exit(1);
}
} else
- prog = parse_oceani(s->code, &context.config,
- dotrace ? stderr : NULL);
- if (!prog) {
- fprintf(stderr, "oceani: fatal parser error.\n");
+ parse_oceani(s->code, &context.config,
+ dotrace ? stderr : NULL);
+ if (!context.prog) {
+ fprintf(stderr, "oceani: no program found.\n");
context.parse_error = 1;
}
- if (prog && doprint)
- print_exec(*prog, 0, brackets);
- if (prog && doexec && !context.parse_error) {
- if (!analyse_prog(*prog, &context)) {
+ if (context.prog && doprint)
+ print_exec(context.prog, 0, brackets);
+ 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(*prog, argv+optind+1);
+ interp_prog(context.prog, argv+optind+1);
}
- if (prog) {
- free_exec(*prog);
- free(prog);
+ if (context.prog) {
+ free_exec(context.prog);
}
while (s) {
struct section *t = s->next;
}
fmt++;
switch (*fmt) {
- case '%': fputc(*fmt, stderr); break;
- default: fputc('?', stderr); break;
+ case '%': fputc(*fmt, stderr); break; // NOTEST
+ default: fputc('?', stderr); break; // NOTEST
case '1':
type_print(t1, stderr);
break;
else if (type->print_type)
type->print_type(type, f);
else
- fputs("*invalid*type*", f);
+ fputs("*invalid*type*", f); // NOTEST
}
static struct value val_prepare(struct type *type)
if (v.type && v.type->print)
v.type->print(v);
else
- printf("*Unknown*");
+ printf("*Unknown*"); // NOTEST
}
static struct value parse_value(struct type *type, char *arg)
if (type && type->parse)
return type->parse(type, arg);
- rv.type = NULL;
- return rv;
+ rv.type = NULL; // NOTEST
+ return rv; // NOTEST
}
###### forward decls
rv.type = type;
switch(type->vtype) {
- case Vnone:
- break;
+ case Vnone: // NOTEST
+ break; // NOTEST
case Vnum:
mpq_init(rv.num); break;
case Vstr:
case Vbool:
rv.bool = 0;
break;
- case Vlabel:
- rv.label = NULL;
- break;
+ case Vlabel: // NOTEST
+ rv.label = NULL; // NOTEST
+ break; // NOTEST
}
return rv;
}
struct value rv;
rv.type = v.type;
switch (rv.type->vtype) {
- case Vnone:
- break;
+ case Vnone: // NOTEST
+ break; // NOTEST
case Vlabel:
rv.label = v.label;
break;
{
int cmp;
if (left.type != right.type)
- return left.type - right.type;
+ return left.type - right.type; // NOTEST
switch (left.type->vtype) {
case Vlabel: cmp = left.label == right.label ? 0 : 1; break;
case Vnum: cmp = mpq_cmp(left.num, right.num); break;
case Vstr: cmp = text_cmp(left.str, right.str); break;
case Vbool: cmp = left.bool - right.bool; break;
- case Vnone: cmp = 0;
+ case Vnone: cmp = 0; // NOTEST
}
return cmp;
}
static void _print_value(struct value v)
{
switch (v.type->vtype) {
- case Vnone:
- printf("*no-value*"); break;
- case Vlabel:
- printf("*label-%p*", v.label); break;
+ case Vnone: // NOTEST
+ printf("*no-value*"); break; // NOTEST
+ case Vlabel: // NOTEST
+ printf("*label-%p*", v.label); break; // NOTEST
case Vstr:
printf("%.*s", v.str.len, v.str.txt); break;
case Vbool:
val.type = type;
switch(type->vtype) {
- case Vlabel:
- case Vnone:
- val.type = NULL;
- break;
+ case Vlabel: // NOTEST
+ case Vnone: // NOTEST
+ val.type = NULL; // NOTEST
+ break; // NOTEST
case Vstr:
val.str.len = strlen(arg);
val.str.txt = malloc(val.str.len);
v = t->previous;
free_value(t->val);
+ if (t->min_depth == 0)
+ free_exec(t->where_decl);
free(t);
}
}
static int __fput_loc(struct exec *loc, FILE *f)
{
if (!loc)
- return 0;
+ return 0; // NOTEST
if (loc->line >= 0) {
fprintf(f, "%d:%d: ", loc->line, loc->column);
return 1;
static void fput_loc(struct exec *loc, FILE *f)
{
if (!__fput_loc(loc, f))
- fprintf(f, "??:??: ");
+ fprintf(f, "??:??: "); // NOTEST
}
Each different type of `exec` node needs a number of functions
static void print_exec(struct exec *e, int indent, int bracket)
{
if (!e)
- return;
+ return; // NOTEST
switch (e->type) {
case Xbinode:
print_binode(cast(binode, e), indent, bracket); break;
struct binding *b = v->var->name;
fprintf(stderr, "%.*s", b->name.len, b->name.txt);
} else
- fputs("???", stderr);
+ fputs("???", stderr); // NOTEST
} else
- fputs("NOTVAR", stderr);
+ fputs("NOTVAR", stderr); // NOTEST
break;
###### propagate exec cases
struct var *var = cast(var, prog);
struct variable *v = var->var;
if (!v) {
- type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone);
- *ok = 0;
- return Tnone;
+ type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone); // NOTEST
+ *ok = 0; // NOTEST
+ return Tnone; // NOTEST
}
if (v->merged)
v = v->merged;
case GtrEq: printf(" >= "); break;
case Eql: printf(" == "); break;
case NEql: printf(" != "); break;
- default: abort();
+ default: abort(); // NOTEST
}
print_exec(b->right, -1, 0);
break;
case GtrEq: rv.bool = cmp >= 0; break;
case Eql: rv.bool = cmp == 0; break;
case NEql: rv.bool = cmp != 0; break;
- default: rv.bool = 0; break;
+ default: rv.bool = 0; break; // NOTEST
}
break;
}
case Divide: fputs(" / ", stdout); break;
case Rem: fputs(" % ", stdout); break;
case Concat: fputs(" ++ ", stdout); break;
- default: abort();
- }
+ default: abort(); // NOTEST
+ } // NOTEST
print_exec(b->right, indent, 0);
break;
case Absolute:
free_value(*lleft);
*lleft = right;
} else
- free_value(right);
+ free_value(right); // NOTEST
right.type = NULL;
break;
break;
}
+### Top level structure
+
+All the language elements so far can be used in various places. Now
+it is time to clarify what those places are.
+
+At the top level of a file there will be a number of declarations.
+Many of the things that can be declared haven't been described yet,
+such as functions, procedures, imports, named types, and probably
+more.
+For now there are two sorts of things that can appear at the top
+level. They are predefined constants and the main program. While the
+syntax will allow the main program to appear multiple times, that will
+trigger an error if it is actually attempted.
+
+The various declarations do not return anything. They store the
+various declarations in the parse context.
+
+###### Parser: grammar
+
+ $void
+ Ocean -> DeclarationList
+
+ DeclarationList -> Declaration
+ | DeclarationList Declaration
+
+ Declaration -> DeclareConstant
+ | DeclareProgram
+ | NEWLINE
+
+ ## top level grammar
+
+### The `const` section
+
+As well as being defined in with the code that uses them, constants
+can be declared at the top level. These have full-file scope, so they
+are always `InScope`. The value of a top level constant can be given
+as an expression, and this is evaluated immediately rather than in the
+later interpretation stage. Once we add functions to the language, we
+will need rules concern which, if any, can be used to define a top
+level constant.
+
+Constants are defined in a sectiont that starts with the reserved word
+`const` and then has a block with a list of assignment statements.
+For syntactic consistency, these must use the double-colon syntax to
+make it clear that they are constants. Type can also be given: if
+not, the type will be determined during analysis, as with other
+constants.
+
+###### top level grammar
+
+ DeclareConstant -> const Open ConstList Close
+ | const Open Newlines ConstList Close
+ | const Open SimpleConstList }
+ | const Open Newlines SimpleConstList }
+ | const : ConstList
+ | const SimpleConstList
+
+ ConstList -> ComplexConsts
+ ComplexConsts -> ComplexConst ComplexConsts
+ | ComplexConst
+ ComplexConst -> SimpleConstList NEWLINE
+ SimpleConstList -> Const ; SimpleConstList
+ | Const
+ | Const ; SimpleConstList ;
+
+ $*type
+ CType -> Type ${ $0 = $<1; }$
+ | ${ $0 = NULL; }$
+ $void
+ Const -> IDENTIFIER :: CType = Expression ${ {
+ int ok;
+ struct variable *v;
+
+ v = var_decl(config2context(config), $1.txt);
+ if (v) {
+ struct var *var = new_pos(var, $1);
+ v->where_decl = var;
+ v->where_set = var;
+ var->var = v;
+ v->constant = 1;
+ } else {
+ v = var_ref(config2context(config), $1.txt);
+ tok_err(config2context(config), "error: name already declared", &$1);
+ type_err(config2context(config), "info: this is where '%v' was first declared",
+ v->where_decl, NULL, 0, NULL);
+ }
+ do {
+ ok = 1;
+ propagate_types($5, config2context(config), &ok, $3, 0);
+ } while (ok == 2);
+ if (!ok)
+ config2context(config)->parse_error = 1;
+ else if (v) {
+ v->val = interp_exec($5);
+ }
+ } }$
+
+
### Finally the whole program.
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.
+is the code to execute. Unlike Pascal, constants and other
+declarations come *before* the program.
As this is the top level, several things are handled a bit
differently.
###### Binode types
Program,
-###### Parser: grammar
+###### top level grammar
+
+ DeclareProgram -> Program ${ {
+ struct parse_context *c = config2context(config);
+ if (c->prog)
+ type_err(c, "Program defined a second time",
+ $1, NULL, 0, NULL);
+ else
+ c->prog = $<1;
+ } }$
+
$*binode
Program -> program OpenScope Varlist Block OptNL ${
break;
###### propagate binode cases
- case Program: abort();
+ case Program: abort(); // NOTEST
###### core functions
int ok = 1;
if (!b)
- return 0;
+ return 0; // NOTEST
do {
ok = 1;
propagate_types(b->right, c, &ok, Tnone, 0);
struct value v;
if (!prog)
- return;
+ return; // NOTEST
al = cast(binode, p->left);
while (al) {
struct var *v = cast(var, al->left);
}
###### interp binode cases
- case Program: abort();
+ case Program: abort(); // NOTEST
## And now to test it out.
###### test: hello
+ const:
+ pi ::= 3.1415926
+ four ::= 2 + 2 ; five ::= 10/2
+ const pie ::= "I like Pie";
+ cake ::= "The cake is"
+ ++ " a lie"
+
program A B:
print "Hello World, what lovely oceans you have!"
+ print "are there", five, "?"
+ print pi, pie, "but", cake
+
/* When a variable is defined in both branches of an 'if',
* and used afterwards, the variables are merged.
*/