]> ocean-lang.org Git - ocean/commitdiff
oceani: add structs
authorNeilBrown <neil@brown.name>
Sat, 18 May 2019 14:16:01 +0000 (00:16 +1000)
committerNeilBrown <neil@brown.name>
Sat, 18 May 2019 14:16:01 +0000 (00:16 +1000)
This is just basic struct support, showing how they
are declared and used.

There is more sophistication to be added such as anonymous
fields and per-field attributes.

Signed-off-by: NeilBrown <neil@brown.name>
csrc/oceani-tests.mdc
csrc/oceani.mdc

index 102c957b0b4e459327e1418aa1f7e4b33ba3dde8..5be4c5aa89d488467463974cb5a7c28f5a76c495 100644 (file)
@@ -499,6 +499,46 @@ ad-hoc things array related.
 ###### 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
@@ -634,6 +674,10 @@ various places that `type_err()` are called.
 
 ###### test: type_err3
 
+       struct foo:
+               a: number
+               b:string = "hello"
+
        program:
                c := "hello"
                c = c + 1
@@ -659,30 +703,37 @@ various places that `type_err()` are called.
                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
@@ -777,4 +828,3 @@ command list possible.
 
 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
        oceani: cannot find section toast:nothing
-
index 6b43096b13cd2e1fff4cf3df214d35b9db6e02d3..ffa1bbf46e690af76411c61625404a87804c0bfc 100644 (file)
@@ -238,6 +238,7 @@ structures can be used.
                        context.parse_error = 1;
                }
                if (context.prog && doprint) {
+                       ## print const decls
                        ## print decls
                        print_exec(context.prog, 0, brackets);
                }
@@ -445,10 +446,12 @@ which are often passed around by value.
                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);
@@ -598,6 +601,8 @@ which are often passed around by value.
                struct type *t = context.typelist;
 
                context.typelist = t->next;
+               if (t->free_type)
+                       t->free_type(t);
                free(t);
        }
 
@@ -1534,7 +1539,7 @@ Now that we have the shape of the interpreter in place we can add some
 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
 
@@ -1759,6 +1764,324 @@ make a copy of an array with controllable depth.
                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,
@@ -3574,6 +3897,7 @@ various declarations in the parse context.
 
        Declaration -> DeclareConstant
                | DeclareProgram
+               | DeclareStruct
                | NEWLINE
 
        ## top level grammar
@@ -3650,7 +3974,7 @@ searching through for the Nth constant for decreasing N.
                }
        } }$
 
-###### print decls
+###### print const decls
        {
                struct variable *v;
                int target = -1;
@@ -3851,6 +4175,11 @@ Fibonacci, and performs a binary search for a number.
                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, "?"
@@ -3942,3 +4271,8 @@ Fibonacci, and performs a binary search for a number.
                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