static void expr_eval(struct num_exp_ctx *ctx, unsigned int *vp, unsigned int pri) { unsigned int left, right, new_pri; char *tok, op; expr_term(ctx, &left); while (1) { tok = __expr_get(ctx, 1); if (tok == NULL) break; op = tok[0]; new_pri = 0; switch (op) { case '*': case '/': new_pri++; case '+': case '-': new_pri++; case '&': new_pri++; case '|': new_pri++; case ')': break; default: expr_fail(ctx); } if (new_pri < pri) { expr_unget(ctx, tok); break; } pri = new_pri; expr_eval(ctx, &right, new_pri + 1); switch (op) { case '*': left *= right; break; case '/': left /= right; break; case '+': left += right; break; case '-': left -= right; break; case '&': left &= right; break; case '|': left |= right; break; default: expr_fail(ctx); } } *vp = left; }
/* * Flattening transformation. */ static expr_t flatten(expr_t e, bool toplevel, context_t cxt) { if (expr_gettype(e) != EXPRTYPE_OP) return e; exprop_t op = expr_op(e); switch (op) { case EXPROP_EQ: { expr_t x, y; if (expr_view_x_eq_func(e, &x, &y)) { y = flatten(y, false, cxt); return flatten_eq_to_builtin(exprop_atom_make(ATOM_INT_EQ), x, y, toplevel, cxt); } // Fall-through: } case EXPROP_NEQ: case EXPROP_LT: case EXPROP_LEQ: case EXPROP_GT: case EXPROP_GEQ: { // Check if already a flattened comparison: exprop_t binop, cmp; expr_t x, y, z; if (expr_view_x_cmp_y(e, &x, &cmp, &y)) return flatten_x_cmp_y_to_builtin(x, cmp, y, cxt); if (expr_view_x_cmp_y_op_z(e, &x, &cmp, &y, &binop, &z)) { y = expr_make(binop, y, z); return flatten_eq_to_builtin(exprop_atom_make(ATOM_INT_EQ), x, y, toplevel, cxt); } e = expr_arg(e, 1); if (!expr_view_plus_sign_partition(e, &x, &y)) panic("failed to partition (+) expression"); if (op == EXPROP_EQ) { expr_t x0, x1; if (expr_view_plus_first_partition(x, &x0, &x1)) { x1 = flatten_to_primitive(x1, cxt); y = flatten_to_var(y, cxt); e = expr_make(EXPROP_ADD, x0, x1); return flatten_eq_to_builtin(exprop_atom_make(ATOM_INT_EQ), y, e, toplevel, cxt); } expr_t y0, y1; if (expr_view_plus_first_partition(y, &y0, &y1)) { y1 = flatten_to_primitive(y1, cxt); x = flatten_to_var(x, cxt); e = expr_make(EXPROP_ADD, y0, y1); return flatten_eq_to_builtin(exprop_atom_make(ATOM_INT_EQ), x, e, toplevel, cxt); } x = flatten_to_primitive(x, cxt); y = flatten_to_primitive(y, cxt); return flatten_eq_to_builtin(exprop_atom_make(ATOM_INT_EQ), x, y, toplevel, cxt); } else { x = flatten_to_primitive(x, cxt); y = flatten_to_primitive(y, cxt); if (expr_gettype(y) == EXPRTYPE_NUM) { num_t c = expr_getnum(y); y = expr_num(c - 1); e = flatten_x_cmp_y_to_builtin(x, cmp, y, cxt); return expr_not(e); } else return flatten_x_cmp_y_to_builtin(y, cmp, x, cxt); } } case EXPROP_NOT: { expr_t arg = expr_arg(e, 0); arg = flatten(arg, toplevel, cxt); return expr_not(arg); } case EXPROP_IFF: { expr_t arg0 = flatten(expr_arg(e, 0), false, cxt); expr_t arg1 = flatten(expr_arg(e, 1), false, cxt); return expr_iff(arg0, arg1); } case EXPROP_AND: { expr_t and = expr_bool(true), k, v; for (expritr_t i = expritr(e); expr_getpair(i, &k, &v); expr_next(i)) { k = flatten(k, toplevel, cxt); if (v == expr_bool(true)) k = expr_not(k); and = expr_and(k, and); } return and; } case EXPROP_OR: { expr_t or = expr_bool(false), k, v; for (expritr_t i = expritr(e); expr_getpair(i, &k, &v); expr_next(i)) { k = flatten(k, false, cxt); if (v == expr_bool(true)) k = expr_not(k); or = expr_or(k, or); } return or; } case EXPROP_ADD: case EXPROP_MUL: { size_t a = expr_arity(e); expr_t args[a]; expr_args(e, args); for (size_t i = 0; i < a; i++) args[i] = flatten_to_primitive(args[i], cxt); e = expr(op, args); return e; } case EXPROP_POW: { expr_t arg1 = expr_arg(e, 1); if (expr_gettype(arg1) != EXPRTYPE_NUM) { flatten_bad_pow: error("(%s: %zu) failed to flatten expression `!y%s!d'; " "exponent must be a positive constant, found `!y%s!d'", cxt->file, cxt->line, show(expr_term(e)), show(expr_term(arg1))); cxt->error = true; return e; } num_t c = expr_getnum(arg1); if (c <= 1) goto flatten_bad_pow; expr_t arg0 = flatten_to_primitive(expr_arg(e, 0), cxt); expr_t e = expr_pow(arg0, arg1); return e; } default: { atom_t atom = expr_sym(e); if (atom == ATOM_NIL_EQ || atom == ATOM_STR_EQ || atom == ATOM_ATOM_EQ || is_eq(atom)) { expr_t x = expr_arg(e, 0), y = expr_arg(e, 1); x = flatten(x, false, cxt); y = flatten(y, false, cxt); return flatten_eq_to_builtin(expr_op(e), x, y, toplevel, cxt); } size_t a = expr_arity(e); expr_t args[a]; expr_args(e, args); typesig_t sig = typeinst_get_decl((atom_t)op); for (size_t i = 0; i < a; i++) { typeinst_t t = typeinst_decl_arg(sig, i); if (t != typeinst_make_ground(t)) args[i] = flatten_to_var(args[i], cxt); else { // Note: type check does not check instances; we check // here. if (type(args[i]) == VAR || type(args[i]) == FUNC) { error("(%s: %zu) failed to flatten expression " "`!y%s!d'; cannot flatten %s argument " "`!y%s!d' to a ground term", cxt->file, cxt->line, show(expr_term(e)), (type(args[i]) == VAR? "variable": "function call"), show(expr_term(args[i]))); cxt->error = true; } } } e = expr(op, args); return e; } } }