X-Git-Url: https://ocean-lang.org/code/?a=blobdiff_plain;f=csrc%2Foceani.mdc;h=2398fb3bb87b4f8c04c1ddb49f67baa64ce64071;hb=9c3c0e628883128ce89c34c177c1b5bb534ea69d;hp=9720d222fac37ffced3f0494c446e3af2203eb19;hpb=e4967da39aff091cd9dfa187cf77c84ba8643293;p=ocean diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index 9720d22..2398fb3 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -367,6 +367,7 @@ context so indicate that parsing failed. static void type_err(struct parse_context *c, char *fmt, struct exec *loc, struct type *t1, int rules, struct type *t2); + static void tok_err(struct parse_context *c, char *fmt, struct token *t); ###### core functions @@ -477,12 +478,12 @@ from the `exec_types` enum. if (loc->type == Xbinode) return __fput_loc(cast(binode,loc)->left, f) || __fput_loc(cast(binode,loc)->right, f); // NOTEST - return 0; + return 0; // NOTEST } 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 defined, @@ -588,13 +589,15 @@ which can be `NULL` signifying "unknown". A `prop_err` flag set is passed by reference. It has `Efail` set when an error is found, and `Eretry` when the type for some element is set via propagation. If any expression cannot be evaluated immediately, `Enoconst` is set. +If the expression can be copied, `Emaycopy` is set. If it remains unchanged at `0`, then no more propagation is needed. ###### ast enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1, Rnoconstant = 1<<2}; - enum prop_err {Efail = 1<<0, Eretry = 1<<1, Enoconst = 1<<2}; + enum prop_err {Efail = 1<<0, Eretry = 1<<1, Enoconst = 1<<2, + Emaycopy = 1<<3}; ###### format cases case 'r': @@ -789,10 +792,11 @@ which might be reported in error messages. struct type { struct text name; struct type *next; + struct token first_use; int size, align; int anon; void (*init)(struct type *type, struct value *val); - void (*prepare_type)(struct parse_context *c, struct type *type, int parse_time); + int (*prepare_type)(struct parse_context *c, struct type *type, int parse_time); void (*print)(struct type *type, struct value *val, FILE *f); void (*print_type)(struct type *type, FILE *f); int (*cmp_order)(struct type *t1, struct type *t2, @@ -800,6 +804,7 @@ which might be reported in error messages. int (*cmp_eq)(struct type *t1, struct type *t2, struct value *v1, struct value *v2); void (*dup)(struct type *type, struct value *vold, struct value *vnew); + int (*test)(struct type *type, struct value *val); void (*free)(struct type *type, struct value *val); void (*free_type)(struct type *t); long long (*to_int)(struct value *v); @@ -836,7 +841,10 @@ which might be reported in error messages. struct type *n; n = calloc(1, sizeof(*n)); - *n = *proto; + if (proto) + *n = *proto; + else + n->size = -1; n->name = s; n->anon = anon; n->next = c->typelist; @@ -859,7 +867,7 @@ which might be reported in error messages. va_start(ap, name); vasprintf(&t.txt, name, ap); va_end(ap); - t.len = strlen(name); + t.len = strlen(t.txt); return _add_type(c, t, proto, 1); } @@ -886,8 +894,10 @@ which might be reported in error messages. fprintf(f, "%.*s", type->name.len, type->name.txt); else if (type->print_type) type->print_type(type, f); + else if (type->name.len && type->anon) + fprintf(f, "\"%.*s\"", type->name.len, type->name.txt); else - fputs("*invalid*type*", f); + fputs("*invalid*type*", f); // NOTEST } static void val_init(struct type *type, struct value *val) @@ -924,10 +934,35 @@ which might be reported in error messages. static void prepare_types(struct parse_context *c) { struct type *t; + int retry = 1; + enum { none, some, cannot } progress = none; - for (t = c->typelist; t; t = t->next) - if (t->prepare_type) - t->prepare_type(c, t, 1); + while (retry) { + retry = 0; + + for (t = c->typelist; t; t = t->next) { + if (t->size < 0) + tok_err(c, "error: type used but not declared", + &t->first_use); + if (t->size == 0 && t->prepare_type) { + if (t->prepare_type(c, t, 1)) + progress = some; + else if (progress == cannot) + tok_err(c, "error: type has recursive definition", + &t->first_use); + else + retry = 1; + } + } + switch (progress) { + case cannot: + retry = 0; break; + case none: + progress = cannot; break; + case some: + progress = none; break; + } + } } ###### forward decls @@ -966,12 +1001,10 @@ symbol for those. $*type Type -> IDENTIFIER ${ - $0 = find_type(c, $1.txt); + $0 = find_type(c, $ID.txt); if (!$0) { - tok_err(c, - "error: undefined type", &$1); - - $0 = Tnone; + $0 = add_type(c, $ID.txt, NULL); + $0->first_use = $ID; } }$ ## type grammar @@ -1138,6 +1171,11 @@ A separate function encoding these cases will simplify some code later. static void _free_value(struct type *type, struct value *v); + static int bool_test(struct type *type, struct value *v) + { + return v->bool; + } + static struct type base_prototype = { .init = _val_init, .print = _print_value, @@ -1168,6 +1206,7 @@ A separate function encoding these cases will simplify some code later. ###### context initialization Tbool = add_base_type(&context, "Boolean", Vbool, sizeof(char)); + Tbool->test = bool_test; Tstr = add_base_type(&context, "string", Vstr, sizeof(struct text)); Tnum = add_base_type(&context, "number", Vnum, sizeof(mpq_t)); Tnone = add_base_type(&context, "none", Vnone, 0); @@ -2112,7 +2151,7 @@ correctly. } else fputs("???", stderr); // NOTEST } else - fputs("NOTVAR", stderr); + fputs("NOTVAR", stderr); // NOTEST break; ###### propagate exec cases @@ -2251,33 +2290,37 @@ with a const size by whether they are prepared at parse time or not. ###### value functions - static void array_prepare_type(struct parse_context *c, struct type *type, + static int array_prepare_type(struct parse_context *c, struct type *type, int parse_time) { struct value *vsize; mpz_t q; if (type->array.static_size) - return; + return 1; // UNTESTED if (type->array.unspec && parse_time) - return; + return 1; // UNTESTED if (parse_time && type->array.vsize && !type->array.vsize->global) - return; + return 1; // UNTESTED if (type->array.vsize) { vsize = var_value(c, type->array.vsize); if (!vsize) - return; // UNTESTED + return 1; // UNTESTED mpz_init(q); mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num)); type->array.size = mpz_get_si(q); mpz_clear(q); } + if (!parse_time) + return 1; + if (type->array.member->size <= 0) + return 0; // UNTESTED - if (parse_time && type->array.member->size) { - type->array.static_size = 1; - type->size = type->array.size * type->array.member->size; - type->align = type->array.member->align; - } + type->array.static_size = 1; + type->size = type->array.size * type->array.member->size; + type->align = type->array.member->align; + + return 1; } static void array_init(struct type *type, struct value *val) @@ -2606,21 +2649,24 @@ function will be needed. free_fieldlist(t->structure.field_list); } - static void structure_prepare_type(struct parse_context *c, - struct type *t, int parse_time) + static int structure_prepare_type(struct parse_context *c, + struct type *t, int parse_time) { int cnt = 0; struct fieldlist *f; if (!parse_time || t->structure.fields) - return; + return 1; for (f = t->structure.field_list; f; f=f->prev) { enum prop_err perr; cnt += 1; + if (f->f.type->size <= 0) + return 0; if (f->f.type->prepare_type) - f->f.type->prepare_type(c, f->f.type, 1); + f->f.type->prepare_type(c, f->f.type, parse_time); + if (f->init == NULL) continue; do { @@ -2653,6 +2699,7 @@ function will be needed. f = f->prev; } + return 1; } static struct type structure_prototype = { @@ -2755,9 +2802,23 @@ function will be needed. ###### top level grammar DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ { - struct type *t = - add_type(c, $2.txt, &structure_prototype); + struct type *t; + t = find_type(c, $ID.txt); + if (!t) + t = add_type(c, $ID.txt, &structure_prototype); + else if (t->size >= 0) { + tok_err(c, "error: type already declared", &$ID); + tok_err(c, "info: this is location of declartion", &t->first_use); + /* Create a new one - duplicate */ + t = add_type(c, $ID.txt, &structure_prototype); + } else { + struct type tmp = *t; + *t = structure_prototype; + t->name = tmp.name; + t->next = tmp.next; + } t->structure.field_list = $first_use = $ID; } }$ $*fieldlist @@ -3037,6 +3098,7 @@ further detailed when Expression Lists are introduced. e->var = v; if (v) { v->where_decl = e; + v->where_set = e; $0 = v; } else { v = var_ref(c, $1.txt); @@ -3065,7 +3127,7 @@ further detailed when Expression Lists are introduced. | Varlist ; ${ $0 = $<1; }$ Varlist -> Varlist ; ArgDecl ${ - $0 = new(binode); + $0 = new_pos(binode, $2); $0->op = List; $0->left = $right = $ IDENTIFIER : FormalType ${ { - struct variable *v = var_decl(c, $1.txt); - $0 = new(var); + struct variable *v = var_decl(c, $ID.txt); + $0 = new_pos(var, $ID); $0->var = v; + v->where_decl = $0; + v->where_set = $0; v->type = $var->type->check_args(c, perr, v->var->type, args); + if (v->var->type->function.inline_result) + *perr |= Emaycopy; return v->var->type->function.return_type; } @@ -3596,10 +3662,17 @@ expression operator, and the `CMPop` non-terminal will match one of them. ### Expressions: Arithmetic etc. The remaining expressions with the highest precedence are arithmetic, -string concatenation, and string conversion. String concatenation +string concatenation, string conversion, and testing. String concatenation (`++`) has the same precedence as multiplication and division, but lower than the uniary. +Testing comes in two forms. A single question mark (`?`) is a uniary +operator which converts come types into Boolean. The general meaning is +"is this a value value" and there will be more uses as the language +develops. A double questionmark (`??`) is a binary operator (Choose), +with same precedence as multiplication, which returns the LHS if it +tests successfully, else returns the RHS. + String conversion is a temporary feature until I get a better type system. `$` is a prefix operator which expects a string and returns a number. @@ -3615,15 +3688,15 @@ parentheses around an expression converts it into a Term, ###### Binode types Plus, Minus, Times, Divide, Rem, - Concat, - Absolute, Negate, + Concat, Choose, + Absolute, Negate, Test, StringConv, Bracket, ###### declare terminals $LEFT + - Eop - $LEFT * / % ++ Top - $LEFT Uop $ + $LEFT * / % ++ ?? Top + $LEFT Uop $ ? $TERM ( ) ###### expression grammar @@ -3668,11 +3741,13 @@ parentheses around an expression converts it into a Term, Uop -> + ${ $0.op = Absolute; }$ | - ${ $0.op = Negate; }$ | $ ${ $0.op = StringConv; }$ + | ? ${ $0.op = Test; }$ Top -> * ${ $0.op = Times; }$ | / ${ $0.op = Divide; }$ | % ${ $0.op = Rem; }$ | ++ ${ $0.op = Concat; }$ + | ?? ${ $0.op = Choose; }$ ###### print binode cases case Plus: @@ -3681,6 +3756,7 @@ parentheses around an expression converts it into a Term, case Divide: case Concat: case Rem: + case Choose: if (bracket) printf("("); print_exec(b->left, indent, bracket); switch(b->op) { @@ -3690,6 +3766,7 @@ parentheses around an expression converts it into a Term, case Divide: fputs(" / ", stdout); break; case Rem: fputs(" % ", stdout); break; case Concat: fputs(" ++ ", stdout); break; + case Choose: fputs(" ?? ", stdout); break; default: abort(); // NOTEST } // NOTEST print_exec(b->right, indent, bracket); @@ -3698,11 +3775,13 @@ parentheses around an expression converts it into a Term, case Absolute: case Negate: case StringConv: + case Test: if (bracket) printf("("); switch (b->op) { case Absolute: fputs("+", stdout); break; case Negate: fputs("-", stdout); break; case StringConv: fputs("$", stdout); break; + case Test: fputs("?", stdout); break; default: abort(); // NOTEST } // NOTEST print_exec(b->right, indent, bracket); @@ -3750,6 +3829,25 @@ parentheses around an expression converts it into a Term, prog, type, 0, NULL); return Tnum; + case Test: + /* LHS must support ->test, result is Tbool */ + t = propagate_types(b->right, c, perr, NULL, 0); + if (!t || !t->test) + type_err(c, "error: '?' requires a testable value, not %1", + prog, t, 0, NULL); + return Tbool; + + case Choose: + /* LHS and RHS must match and are returned. Must support + * ->test + */ + t = propagate_types(b->left, c, perr, type, rules); + t = propagate_types(b->right, c, perr, t, rules); + if (t && t->test == NULL) + type_err(c, "error: \"??\" requires a testable value, not %1", + prog, t, 0, NULL); + return t; + case Bracket: return propagate_types(b->right, c, perr, type, 0); @@ -3828,6 +3926,20 @@ parentheses around an expression converts it into a Term, printf("Unsupported suffix: %.*s\n", tx.len, tx.txt); // UNTESTED break; + case Test: + right = interp_exec(c, b->right, &rtype); + rvtype = Tbool; + rv.bool = !!rtype->test(rtype, &right); + break; + case Choose: + left = interp_exec(c, b->left, <ype); + if (ltype->test(ltype, &left)) { + rv = left; + rvtype = ltype; + ltype = NULL; + } else + rv = interp_exec(c, b->right, &rvtype); + break; ###### value functions @@ -4067,19 +4179,19 @@ printed. ###### SimpleStatement Grammar | print ExpressionList ${ - $0 = b = new(binode); + $0 = b = new_pos(binode, $1); b->op = Print; b->right = NULL; b->left = reorder_bilist($op = Print; b->right = reorder_bilist($left = NULL; } }$ | print ${ - $0 = b = new(binode); + $0 = b = new_pos(binode, $1); b->op = Print; b->left = NULL; b->right = NULL; @@ -4181,9 +4293,9 @@ it is declared, and error will be raised as the name is created as case Assign: do_indent(indent, ""); - print_exec(b->left, indent, bracket); + print_exec(b->left, -1, bracket); printf(" = "); - print_exec(b->right, indent, bracket); + print_exec(b->right, -1, bracket); if (indent >= 0) printf("\n"); break; @@ -4192,7 +4304,7 @@ it is declared, and error will be raised as the name is created as { struct variable *v = cast(var, b->left)->var; do_indent(indent, ""); - print_exec(b->left, indent, bracket); + print_exec(b->left, -1, bracket); if (cast(var, b->left)->var->constant) { printf("::"); if (v->explicit_type) { @@ -4208,7 +4320,7 @@ it is declared, and error will be raised as the name is created as } if (b->right) { printf("= "); - print_exec(b->right, indent, bracket); + print_exec(b->right, -1, bracket); } if (indent >= 0) printf("\n"); @@ -4240,7 +4352,7 @@ it is declared, and error will be raised as the name is created as propagate_types(b->left, c, perr, t, (b->op == Assign ? Rnoconstant : 0)); } - if (t && t->dup == NULL && t->name.txt[0] != ' ') // HACK + if (t && t->dup == NULL && !(*perr & Emaycopy)) type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL); return Tnone; @@ -5049,7 +5161,7 @@ is a bit more interesting at this level. * is a list for 'struct var' */ struct type *t = add_anon_type(c, &structure_prototype, - " function result"); + "function result"); int cnt = 0; struct binode *b;