view src/ploki/expr.c @ 10131:bbbdb09e6365

<boily> le/rn mate//Mat\xc3\xa9 is a southern hemisphere shamanist beverage that opens your inner self to the Sacred World. Its enlightened users become friendly, wishing \xe2\x80\x9cG\'day, mat\xc3\xa9!\xe2\x80\x9d to one another.
author HackBot
date Sat, 14 Jan 2017 14:02:22 +0000
parents ac0403686959
children
line wrap: on
line source

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