]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: introduce 'test' and 'choose' operators
[ocean] / csrc / oceani.mdc
index e09fe07a1c22fee614fd2356d9a5bf0e05991553..f2cc254612da9316433537ee9e4dba91f128deb4 100644 (file)
@@ -589,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':
@@ -802,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);
@@ -1166,6 +1169,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,
@@ -1196,6 +1204,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);
@@ -2303,7 +2312,7 @@ with a const size by whether they are prepared at parse time or not.
                if (!parse_time)
                        return 1;
                if (type->array.member->size <= 0)
-                       return 0;
+                       return 0;       // UNTESTED
 
                type->array.static_size = 1;
                type->size = type->array.size * type->array.member->size;
@@ -3209,6 +3218,8 @@ it in the "SimpleStatement Grammar" which will be described later.
                }
                *perr |= Enoconst;
                v->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;
        }
 
@@ -3646,10 +3657,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.
@@ -3665,15 +3683,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
@@ -3718,11 +3736,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:
@@ -3731,6 +3751,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) {
@@ -3740,6 +3761,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);
@@ -3748,11 +3770,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);
@@ -3800,6 +3824,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);
 
@@ -3878,6 +3921,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, &ltype);
+               if (ltype->test(ltype, &left)) {
+                       rv = left;
+                       rvtype = ltype;
+                       ltype = NULL;
+               } else
+                       rv = interp_exec(c, b->right, &rvtype);
+               break;
 
 ###### value functions
 
@@ -4290,7 +4347,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;
 
@@ -5099,7 +5156,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;