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();
+}