###### output: arrays
False True False False False
+## Structures
+
+Time to test if structure declarations and accesses work correctly.
+
+###### test list
+ oceani_tests += structs
+
+###### test: structs
+
+ struct foo:
+ size:[3]number
+ name:string
+ active:Boolean
+
+ struct baz { a:number; b:Boolean; }
+
+ program:
+ info:[4]foo
+
+ for i:=0; then i=i+1; while i < 4:
+ switch i
+ case 2: nm:= "peter"
+ case 0: nm:= "bob"
+ case 1: nm:= "jane"
+ else: nm:= "janine"
+
+ info[i].name = nm
+ info[i].size[0] = i*i
+ info[i].active = nm == "jane"
+
+ for i:=0; then i=i+1; while i < 4:
+ print info[i].name, info[i].active, info[i].size[0]
+
+###### output: structs
+
+ bob False 0
+ jane True 1
+ peter False 4
+ janine False 9
+
## Test code with syntax errors
Syntax errors aren't handled well yet - the result is almost always a
###### test: type_err3
+ struct foo:
+ a: number
+ b:string = "hello"
+
program:
c := "hello"
c = c + 1
a4[1] = True
c = a2[3]
+ bar:foo
+ foo.c = 43
+ print c.foo
+
###### output: type_err3
- .tmp.code:4:12: error: expected number but variable 'c' is string
- .tmp.code:3:8: info: this is where 'c' was set to string
- .tmp.code:4:12: error: Arithmetic returns number but string expected
- .tmp.code:3:8: info: variable 'c' was set as string here.
- .tmp.code:5:24: error: Boolean operation found where string expected
- .tmp.code:6:12: error: Comparison returns Boolean but string expected
- .tmp.code:3:8: info: variable 'c' was set as string here.
- .tmp.code:7:21: error: Concat returns string but number expected
- .tmp.code:8:8: error: string cannot be indexed
- .tmp.code:8:8: error: string cannot be indexed
- .tmp.code:17:13: error: expected number found string
- .tmp.code:13:16: error: expected number, found string
- .tmp.code:20:8: error: cannot assign value of type [5]number
- .tmp.code:21:13: error: expected [5]number but variable 'a3' is [10]number
- .tmp.code:19:36: info: this is where 'a3' was set to [10]number
- .tmp.code:21:8: error: cannot assign value of type [5]number
- .tmp.code:22:13: error: expected [5]number but variable 'a4' is [5]string
- .tmp.code:19:51: info: this is where 'a4' was set to [5]string
- .tmp.code:22:8: error: cannot assign value of type [5]number
- .tmp.code:23:16: error: expected number found string
- .tmp.code:24:16: error: expected string found Boolean
- .tmp.code:25:12: error: have number but need string
- .tmp.code:3:8: info: variable 'c' was set as string here.
+ .tmp.code:8:12: error: expected number but variable 'c' is string
+ .tmp.code:7:8: info: this is where 'c' was set to string
+ .tmp.code:8:12: error: Arithmetic returns number but string expected
+ .tmp.code:7:8: info: variable 'c' was set as string here.
+ .tmp.code:9:24: error: Boolean operation found where string expected
+ .tmp.code:10:12: error: Comparison returns Boolean but string expected
+ .tmp.code:7:8: info: variable 'c' was set as string here.
+ .tmp.code:11:21: error: Concat returns string but number expected
+ .tmp.code:12:8: error: string cannot be indexed
+ .tmp.code:12:8: error: string cannot be indexed
+ .tmp.code:21:13: error: expected number found string
+ .tmp.code:17:16: error: expected number, found string
+ .tmp.code:24:8: error: cannot assign value of type [5]number
+ .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
+ .tmp.code:23:36: info: this is where 'a3' was set to [10]number
+ .tmp.code:25:8: error: cannot assign value of type [5]number
+ .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
+ .tmp.code:23:51: info: this is where 'a4' was set to [5]string
+ .tmp.code:26:8: error: cannot assign value of type [5]number
+ .tmp.code:27:16: error: expected number found string
+ .tmp.code:28:16: error: expected string found Boolean
+ .tmp.code:29:12: error: have number but need string
+ .tmp.code:7:8: info: variable 'c' was set as string here.
+ .tmp.code:32:8: error: field reference attempted on label, not a struct
+ .tmp.code:32:16: error: expected none found number
+ .tmp.code:33:14: error: field reference attempted on string, not a struct
oceani: type error in program - not running.
###### test: type_err4
###### output: cmd,--section,toast:nothing,oceani-tests.mdc
oceani: cannot find section toast:nothing
-
context.parse_error = 1;
}
if (context.prog && doprint) {
+ ## print const decls
## print decls
print_exec(context.prog, 0, brackets);
}
struct value (*parse)(struct type *type, char *str);
void (*print)(struct value val);
void (*print_type)(struct type *type, FILE *f);
+ void (*print_type_decl)(struct type *type, FILE *f);
int (*cmp_order)(struct value v1, struct value v2);
int (*cmp_eq)(struct value v1, struct value v2);
struct value (*dup)(struct value val);
void (*free)(struct value val);
+ void (*free_type)(struct type *t);
int (*compat)(struct type *this, struct type *other);
long long (*to_int)(struct value *v);
double (*to_float)(struct value *v);
struct type *t = context.typelist;
context.typelist = t->next;
+ if (t->free_type)
+ t->free_type(t);
free(t);
}
complex types and connected them in to the data structures and the
different phases of parse, analyse, print, interpret.
-For now, just arrays.
+Thus far we have arrays and structs.
#### Arrays
break;
}
+#### Structs
+
+A `struct` is a data-type that contains one or more other data-types.
+It differs from an array in that each member can be of a different
+type, and they are accessed by name rather than by number. Thus you
+cannot choose and element by calculation, you need to know what you
+want up-front.
+
+The language makes no promises about how a give structure will be
+stored in memory - it is free to rearrange fields to suit whatever
+criteria seems important.
+
+Structs are declared separately from program code - they cannot be
+declared in-line in a variable declaration like arrays can. A struct
+is given a name and this name is used to identify the type - the name
+is not prefixed by the word `struct` as it would be in C.
+
+Each component datum is identified much like a variable is declared,
+with a name, one or to colons, and a type. The type cannot be omitted
+as there is no opportunity to deduce the type from usage. An initial
+value can be given following an equals sign, so
+
+Structs are only treated as the same if they have the same name.
+Simply having the same fields in the same order is not enough. This
+might change once we can create structure initializes from a list of
+values.
+
+##### Example: a struct type
+
+ struct complex:
+ x:number = 0
+ y:number = 0
+
+would declare a type called "complex" which has two number fields,
+each initialised to zero.
+
+###### type union fields
+
+ struct {
+ int nfields;
+ struct field {
+ struct text name;
+ struct type *type;
+ struct value init;
+ } *fields;
+ } structure;
+
+###### value union fields
+ struct {
+ struct value *fields;
+ } structure;
+
+###### value functions
+
+ static struct value structure_prepare(struct type *type)
+ {
+ struct value ret;
+
+ ret.type = type;
+ ret.structure.fields = NULL;
+ return ret;
+ }
+
+ static struct value structure_init(struct type *type)
+ {
+ struct value ret;
+ int i;
+
+ ret.type = type;
+ ret.structure.fields = calloc(type->structure.nfields,
+ sizeof(ret.structure.fields[0]));
+ for (i = 0; ret.structure.fields && i < type->structure.nfields; i++)
+ ret.structure.fields[i] = val_init(type->structure.fields[i].type);
+ return ret;
+ }
+
+ static void structure_free(struct value val)
+ {
+ int i;
+
+ if (val.structure.fields)
+ for (i = 0; i < val.type->structure.nfields; i++)
+ free_value(val.structure.fields[i]);
+ free(val.structure.fields);
+ }
+
+ static void structure_free_type(struct type *t)
+ {
+ int i;
+ for (i = 0; i < t->structure.nfields; i++)
+ free_value(t->structure.fields[i].init);
+ free(t->structure.fields);
+ }
+
+ static struct type structure_prototype = {
+ .prepare = structure_prepare,
+ .init = structure_init,
+ .free = structure_free,
+ .free_type = structure_free_type,
+ .print_type_decl = structure_print_type,
+ };
+
+###### exec type
+ Xfieldref,
+
+###### ast
+ struct fieldref {
+ struct exec;
+ struct exec *left;
+ int index;
+ struct text name;
+ };
+
+###### free exec cases
+ case Xfieldref:
+ free_exec(cast(fieldref, e)->left);
+ free(e);
+ break;
+
+###### variable grammar
+
+ | Variable . IDENTIFIER ${ {
+ struct fieldref *fr = new_pos(fieldref, $2);
+ fr->left = $<1;
+ fr->name = $3.txt;
+ fr->index = -2;
+ $0 = fr;
+ } }$
+
+###### print exec cases
+
+ case Xfieldref:
+ {
+ struct fieldref *f = cast(fieldref, e);
+ print_exec(f->left, -1, 0);
+ printf(".%.*s", f->name.len, f->name.txt);
+ break;
+ }
+
+###### ast functions
+ static int find_struct_index(struct type *type, struct text field)
+ {
+ int i;
+ for (i = 0; i < type->structure.nfields; i++)
+ if (text_cmp(type->structure.fields[i].name, field) == 0)
+ return i;
+ return -1;
+ }
+
+###### propagate exec cases
+
+ case Xfieldref:
+ {
+ struct fieldref *f = cast(fieldref, prog);
+ struct type *st = propagate_types(f->left, c, ok, NULL, 0);
+
+ if (!st)
+ type_err(c, "error: unknown type for field access", f->left,
+ NULL, 0, NULL);
+ else if (st->prepare != structure_prepare)
+ type_err(c, "error: field reference attempted on %1, not a struct",
+ f->left, st, 0, NULL);
+ else if (f->index == -2) {
+ f->index = find_struct_index(st, f->name);
+ if (f->index < 0) {
+ type_err(c, "error: cannot find requested field in %1",
+ f->left, st, 0, NULL);
+ *ok = 0;
+ }
+ }
+ if (f->index >= 0) {
+ struct type *ft = st->structure.fields[f->index].type;
+ if (!type_compat(type, ft, rules)) {
+ type_err(c, "error: have %1 but need %2", prog,
+ ft, rules, type);
+ *ok = 0;
+ }
+ return ft;
+ }
+ break;
+ }
+
+###### interp exec cases
+ case Xfieldref:
+ {
+ struct fieldref *f = cast(fieldref, e);
+ struct value *lleft = linterp_exec(f->left);
+ lrv = &lleft->structure.fields[f->index];
+ break;
+ }
+
+###### ast
+ struct fieldlist {
+ struct fieldlist *prev;
+ struct field f;
+ };
+
+###### ast functions
+ static void free_fieldlist(struct fieldlist *f)
+ {
+ if (!f)
+ return;
+ free_fieldlist(f->prev);
+ free_value(f->f.init);
+ free(f);
+ }
+
+###### top level grammar
+ DeclareStruct -> struct IDENTIFIER FieldBlock ${ {
+ struct type *t =
+ add_type(config2context(config), $2.txt, &structure_prototype);
+ int cnt = 0;
+ struct fieldlist *f;
+
+ for (f = $3; f; f=f->prev)
+ cnt += 1;
+
+ t->structure.nfields = cnt;
+ t->structure.fields = calloc(cnt, sizeof(struct field));
+ f = $3;
+ while (cnt > 0) {
+ cnt -= 1;
+ t->structure.fields[cnt] = f->f;
+ f->f.init = val_prepare(Tnone);
+ f = f->prev;
+ }
+ } }$
+
+ $*fieldlist
+ FieldBlock -> Open SimpleFieldList Close ${ $0 = $<2; }$
+ | Open Newlines SimpleFieldList Close ${ $0 = $<3; }$
+ | : FieldList ${ $0 = $<2; }$
+
+ FieldList -> Field NEWLINE ${ $0 = $<1; }$
+ | FieldList NEWLINE ${ $0 = $<1; }$
+ | FieldList Field NEWLINE ${
+ $2->prev = $<1;
+ $0 = $<2;
+ }$
+
+ SimpleFieldList -> Field ; ${ $0 = $<1; }$
+ | SimpleFieldList Field ; ${
+ $2->prev = $<1;
+ $0 = $<2;
+ }$
+
+ Field -> IDENTIFIER : Type = Expression ${ {
+ int ok;
+
+ $0 = calloc(1, sizeof(struct fieldlist));
+ $0->f.name = $1.txt;
+ $0->f.type = $<3;
+ $0->f.init = val_prepare($0->f.type);
+ do {
+ ok = 1;
+ propagate_types($<5, config2context(config), &ok, $3, 0);
+ } while (ok == 2);
+ if (!ok)
+ config2context(config)->parse_error = 1;
+ else
+ $0->f.init = interp_exec($5);
+ } }$
+ | IDENTIFIER : Type ${
+ $0 = calloc(1, sizeof(struct fieldlist));
+ $0->f.name = $1.txt;
+ $0->f.type = $<3;
+ $0->f.init = val_init($3);
+ }$
+
+###### forward decls
+ static void structure_print_type(struct type *t, FILE *f);
+
+###### value functions
+ static void structure_print_type(struct type *t, FILE *f)
+ {
+ int i;
+
+ fprintf(f, "struct %.*s:\n", t->name.len, t->name.txt);
+
+ for (i = 0; i < t->structure.nfields; i++) {
+ struct field *fl = t->structure.fields + i;
+ fprintf(f, " %.*s : ", fl->name.len, fl->name.txt);
+ type_print(fl->type, f);
+ if (fl->init.type->print) {
+ fprintf(f, " = ");
+ if (fl->init.type == Tstr)
+ fprintf(f, "\"");
+ print_value(fl->init);
+ if (fl->init.type == Tstr)
+ fprintf(f, "\"");
+ }
+ printf("\n");
+ }
+ }
+
+###### print decls
+ {
+ struct type *t;
+ int target = -1;
+
+ while (target != 0) {
+ int i = 0;
+ for (t = context.typelist; t ; t=t->next)
+ if (t->print_type_decl) {
+ i += 1;
+ if (i == target)
+ break;
+ }
+
+ if (target == -1) {
+ target = i;
+ } else {
+ t->print_type_decl(t, stdout);
+ target -= 1;
+ }
+ }
+ }
+
## Language elements
Each language element needs to be parsed, printed, analysed,
Declaration -> DeclareConstant
| DeclareProgram
+ | DeclareStruct
| NEWLINE
## top level grammar
}
} }$
-###### print decls
+###### print const decls
{
struct variable *v;
int target = -1;
cake ::= "The cake is"
++ " a lie"
+ struct fred:
+ size:[four]number
+ name:string
+ alive:Boolean
+
program A B:
print "Hello World, what lovely oceans you have!"
print "are there", five, "?"
print "After sort:"
for i:=0; then i = i + 1; while i < size:
print "list[",i,"]=",list[i]
+
+ bob:fred
+ bob.name = "Hello"
+ bob.alive = (bob.name == "Hello")
+ print bob.alive