From: NeilBrown Date: Mon, 22 Apr 2019 01:17:03 +0000 (+1000) Subject: oceani: initial support for named types. X-Git-Tag: JamisonCreek-3~43 X-Git-Url: https://ocean-lang.org/code/?p=ocean;a=commitdiff_plain;h=e540d72cf62c4e120b2ff8c661ee60316e486650 oceani: initial support for named types. The base types are now stored in a symbol table (linked list) and can be fetched by name. name:type = value now works to declare 'name' as of the given type. The narrative needs to be improved to include a clear section on types, and variable declarations of the form name:type with no assignment still need to be added. Signed-off-by: NeilBrown --- diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index 062eaed..c4f7e29 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -250,7 +250,8 @@ option. free(s); s = t; } - ## free context + ## free context vars + ## free context types exit(context.parse_error ? 1 : 0); } @@ -365,10 +366,17 @@ in too. case '%': fputc(*fmt, stderr); break; default: fputc('?', stderr); break; case '1': - fputs(t1 ? t1->name : "*unknown*", stderr); + if (t1) + fprintf(stderr, "%.*s", t1->name.len, t1->name.txt); + else + fputs("*unknown*", stderr); break; case '2': - fputs(t2 ? t2->name : "*unknown*", stderr); + if (t2) + fprintf(stderr, "%.*s", t2->name.len, t2->name.txt); + else + fputs("*unknown*", stderr); + break; break; ## format cases } @@ -410,10 +418,13 @@ as one of a few standard types: integer, float, and fraction. The existance of these conversion functions enable types to determine if they are compatible with other types. +Named type are stored in a simple linked list. + ###### ast struct type { - char *name; + struct text name; + struct type *next; struct value (*init)(struct type *type); struct value (*parse)(struct type *type, char *str); void (*print)(struct value val); @@ -425,9 +436,65 @@ they are compatible with other types. long long (*to_int)(struct value *v); double (*to_float)(struct value *v); int (*to_mpq)(mpq_t *q, struct value *v); - ## type fields + union { + ## type union fields + }; + }; + + struct typep { + struct type *t; }; +###### parse context + + struct type *typelist; + +###### ast functions + + static struct type *find_type(struct parse_context *c, struct text s) + { + struct type *l = c->typelist; + + while (l && + text_cmp(l->name, s) != 0) + l = l->next; + return l; + } + + static struct type *add_type(struct parse_context *c, struct text s, + struct type *proto) + { + struct type *n; + + n = calloc(1, sizeof(*n)); + *n = *proto; + n->name = s; + n->next = c->typelist; + c->typelist = n; + return n; + } + + static void free_type(struct type *t) + { + /* The type is always a reference to something in the + * context, so we don't need to free anything. + */ + } + + static void context_init(struct parse_context *c) + { + ## context initialization + } + +###### free context types + + while (context.typelist) { + struct type *t = context.typelist; + + context.typelist = t->next; + free(t); + } + ### Values Values can be numbers, which we represent as multi-precision @@ -459,7 +526,7 @@ to parse each type from a string. myLDLIBS := libnumber.o libstring.o -lgmp LDLIBS := $(filter-out $(myLDLIBS),$(LDLIBS)) $(myLDLIBS) -###### type fields +###### type union fields enum vtype {Vnone, Vstr, Vnum, Vbool, Vlabel} vtype; ###### ast @@ -501,9 +568,9 @@ to parse each type from a string. static int vtype_compat(struct type *require, struct type *have, int rules) { - if ((rules & Rboolok) && have == &Tbool) + if ((rules & Rboolok) && have == Tbool) return 1; - if ((rules & Rnolabel) && have == &Tlabel) + if ((rules & Rnolabel) && have == Tlabel) return 0; if (!require || !have) return 1; @@ -519,7 +586,8 @@ to parse each type from a string. rv.type = type; switch(type->vtype) { - case Vnone:abort(); + case Vnone: + break; case Vnum: mpq_init(rv.num); break; case Vstr: @@ -705,44 +773,39 @@ to parse each type from a string. static void _free_value(struct value v); - #define BaseType \ - .init = _val_init, \ - .parse = _parse_value, \ - .print = _print_value, \ - .cmp_order = _value_cmp, \ - .cmp_eq = _value_cmp, \ - .dup = _dup_value, \ - .free = _free_value, \ - - static struct type Tbool = { - BaseType - .name = "Boolean", - .vtype = Vbool, + static struct type base_prototype = { + .init = _val_init, + .parse = _parse_value, + .print = _print_value, + .cmp_order = _value_cmp, + .cmp_eq = _value_cmp, + .dup = _dup_value, + .free = _free_value, }; - static struct type Tstr = { - BaseType - .name = "string", - .vtype = Vstr, - }; + static struct type *Tbool, *Tstr, *Tnum, *Tnone, *Tlabel; - static struct type Tnum = { - BaseType - .name = "number", - .vtype = Vnum, - }; +###### ast functions + static struct type *add_base_type(struct parse_context *c, char *n, enum vtype vt) + { + struct text txt = { n, strlen(n) }; + struct type *t; - static struct type Tnone = { - BaseType - .name = "none", - .vtype = Vnone, - }; + t = add_type(c, txt, &base_prototype); + t->vtype = vt; + return t; + } - static struct type Tlabel = { - BaseType - .name = "label", - .vtype = Vlabel, - }; +###### forward decls + static struct type *add_base_type(struct parse_context *c, char *n, enum vtype vt); + +###### context initialization + + Tbool = add_base_type(c, "Boolean", Vbool); + Tstr = add_base_type(c, "string", Vstr); + Tnum = add_base_type(c, "number", Vnum); + Tnone = add_base_type(c, "none", Vnone); + Tlabel = add_base_type(c, "label", Vlabel); ### Variables @@ -964,7 +1027,7 @@ no longer be primary. } } -###### free context +###### free context vars while (context.varlist) { struct binding *b = context.varlist; @@ -1102,7 +1165,7 @@ all pending-scope variables become conditionally scoped. else if (v->previous && v->previous->scope == PendingScope) v->scope = PendingScope; - else if (v->val.type == &Tlabel) + else if (v->val.type == Tlabel) v->scope = PendingScope; else if (v->name->var == v) v->scope = OutScope; @@ -1119,14 +1182,14 @@ all pending-scope variables become conditionally scoped. for (v2 = v; v2 && v2->scope == PendingScope; v2 = v2->previous) - if (v2->val.type != &Tlabel) + if (v2->val.type != Tlabel) v2->scope = OutScope; break; case OutScope: break; } break; case CloseSequential: - if (v->val.type == &Tlabel) + if (v->val.type == Tlabel) v->scope = PendingScope; switch (v->scope) { case InScope: @@ -1141,7 +1204,7 @@ all pending-scope variables become conditionally scoped. for (v2 = v; v2 && v2->scope == PendingScope; v2 = v2->previous) - if (v2->val.type == &Tlabel) { + if (v2->val.type == Tlabel) { v2->scope = CondScope; v2->min_depth = c->scope_depth; } else @@ -1326,7 +1389,7 @@ propagation is needed. struct type *t; if (!prog) - return &Tnone; + return Tnone; switch (prog->type) { case Xbinode: @@ -1339,7 +1402,7 @@ propagation is needed. } ## propagate exec cases } - return &Tnone; + return Tnone; } #### Interpreting @@ -1357,7 +1420,7 @@ Each `exec` can return a value, which may be `Tnone` but must be non-NULL; static struct value interp_exec(struct exec *e) { struct value rv; - rv.type = &Tnone; + rv.type = Tnone; if (!e) return rv; @@ -1366,7 +1429,7 @@ Each `exec` can return a value, which may be `Tnone` but must be non-NULL; { struct binode *b = cast(binode, e); struct value left, right; - left.type = right.type = &Tnone; + left.type = right.type = Tnone; switch (b->op) { ## interp binode cases } @@ -1405,17 +1468,17 @@ an executable. $*val Value -> True ${ $0 = new_pos(val, $1); - $0->val.type = &Tbool; + $0->val.type = Tbool; $0->val.bool = 1; }$ | False ${ $0 = new_pos(val, $1); - $0->val.type = &Tbool; + $0->val.type = Tbool; $0->val.bool = 0; }$ | NUMBER ${ $0 = new_pos(val, $1); - $0->val.type = &Tnum; + $0->val.type = Tnum; { char tail[3]; if (number_parse($0->val.num, tail, $1.txt) == 0) @@ -1427,7 +1490,7 @@ an executable. }$ | STRING ${ $0 = new_pos(val, $1); - $0->val.type = &Tstr; + $0->val.type = Tstr; { char tail[3]; string_parse(&$1, '\\', &$0->val.str, tail); @@ -1438,7 +1501,7 @@ an executable. }$ | MULTI_STRING ${ $0 = new_pos(val, $1); - $0->val.type = &Tstr; + $0->val.type = Tstr; { char tail[3]; string_parse(&$1, '\\', &$0->val.str, tail); @@ -1452,10 +1515,10 @@ an executable. case Xval: { struct val *v = cast(val, e); - if (v->val.type == &Tstr) + if (v->val.type == Tstr) printf("\""); print_value(v->val); - if (v->val.type == &Tstr) + if (v->val.type == Tstr) printf("\""); break; } @@ -1540,9 +1603,9 @@ link to find the primary instance. v = var_ref(config2context(config), $1.txt); $0->var = v; type_err(config2context(config), "error: variable '%v' redeclared", - $0, &Tnone, 0, &Tnone); + $0, Tnone, 0, Tnone); type_err(config2context(config), "info: this is where '%v' was first declared", - v->where_decl, &Tnone, 0, &Tnone); + v->where_decl, Tnone, 0, Tnone); } } }$ | IDENTIFIER ::= ${ { @@ -1556,9 +1619,44 @@ link to find the primary instance. v = var_ref(config2context(config), $1.txt); $0->var = v; type_err(config2context(config), "error: variable '%v' redeclared", - $0, &Tnone, 0, &Tnone); + $0, Tnone, 0, Tnone); type_err(config2context(config), "info: this is where '%v' was first declared", - v->where_decl, &Tnone, 0, &Tnone); + v->where_decl, Tnone, 0, Tnone); + } + } }$ + | IDENTIFIER : Type = ${ { + struct variable *v = var_decl(config2context(config), $1.txt); + $0 = new_pos(var, $1); + $0->var = v; + if (v) { + v->where_decl = $0; + v->where_set = $0; + v->val = val_init($<3); + } else { + v = var_ref(config2context(config), $1.txt); + $0->var = v; + type_err(config2context(config), "error: variable '%v' redeclared", + $0, Tnone, 0, Tnone); + type_err(config2context(config), "info: this is where '%v' was first declared", + v->where_decl, Tnone, 0, Tnone); + } + } }$ + | IDENTIFIER :: Type = ${ { + struct variable *v = var_decl(config2context(config), $1.txt); + $0 = new_pos(var, $1); + $0->var = v; + if (v) { + v->where_decl = $0; + v->where_set = $0; + v->val = val_init($<3); + v->constant = 1; + } else { + v = var_ref(config2context(config), $1.txt); + $0->var = v; + type_err(config2context(config), "error: variable '%v' redeclared", + $0, Tnone, 0, Tnone); + type_err(config2context(config), "info: this is where '%v' was first declared", + v->where_decl, Tnone, 0, Tnone); } } }$ @@ -1569,7 +1667,7 @@ link to find the primary instance. /* This might be a label - allocate a var just in case */ v = var_decl(config2context(config), $1.txt); if (v) { - v->val = val_init(&Tlabel); + v->val = val_init(Tlabel); v->val.label = &v->val; v->where_set = $0; } @@ -1577,6 +1675,17 @@ link to find the primary instance. $0->var = v; } }$ + $*type + Type -> IDENTIFIER ${ + $0 = find_type(config2context(config), $1.txt); + if (!$0) { + tok_err(config2context(config), + "error: undefined type", &$1); + + $0 = Tnone; + } + }$ + ###### print exec cases case Xvar: { @@ -1608,9 +1717,9 @@ link to find the primary instance. struct var *var = cast(var, prog); struct variable *v = var->var; if (!v) { - type_err(c, "%d:BUG: no variable!!", prog, &Tnone, 0, &Tnone); + type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone); *ok = 0; - return &Tnone; + return Tnone; } if (v->merged) v = v->merged; @@ -1626,7 +1735,7 @@ link to find the primary instance. type_err(c, "error: expected %1%r but variable '%v' is %2", prog, type, rules, v->val.type); type_err(c, "info: this is where '%v' was set to %1", v->where_set, - v->val.type, rules, &Tnone); + v->val.type, rules, Tnone); *ok = 0; } if (!type) @@ -1669,7 +1778,7 @@ and `BFact`s. Or, Not, -####### Grammar +###### Grammar $*exec Expression -> Expression or BTerm ${ { @@ -1719,14 +1828,14 @@ and `BFact`s. case Or: case Not: /* both must be Tbool, result is Tbool */ - propagate_types(b->left, c, ok, &Tbool, 0); - propagate_types(b->right, c, ok, &Tbool, 0); - if (type && type != &Tbool) { + propagate_types(b->left, c, ok, Tbool, 0); + propagate_types(b->right, c, ok, Tbool, 0); + if (type && type != Tbool) { type_err(c, "error: %1 operation found where %2 expected", prog, - &Tbool, 0, type); + Tbool, 0, type); *ok = 0; } - return &Tbool; + return Tbool; ###### interp binode cases case And: @@ -1831,12 +1940,12 @@ expression operator. if (t) t = propagate_types(b->left, c, ok, t, 0); } - if (!vtype_compat(type, &Tbool, 0)) { + if (!vtype_compat(type, Tbool, 0)) { type_err(c, "error: Comparison returns %1 but %2 expected", prog, - &Tbool, rules, type); + Tbool, rules, type); *ok = 0; } - return &Tbool; + return Tbool; ###### interp binode cases case Less: @@ -1850,7 +1959,7 @@ expression operator. left = interp_exec(b->left); right = interp_exec(b->right); cmp = value_cmp(left, right); - rv.type = &Tbool; + rv.type = Tbool; switch (b->op) { case Less: rv.bool = cmp < 0; break; case LessEq: rv.bool = cmp <= 0; break; @@ -1972,25 +2081,25 @@ precedence is handled better I might be able to discard this. case Negate: /* as propagate_types ignores a NULL, * unary ops fit here too */ - propagate_types(b->left, c, ok, &Tnum, 0); - propagate_types(b->right, c, ok, &Tnum, 0); - if (!vtype_compat(type, &Tnum, 0)) { + propagate_types(b->left, c, ok, Tnum, 0); + propagate_types(b->right, c, ok, Tnum, 0); + if (!vtype_compat(type, Tnum, 0)) { type_err(c, "error: Arithmetic returns %1 but %2 expected", prog, - &Tnum, rules, type); + Tnum, rules, type); *ok = 0; } - return &Tnum; + return Tnum; case Concat: /* both must be Tstr, result is Tstr */ - propagate_types(b->left, c, ok, &Tstr, 0); - propagate_types(b->right, c, ok, &Tstr, 0); - if (!vtype_compat(type, &Tstr, 0)) { + propagate_types(b->left, c, ok, Tstr, 0); + propagate_types(b->right, c, ok, Tstr, 0); + if (!vtype_compat(type, Tstr, 0)) { type_err(c, "error: Concat returns %1 but %2 expected", prog, - &Tstr, rules, type); + Tstr, rules, type); *ok = 0; } - return &Tstr; + return Tstr; case Bracket: return propagate_types(b->right, c, ok, type, 0); @@ -2031,7 +2140,7 @@ precedence is handled better I might be able to discard this. case Concat: left = interp_exec(b->left); right = interp_exec(b->right); - rv.type = &Tstr; + rv.type = Tstr; rv.str = text_join(left.str, right.str); break; @@ -2188,9 +2297,9 @@ list. 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 = NULL; - if (t && t != &Tnone && t != &Tbool) { + if (t && t != Tnone && t != Tbool) { if (!type) type = t; else if (t != type) { @@ -2205,7 +2314,7 @@ list. ###### interp binode cases case Block: - while (rv.type == &Tnone && + while (rv.type == Tnone && b) { if (b->left) rv = interp_exec(b->left); @@ -2301,7 +2410,7 @@ same solution. sep = ' '; } else if (sep) eol = 0; - left.type = &Tnone; + left.type = Tnone; if (eol) printf("\n"); break; @@ -2352,15 +2461,27 @@ it is declared, and error will be raised as the name is created as break; case Declare: + { + struct variable *v = cast(var, b->left)->var; do_indent(indent, ""); print_exec(b->left, indent, 0); - if (cast(var, b->left)->var->constant) - printf(" ::= "); - else - printf(" := "); + if (cast(var, b->left)->var->constant) { + if (v->where_decl == v->where_set) + printf("::%.*s = ", v->val.type->name.len, + v->val.type->name.txt); + else + printf(" ::= "); + } else { + if (v->where_decl == v->where_set) + printf(":%.*s = ", v->val.type->name.len, + v->val.type->name.txt); + else + printf(" := "); + } print_exec(b->right, indent, 0); if (indent >= 0) printf("\n"); + } break; ###### propagate binode cases @@ -2373,13 +2494,13 @@ it is declared, and error will be raised as the name is created as if (propagate_types(b->right, c, ok, t, 0) != t) if (b->left->type == Xvar) type_err(c, "info: variable '%v' was set as %1 here.", - cast(var, b->left)->var->where_set, t, rules, &Tnone); + cast(var, b->left)->var->where_set, t, rules, Tnone); } else { t = propagate_types(b->right, c, ok, NULL, Rnolabel); if (t) propagate_types(b->left, c, ok, t, 0); } - return &Tnone; + return Tnone; break; @@ -2818,19 +2939,19 @@ defined. struct cond_statement *cs = cast(cond_statement, prog); struct casepart *cp; - t = propagate_types(cs->forpart, c, ok, &Tnone, 0); - if (!vtype_compat(&Tnone, t, 0)) + t = propagate_types(cs->forpart, c, ok, Tnone, 0); + if (!vtype_compat(Tnone, t, 0)) *ok = 0; - t = propagate_types(cs->dopart, c, ok, &Tnone, 0); - if (!vtype_compat(&Tnone, t, 0)) + t = propagate_types(cs->dopart, c, ok, Tnone, 0); + if (!vtype_compat(Tnone, t, 0)) *ok = 0; if (cs->dopart) { - t = propagate_types(cs->thenpart, c, ok, &Tnone, 0); - if (!vtype_compat(&Tnone, t, 0)) + t = propagate_types(cs->thenpart, c, ok, Tnone, 0); + if (!vtype_compat(Tnone, t, 0)) *ok = 0; } if (cs->casepart == NULL) - propagate_types(cs->condpart, c, ok, &Tbool, 0); + propagate_types(cs->condpart, c, ok, Tbool, 0); else { /* Condpart must match case values, with bool permitted */ t = NULL; @@ -2879,9 +3000,9 @@ defined. if (c->condpart) cnd = interp_exec(c->condpart); else - cnd.type = &Tnone; - if (!(cnd.type == &Tnone || - (cnd.type == &Tbool && cnd.bool != 0))) + cnd.type = Tnone; + if (!(cnd.type == Tnone || + (cnd.type == Tbool && cnd.bool != 0))) break; // cnd is Tnone or Tbool, doesn't need to be freed if (c->dopart) @@ -2889,7 +3010,7 @@ defined. if (c->thenpart) { v = interp_exec(c->thenpart); - if (v.type != &Tnone || !c->dopart) + if (v.type != Tnone || !c->dopart) return v; free_value(v); } @@ -2907,7 +3028,7 @@ defined. free_value(cnd); if (c->elsepart) return interp_exec(c->elsepart); - v.type = &Tnone; + v.type = Tnone; return v; } @@ -2930,7 +3051,7 @@ analysis is a bit more interesting at this level. ###### Parser: grammar $*binode - Program -> program OpenScope Varlist Block OptNL ${ + Program -> InitProgram OpenScope Varlist Block OptNL ${ $0 = new(binode); $0->op = Program; $0->left = reorder_bilist($<3); @@ -2943,6 +3064,10 @@ analysis is a bit more interesting at this level. "error: unhandled parse error.", &$1); }$ + InitProgram -> program ${ + context_init(config2context(config)); + }$ + Varlist -> Varlist ArgDecl ${ $0 = new(binode); $0->op = Program; @@ -2990,7 +3115,7 @@ analysis is a bit more interesting at this level. return 0; do { ok = 1; - propagate_types(b->right, c, &ok, &Tnone, 0); + propagate_types(b->right, c, &ok, Tnone, 0); } while (ok == 2); if (!ok) return 0; @@ -2999,19 +3124,19 @@ analysis is a bit more interesting at this level. struct var *v = cast(var, b->left); if (!v->var->val.type) { v->var->where_set = b; - v->var->val = val_init(&Tstr); + v->var->val = val_init(Tstr); } } b = cast(binode, prog); do { ok = 1; - propagate_types(b->right, c, &ok, &Tnone, 0); + propagate_types(b->right, c, &ok, Tnone, 0); } while (ok == 2); if (!ok) return 0; /* Make sure everything is still consistent */ - propagate_types(b->right, c, &ok, &Tnone, 0); + propagate_types(b->right, c, &ok, Tnone, 0); return !!ok; } @@ -3075,13 +3200,14 @@ Fibonacci, and performs a binary search for a number. * merge happens, so types can be different */ if A * 2 > B: - double := "yes" + double:string = "yes" print A, "is more than twice", B, "?", double else: double := A*2 print "double", A, "is only", double - a := A; b := B + a := A; + b:number = B if a > 0 and b > 0: while a != b: if a < b: