Mercurial > repo
diff src/ploki/expr.c @ 4223:ac0403686959
<oerjan> rm -rf src/ploki; mv ploki src
author | HackBot |
---|---|
date | Fri, 20 Dec 2013 22:18:50 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ploki/expr.c Fri Dec 20 22:18:50 2013 +0000 @@ -0,0 +1,1080 @@ +#include "config.h" +#include "Str.h" +#include "expr.h" +#include "hang.h" +#include "list.h" +#include "main_io.h" +#include "main_label.h" +#include "main_var.h" +#include "mars.h" +#include "match.h" +#include "op.h" +#include "pp.h" +#include "random.h" +#include "re.h" +#include "run.h" +#include "stack.h" +#include "val.h" +#include "venus.h" +#include "xmalloc.h" +#include "zz.h" + +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <setjmp.h> + +#ifdef M_PI + #define MY_PI M_PI +#else + #define MY_PI 3.1415926535897932384626433832795028841971693993751058209749445923 +#endif + +#ifdef M_E + #define MY_E M_E +#else + #define MY_E 2.71828182845904523536028747135266249775724709369995957496696762772 +#endif + +typedef struct val *svalp; +stack_declare(svalp, static) + +static void sv_init(svalp *x) { + *x = v_undef(); +} + +static void sv_end(svalp *x) { + v_delete(*x); +} + +static void sv_copy(svalp *dst, const svalp *src) { + v_set(*dst, *src); +} + +stack_define(svalp, static, sv_init, sv_end, sv_copy) +static stack(svalp) Stack; + +static void con_nop(t_context *c) { + (void)c; +} + +ATTR_NORETURN +static void con_error(const t_context *x, const t_context *y) { + (void)x; + (void)y; + NOTREACHED; +} + +stack_define(t_context, extern, con_nop, con_nop, con_error) +stack(t_context) Context; + +void expr_init(void) { + stack_func(svalp, init)(&Stack); + stack_func(t_context, init)(&Context); +} + +void expr_end(void) { + stack_func(t_context, end)(&Context); + stack_func(svalp, end)(&Stack); +} + +void expr_pp(enum t_binop op, struct val *x, struct val *y) { + switch (op) { + case B_AMPERSAND: pp_and(x, y); return; + case B_ANGLE: pp_lt_n(x, y); return; + case B_BACKSPARK: pp_tobase(x, y); return; + case B_BRACELET: pp_gt(x, y); return; + case B_DOUBLE_OH_SEVEN: pp_mod(x, y); return; + case B_EMBRACE: pp_lt(x, y); return; + case B_FLATWORM: pp_concat(x, y); return; + case B_HALF_MESH: pp_eq_n(x, y); return; + case B_HYBRID: pp_ne(x, y); return; + case B_INTERSECTION: pp_add(x, y); return; + case B_RIGHT_ANGLE: pp_gt_n(x, y); return; + case B_SHARK_FIN: pp_pow(x, y); return; + case B_SLAT: pp_div(x, y); return; + case B_SPARK: pp_frombase(x, y); return; + case B_SPARK_SPOT: pp_ne_n(x, y); return; + case B_SPIKE: pp_or(x, y); return; + case B_SPLAT: pp_mult(x, y); return; + case B_SPOT: pp_read(x, y); return; + case B_SQIGGLE: pp_match(x, y); return; + case B_TAIL: pp_comma(x, y); return; + case B_TWO_SPOT: pp_eq(x, y); return; + case B_U_TURN: pp_shift(x, y); return; + case B_U_TURN_BACK: pp_pop(x, y); return; + case B_WORM: pp_sub(x, y); return; + case B_XMATCH: NOTREACHED; + } +} + +enum t_binop expr_binop(int c) { + switch (c) { + case '!': return B_SPARK_SPOT; + case '%': return B_DOUBLE_OH_SEVEN; + case '&': return B_AMPERSAND; + case '\'': return B_SPARK; + case '*': return B_SPLAT; + case '+': return B_INTERSECTION; + case ',': return B_TAIL; + case '-': return B_WORM; + case '.': return B_SPOT; + case '/': return B_SLAT; + case ':': return B_TWO_SPOT; + case ';': return B_HYBRID; + case '<': return B_ANGLE; + case '=': return B_HALF_MESH; + case '>': return B_RIGHT_ANGLE; + case '[': return B_U_TURN; + case ']': return B_U_TURN_BACK; + case '^': return B_SHARK_FIN; + case '_': return B_FLATWORM; + case '`': return B_BACKSPARK; + case '{': return B_EMBRACE; + case '|': return B_SPIKE; + case '}': return B_BRACELET; + case '~': return B_SQIGGLE; + } + NOTREACHED; +} + +void free_expr(struct expr *x) { + if (!x) { + return; + } + + switch (x->type) { + case literE: + v_end(x->v.val); + xfree(x->v.val); + break; + + case varE: + break; + + case varhashE: + free_expr(x->right); + break; + + case symbolE: + switch (x->op) { + case S_ARGV: + free_expr(x->right); + break; + } + break; + + case unopE: + free_expr(x->right); + if (x->op == F_MATCH) { + re_free(x->left.rx); + } + break; + + case binopE: + free_expr(x->left.expr); + free_expr(x->right); + break; + + case listE: + free_expr(x->right); + free_expr(x->left.expr); + break; + } + xfree(x); +} + +static struct expr *undef(void) { + struct expr *e; + e = xmalloc(1, sizeof *e); + e->type = literE; + e->left.expr = e->right = NULL; + e->v.val = v_undef(); + return e; +} + +static void xv_delete(void *v) { + v_delete(v); +} + +struct expr *get_lval(struct op *op) { + struct expr *x; + int c; + size_t n; + + St_shiftws(&op->txt); + c = ST_FIRSTCHAR(&op->txt); + if (!(c == '$' || isalpha(c))) { + return NULL; + } + for (n = 1; (c = ST_INDEX(&op->txt, n)) == '$' || isalpha(c); ++n) + ; + + x = xmalloc(1, sizeof *x); + x->left.expr = x->right = NULL; + + if (ST_INDEX(&op->txt, n) != '(') { + x->type = varE; + if ((x->v.tent = vr_exists(Var_plain, St_ptr(&op->txt), n)) == VR_NO_COOKIE) { + x->v.tent = vr_register(Var_plain, St_ptr(&op->txt), n, v_undef()); + } + St_del(&op->txt, 0, n); + } else { + x->type = varhashE; + if ((x->v.tent = vr_exists(Var_hash, St_ptr(&op->txt), n)) == VR_NO_COOKIE) { + x->v.tent = vr_register(Var_hash, St_ptr(&op->txt), n, sh_new(xv_delete)); + } + St_del(&op->txt, 0, n + 1); + x->right = get_expr(op); + if (ST_FIRSTCHAR(&op->txt) == ')') { + St_shift(&op->txt); + } + } + return x; +} + +ATTR_CONST +static int xval(int c) { + switch (c) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'A': + case 'a': return 10; + case 'B': + case 'b': return 11; + case 'C': + case 'c': return 12; + case 'D': + case 'd': return 13; + case 'E': + case 'e': return 14; + case 'F': + case 'f': return 15; + } + NOTREACHED; +} + +static struct expr *get_value(struct op *, int); + +static struct expr *get_list(struct op *op, int null) { + struct expr *e; + + e = xmalloc(1, sizeof *e); + e->type = listE; + if ((e->right = get_value(op, 1))) { + e->left.expr = get_list(op, 1); + } else if (null) { + xfree(e); + return NULL; + } else { + e->right = e->left.expr = NULL; + } + return e; +} + +static struct expr *get_value(struct op *op, int null) { + struct expr *e; + int c; + + St_shiftws(&op->txt); + if (!St_len(&op->txt)) { + return null ? NULL : undef(); + } + c = ST_FIRSTCHAR(&op->txt); + + if (c == '$' || isalpha(c)) { + return get_lval(op); + } + + if (isdigit(c) || (c == '.' && isdigit(ST_INDEX(&op->txt, 1)))) { + char *tmp; + + e = undef(); + tmp = St_ptr(&op->txt); + v_set_n(e->v.val, strtod(tmp, &tmp)); + tmp += tmp == St_ptr(&op->txt); + St_del(&op->txt, 0, tmp - St_ptr(&op->txt)); + + return e; + } + + if (OPERATOR_P(c)) { + e = xmalloc(1, sizeof *e); + e->type = binopE; + e->op = expr_binop(c); + St_shift(&op->txt); + e->left.expr = undef(); + e->right = get_value(op, 0); + return e; + } + if (c == '?' && ST_INDEX(&op->txt, 1) == 'o' && ST_INDEX(&op->txt, 2) == '~') { + e = xmalloc(1, sizeof *e); + e->type = binopE; + e->op = B_XMATCH; + St_del(&op->txt, 0, 3); + e->left.expr = undef(); + e->right = get_value(op, 0); + return e; + } + + switch (c) { + case '#': + if (ST_INDEX(&op->txt, 1) != '<') { + goto unrecog; + } + St_del(&op->txt, 0, 2); + e = get_list(op, 0); + St_shiftws(&op->txt); + if (ST_FIRSTCHAR(&op->txt) == '#' && ST_INDEX(&op->txt, 1) == '>') { + St_del(&op->txt, 0, 2); + } + break; + + case '(': + St_shift(&op->txt); + e = get_expr(op); + St_shiftws(&op->txt); + if (ST_FIRSTCHAR(&op->txt) == ')') { + St_shift(&op->txt); + } + break; + + case '"': { + struct val *v; + St_shift(&op->txt); + e = undef(); + v = e->v.val; + V_STR(v); + for (; St_len(&op->txt) && (c = St_shift(&op->txt)) != '"'; ) { + switch (c) { + case '\\': + if (St_len(&op->txt) == 0) { + v_cat_c(v, c); + break; + } + c = St_shift(&op->txt); + if (c >= '0' && c <= '7') { + int oct; + oct = c - '0'; + c = ST_FIRSTCHAR(&op->txt); + if (c >= '0' && c <= '7') { + oct *= 010; + oct += c - '0'; + St_shift(&op->txt); + c = ST_FIRSTCHAR(&op->txt); + if (c >= '0' && c <= '7') { + oct *= 010; + oct += c - '0'; + St_shift(&op->txt); + } + } + v_cat_c(v, oct); + break; + } + switch (c) { + case 'x': + c = ST_FIRSTCHAR(&op->txt); + if (isxdigit(c)) { + int hex; + hex = xval(c); + St_shift(&op->txt); + c = ST_FIRSTCHAR(&op->txt); + if (isxdigit(c)) { + hex *= 0x10; + hex += xval(c); + St_shift(&op->txt); + } + v_cat_c(v, hex); + } else { + v_cat_m(v, "\\x", 2); + } + break; + + case 'a': v_cat_c(v, '\a'); break; + case 'b': v_cat_c(v, '\b'); break; + case 'f': v_cat_c(v, '\f'); break; + case 'n': v_cat_c(v, '\n'); break; + case 'r': v_cat_c(v, '\r'); break; + case 't': v_cat_c(v, '\t'); break; + case 'v': v_cat_c(v, '\v'); break; + + case 'c': + if (St_len(&op->txt)) { + c = St_shift(&op->txt); + v_cat_c(v, (toupper(c) + 64) % 128); + } else { + v_cat_m(v, "\\c", 2); + } + break; + + case 'V': { + struct expr *tmp; + tmp = e; + e = xmalloc(1, sizeof *e); + e->type = binopE; + e->op = B_FLATWORM; + e->left.expr = tmp; + e->right = get_value(op, 0); + tmp = e; + e = xmalloc(1, sizeof *e); + e->type = binopE; + e->op = B_FLATWORM; + e->left.expr = tmp; + e->right = undef(); + v = e->right->v.val; + break; + } + + default: + v_cat_c(v, c); + break; + } + break; + + default: + v_cat_c(v, c); + break; + } + } + } + break; + + case '\\': + St_shift(&op->txt); + e = xmalloc(1, sizeof *e); + e->type = symbolE; + e->right = NULL; + e->left.expr = NULL; + + c = ST_FIRSTCHAR(&op->txt); + if (isdigit(c)) { + char *tmp = St_ptr(&op->txt); + e->left.bonus = strtoul(tmp, &tmp, 10); + St_del(&op->txt, 0, tmp - St_ptr(&op->txt)); + e->op = S_MATCH; + } else switch (c) { + case '!': St_shift(&op->txt); e->op = S_ERR; break; + case '?': St_shift(&op->txt); e->op = S_RAND; break; + case '_': St_shift(&op->txt); e->op = S_RESULT; break; + + case '@': + St_shift(&op->txt); + e->op = S_ARG; + break; + + case 'A': + switch (ST_INDEX(&op->txt, 1)) { + case 'R': + if (ST_INDEX(&op->txt, 2) == 'G') { + St_del(&op->txt, 0, 3); + e->op = S_ARGC; + if (ST_FIRSTCHAR(&op->txt) == ':') { + St_shift(&op->txt); + e->op = S_ARGV; + e->right = get_value(op, 0); + } + break; + } + goto nullsym; + + case 'U': + if (ST_INDEX(&op->txt, 2) == 'S' && + ST_INDEX(&op->txt, 3) == 'G') { + St_del(&op->txt, 0, 4); + e->op = S_STDOUT; + break; + } + goto nullsym; + + default: + goto nullsym; + } + break; + + case 'E': + switch (ST_INDEX(&op->txt, 1)) { + case 'I': + if ( + ST_INDEX(&op->txt, 2) == 'N' && + ST_INDEX(&op->txt, 3) == 'G' + ) { + St_del(&op->txt, 0, 4); + e->op = S_STDIN; + break; + } + if (0) + case 'N': { + if (ST_INDEX(&op->txt, 2) == 'V') { + St_del(&op->txt, 0, 3); + e->type = unopE; + e->op = F_GETENV; + e->right = get_value(op, 0); + break; + } + } + /*DURCHFALL*/ + default: + St_shift(&op->txt); + e->op = S_EULER; + break; + } + break; + + case 'F': + if ( + ST_INDEX(&op->txt, 1) == 'E' && + ST_INDEX(&op->txt, 2) == 'H' && + ST_INDEX(&op->txt, 3) == 'L' + ) { + St_del(&op->txt, 0, 4); + e->op = S_STDERR; + break; + } + goto nullsym; + + case 'L': e->op = F_LOWER; goto simplefunc; + case 'Q': e->op = F_QUOTE; goto simplefunc; + case 'R': e->op = F_RE_ESC; goto simplefunc; + case 'U': e->op = F_UPPER; goto simplefunc; +simplefunc: + St_shift(&op->txt); + e->type = unopE; + e->right = get_value(op, 0); + break; + + case 'P': + if (ST_INDEX(&op->txt, 1) == 'I') { + St_del(&op->txt, 0, 2); + e->op = S_LUDOLF; + break; + } + goto nullsym; + +nullsym: + default: + e->op = S_NUL; + break; + } + break; + + case '@': + #define CASE_DO_STUFF(n, o) \ + if (1) { \ + St_del(&op->txt, 0, (n)); \ + e->op = (o); \ + e->right = get_value(op, 0); \ + break; \ + } else ((void)0) + + #define CASE(i, w, n, o) \ + case i: if (1) { \ + if (St_len(&op->txt) < (n) || St_ncmp_m(&op->txt, w, n)) { \ + goto usrfunc; \ + } \ + CASE_DO_STUFF(n, o); \ + } else ((void)0) + + St_shift(&op->txt); + e = xmalloc(1, sizeof *e); + e->type = unopE; + e->left.expr = NULL; + + switch (ST_FIRSTCHAR(&op->txt)) { + case '+': CASE_DO_STUFF(1, F_MOEND); + case '-': CASE_DO_STUFF(1, F_MOSTART); + case 'A': + switch (ST_INDEX(&op->txt, 1)) { + CASE('B', "ABS", 3, F_ABS); + CASE('C', "ACOS", 4, F_ACOS); + CASE('P', "APERS", 5, F_OPEN); + CASE('S', "ASIN", 4, F_ASIN); + case 'T': + if (St_len(&op->txt) >= 4) { + if (St_ncmp_m(&op->txt, "ATAN2", 5) == 0) { + CASE_DO_STUFF(5, F_ATAN2); + } + if (St_ncmp_m(&op->txt, "ATAN", 4) == 0) { + CASE_DO_STUFF(4, F_ATAN); + } + } + goto usrfunc; + + default: + goto usrfunc; + } + break; + + case 'C': + switch (ST_INDEX(&op->txt, 1)) { + CASE('H', "CHR", 3, F_CHR); + CASE('O', "COS", 3, F_COS); + default: + goto usrfunc; + } + break; + + CASE('D', "DEF-P", 5, F_DEFINED); + + case 'E': + switch (ST_INDEX(&op->txt, 1)) { + CASE('D', "EDD-P", 5, F_EOF); + CASE('N', "ENV", 3, F_GETENV); + CASE('R', "ERR-P", 5, F_ERROR); + CASE('V', "EVAL", 4, F_CATCH); + default: goto usrfunc; + } + break; + + CASE('G', "GET", 3, F_GETC); + + case 'I': + switch (ST_INDEX(&op->txt, 1)) { + CASE('N', "INT", 3, F_INT); + CASE('O', "IO-P", 4, F_IO); + default: goto usrfunc; + } + break; + + case 'L': + switch (ST_INDEX(&op->txt, 1)) { + CASE('A', "LAPERS", 6, F_OPENR); + case 'E': + switch (ST_INDEX(&op->txt, 2)) { + CASE('G', "LEGS", 4, F_GETS); + CASE('N', "LENGTH", 6, F_LENGTH); + default: goto usrfunc; + } + break; + CASE('G', "LG", 2, F_LOG10); + CASE('N', "LN", 2, F_LOG); + default: goto usrfunc; + } + break; + + case 'N': + switch (ST_INDEX(&op->txt, 1)) { + CASE('E', "NEG", 3, F_NEG); + CASE('O', "NOT", 3, F_NOT); + CASE('U', "NUM", 3, F_NUM); + default: goto usrfunc; + } + break; + + case 'O': + switch (ST_INDEX(&op->txt, 1)) { + CASE('R', "ORD", 3, F_ORD); + CASE('M', "OMFG", 4, F_FREEZE); + default: goto usrfunc; + } + break; + + case 'R': + if (ST_INDEX(&op->txt, 1) != 'E') { + goto usrfunc; + } + switch (ST_INDEX(&op->txt, 2)) { + CASE('M', "REMOVE", 6, F_REMOVE); + CASE('N', "RENAEM", 6, F_RENAME); + CASE('V', "REVERSE", 7, F_REVERSE); + default: goto usrfunc; + } + break; + + case 'S': + switch (ST_INDEX(&op->txt, 1)) { + case 'A': + switch (ST_INDEX(&op->txt, 2)) { + CASE('G', "SAG", 3, F_TELL); + CASE('P', "SAPERS", 6, F_OPENW); + default: goto usrfunc; + } + break; + CASE('I', "SIN", 3, F_SIN); + CASE('Q', "SQRT", 4, F_SQRT); + CASE('T', "STR", 3, F_STR); + CASE('U', "SUCH", 4, F_SEEK); + default: goto usrfunc; + } + break; + + case 'T': + switch (ST_INDEX(&op->txt, 1)) { + CASE('A', "TAN", 3, F_TAN); + CASE('Y', "TYPE OF", 7, F_TYPEOF); + default: goto usrfunc; + } + break; + +usrfunc: + default: + if ((e->left.op = ma_find(&Mars, &op->txt))) { + e->op = F_CALL; + } else { + e->op = F_NUL; + e->left.bonus = op->line; + } + e->right = get_value(op, 0); + break; + } + #undef CASE_DO_STUFF + #undef CASE + break; + +unrecog: + default: + if (null) { + e = NULL; + } else { + e = undef(); + } + break; + } + + return e; +} + +struct expr *get_iobj(struct op *op) { + return get_value(op, 0); +} + +struct expr *get_expr(struct op *op) { + struct expr *a, *b; + + a = get_value(op, 0); + + for (St_shiftws(&op->txt); St_len(&op->txt); St_shiftws(&op->txt)) { + struct expr *tmp; + enum t_binop k; + + if (OPERATOR_P(ST_FIRSTCHAR(&op->txt))) { + k = expr_binop(St_shift(&op->txt)); + b = get_value(op, 0); + } else if (St_ncmp_m(&op->txt, "?o~", 3) == 0) { + k = B_XMATCH; + St_del(&op->txt, 0, 3); + b = get_value(op, 0); + } else { + if (!(b = get_value(op, 1))) { + break; + } + k = B_SPOT; + } + + tmp = a; + a = xmalloc(1, sizeof *a); + a->type = binopE; + a->op = k; + a->left.expr = tmp; + a->right = b; + } + + return a; +} + +#define TOS (*stack_func(svalp, at)(&Stack, 0)) + +void eval_push(struct expr *ex) { + const struct expr *const e = ex; + + switch (e->type) { + case literE: + case varE: + stack_func(svalp, push)(&Stack, &e->v.val); + return; + + case varhashE: { + struct val *tmp, *tos; + const char *ptr; + size_t len; + + eval_push(e->right); + tos = TOS; + ptr = v_sptr(tos, &len); + if ((tmp = sh_get(e->v.hash, ptr, len))) { + v_set(tos, tmp); + } else { + v_set_undef(tos); + } + return; + } + + case symbolE: + switch (e->op) { + case S_NUL: + stack_func(svalp, pushnull)(&Stack); + break; + + case S_ARG: { + struct val *const tmp = &Interp.arg; + stack_func(svalp, push)(&Stack, &tmp); + break; + } + + case S_ARGC: + stack_func(svalp, pushnull)(&Stack); + v_set_n(TOS, Interp.a.argc); + break; + + case S_ARGV: { + struct val *tos; + size_t n; + + eval_push(e->right); + tos = TOS; + V_NUM(tos); + n = RINT(tos->num); + + if (n < Interp.a.argc) { + v_set(tos, &Interp.a.argv[n]); + } else { + v_set_undef(tos); + } + break; + } + + case S_ERR: { + struct val *tos; + const char *const tmp = strerror(errno); + stack_func(svalp, pushnull)(&Stack); + tos = TOS; + v_set_m(tos, tmp, strlen(tmp)); + tos->num = errno; + tos->type |= V_NUM_K; + break; + } + + case S_EULER: + stack_func(svalp, pushnull)(&Stack); + v_set_n(TOS, MY_E); + break; + + case S_LUDOLF: + stack_func(svalp, pushnull)(&Stack); + v_set_n(TOS, MY_PI); + break; + + case S_MATCH: + stack_func(svalp, pushnull)(&Stack); + if (e->left.bonus < Interp.match.length) { + v_set(TOS, &Interp.match.matches[e->left.bonus]); + } + break; + + case S_RAND: + stack_func(svalp, pushnull)(&Stack); + v_set_n(TOS, randval()); + break; + + case S_RESULT: { + struct val *const tmp = &Interp.result; + stack_func(svalp, push)(&Stack, &tmp); + break; + } + + case S_STDIN: + stack_func(svalp, pushnull)(&Stack); + v_set_io(TOS, In); + break; + + case S_STDOUT: + stack_func(svalp, pushnull)(&Stack); + v_set_io(TOS, Out); + break; + + case S_STDERR: + stack_func(svalp, pushnull)(&Stack); + v_set_io(TOS, Err); + break; + } + return; + + case unopE: + if (e->op == F_FREEZE) { + struct sub *const tmp = sub_new(e->right); + stack_func(svalp, pushnull)(&Stack); + v_set_sub(TOS, tmp); + sub_decr(tmp); + return; + } + + if (e->op == F_CATCH) { + const size_t stackmark = stack_func(svalp, size)(&Stack); + t_context *tos; + + stack_func(t_context, pushnull)(&Context); + tos = stack_func(t_context, at)(&Context, 0); + if (!setjmp(tos->buf)) { + tos->depth = stack_func(save_pair, size)(&Saved); + eval_push(e->right); + assert(stack_func(svalp, size)(&Stack) - stackmark == 1u); + stack_func(t_context, clear)(&Context, 1); + } else { + tos = stack_func(t_context, at)(&Context, 0); + depth_restore(tos->depth); + stack_func(t_context, clear)(&Context, 1); + assert(stackmark <= stack_func(svalp, size)(&Stack)); + stack_func(svalp, clear)(&Stack, stack_func(svalp, size)(&Stack) - stackmark); + stack_func(svalp, pushnull)(&Stack); + } + return; + } + + eval_push(e->right); + switch (e->op) { + case F_NUL: { + struct op *op; + struct val *const tos = TOS; + + TOLABEL(tos); + if ((op = ve_findnext(&Venus, ko_str(tos->ko), e->left.bonus))) { + struct val *tmp = v_undef(); + v_set(tmp, tos); + tmp = execute(op, tmp); + v_set(TOS, tmp); + v_delete(tmp); + } else { + hang(); + } + break; + } + + case F_CALL: { + struct val *tmp = v_undef(); + v_set(tmp, TOS); + tmp = execute(e->left.op, tmp); + v_set(TOS, tmp); + v_delete(tmp); + break; + } + + case F_MATCH: + do_match(TOS, e->left.rx); + break; + + case F_ABS: pp_abs(TOS); break; + case F_ACOS: pp_acos(TOS); break; + case F_ASIN: pp_asin(TOS); break; + case F_ATAN: pp_atan(TOS); break; + case F_ATAN2: pp_atan2(TOS); break; + case F_CHR: pp_chr(TOS); break; + case F_COS: pp_cos(TOS); break; + case F_DEFINED: pp_defined(TOS); break; + case F_EOF: pp_eof(TOS); break; + case F_ERROR: pp_error(TOS); break; + case F_GETC: pp_getc(TOS); break; + case F_GETENV: pp_getenv(TOS); break; + case F_GETS: pp_gets(TOS); break; + case F_HANG: hang(); break; + case F_INT: pp_int(TOS); break; + case F_IO: pp_io(TOS); break; + case F_LENGTH: pp_length(TOS); break; + case F_LOG: pp_log(TOS); break; + case F_LOG10: pp_log10(TOS); break; + case F_LOWER: pp_lower(TOS); break; + case F_MOEND: pp_moend(TOS); break; + case F_MOSTART: pp_mostart(TOS); break; + case F_NEG: pp_neg(TOS); break; + case F_NOT: pp_not(TOS); break; + case F_NUM: pp_num(TOS); break; + case F_OPEN: pp_open(TOS); break; + case F_OPENR: pp_openr(TOS); break; + case F_OPENW: pp_openw(TOS); break; + case F_ORD: pp_ord(TOS); break; + case F_QUOTE: pp_quote(TOS); break; + case F_RE_ESC: pp_escape(TOS); break; + case F_REMOVE: pp_remove(TOS); break; + case F_RENAME: pp_rename(TOS); break; + case F_REVERSE: pp_reverse(TOS); break; + case F_SEEK: pp_seek(TOS); break; + case F_SIN: pp_sin(TOS); break; + case F_SQRT: pp_sqrt(TOS); break; + case F_STR: pp_str(TOS); break; + case F_TAN: pp_tan(TOS); break; + case F_TELL: pp_tell(TOS); break; + case F_TYPEOF: pp_typeof(TOS); break; + case F_UPPER: pp_upper(TOS); break; + } + return; + + case binopE: + eval_push(e->left.expr); + eval_push(e->right); + + if (e->op != B_XMATCH) { + expr_pp(e->op, *stack_func(svalp, at)(&Stack, 1), TOS); + stack_func(svalp, clear)(&Stack, 1); + } else { + t_regex *rx; + struct val *const tos = TOS; + V_STR(tos); + rx = re_compile(ko_str(tos->ko)); + stack_func(svalp, clear)(&Stack, 1); + free_expr(ex->right); + ex->right = ex->left.expr; + ex->left.rx = rx; + ex->op = F_MATCH; + ex->type = unopE; + + do_match(TOS, rx); + } + return; + + case listE: { + struct val *tos; + + stack_func(svalp, pushnull)(&Stack); + tos = TOS; + tos->magic.list = li_new(); + tos->type = V_LIST_K; + + if (e->right) { + struct val *tmp; + + eval_push(e->right); + tmp = v_undef(); + stack_func(svalp, pop)(&Stack, &tmp); + li_push(tos->magic.list, tmp); + + if (e->left.expr) { + eval_push(e->left.expr); + assert(!!V_LIST_P(TOS)); + li_append((*stack_func(svalp, at)(&Stack, 1))->magic.list, TOS->magic.list); + stack_func(svalp, clear)(&Stack, 1); + } + } + return; + } + } + + NOTREACHED; +} + +void eval_into(struct expr *e, struct val *v) { + DEBUG(const size_t oldsize = stack_func(svalp, size)(&Stack);) + eval_push(e); + assert(stack_func(svalp, size)(&Stack) - oldsize == 1u); + stack_func(svalp, pop)(&Stack, &v); +} + +struct val *eval_pop(void) { + struct val *new = v_undef(); + assert(stack_func(svalp, size)(&Stack) != 0); + stack_func(svalp, pop)(&Stack, &new); + return new; +} + +struct val *eval_expr(struct expr *e) { + eval_push(e); + return eval_pop(); +}