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);
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,
###### 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);
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;
### 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.
###### 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
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:
case Divide:
case Concat:
case Rem:
+ case Choose:
if (bracket) printf("(");
print_exec(b->left, indent, bracket);
switch(b->op) {
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);
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);
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);
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