view src/ploki/run.c @ 8427:1fc808cd5b1f

<b_jonas> learn can\'t is the most frequent word whose pronunciation varies between /\xc9\x91\xcb\x90/ and /\xc3\xa6/ depending on dialect. The list is: advance after answer ask aunt brass can\'t cast castle chance class command dance demand draft enhance example fast father glass graph grass half last laugh mask master nasty pass past path plant rather sample shan\'t staff task vast
author HackBot
date Thu, 09 Jun 2016 21:28:47 +0000
parents ac0403686959
children
line wrap: on
line source

#include "config.h"
#include "IO.h"
#include "Str.h"
#include "atechit.h"
#include "expr.h"
#include "hang.h"
#include "main.h"
#include "main_io.h"
#include "main_label.h"
#include "main_opt.h"
#include "run.h"
#include "stack.h"
#include "strhash.h"
#include "text.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>

struct Interp Interp;

static void my_x_end(void) {
	v_end(&Interp.arg);
	v_end(&Interp.result);

	while (Interp.a.argc) {
		--Interp.a.argc;
		v_end(&Interp.a.argv[Interp.a.argc]);
	}
	xfree(Interp.a.argv);

	while (Interp.match.length) {
		--Interp.match.length;
		v_end(&Interp.match.matches[Interp.match.length]);
	}
	xfree(Interp.match.matches);

	xfree(Interp.m_start.index);
	xfree(Interp.m_end.index);
}

static void sp_nop(save_pair *sp) {
	(void)sp;
}

static void sp_writeback(save_pair *sp) {
	v_set(sp->target, &sp->content);
	v_end(&sp->content);
}

ATTR_NORETURN
static void sp_error(const save_pair *x, const save_pair *y) {
	(void)x;
	(void)y;
	NOTREACHED;
}

stack_define(save_pair, extern, sp_nop, sp_writeback, sp_error)

stack(save_pair) Saved;

void stack_store(struct val *target, const struct val *value) {
	save_pair *tos;

	stack_func(save_pair, pushnull)(&Saved);
	tos = stack_func(save_pair, at)(&Saved, 0);

	tos->target = target;
	v_iniset(&tos->content, target);
	v_set(target, value);
}

static void stack_store_del(struct val *target, struct val *value) {
	stack_store(target, value);
	v_delete(value);
}

size_t depth_get(void) {
	return stack_func(save_pair, size)(&Saved);
}

void depth_restore(size_t n) {
	assert(depth_get() >= n);
	stack_func(save_pair, clear)(&Saved, depth_get() - n);
}

struct val *execute(const struct op *op, struct val *arg) {
	struct val *v;
	const size_t curdepth = depth_get();

	stack_store_del(&Interp.arg, arg);

	while (op) {
		if (Opt.debug & DBG_OPS) {
			fprintf(io_fp(Err), "op %d\n", op->type);
		}
		switch (op->type) {
			case OP_NOP:
				break;

			case OP_ASSIGN:
				if (!op->arh.expr) {
					eval_into(op->arg, &Interp.result);
				} else {
					switch (op->arh.expr->type) {
						case varE:
							eval_into(op->arg, op->arh.expr->v.val);
							break;

						case varhashE: {
							struct val *tmp;
							eval_push(op->arg);
							eval_push(op->arh.expr->right);
							tmp = eval_pop();
							v = eval_pop();
							V_STR(tmp);
							sh_put(op->arh.expr->v.hash, ko_ptr(tmp->ko), ko_length(tmp->ko), v);
							v_delete(tmp);
							break;
						}

						default:
							NOTREACHED;
							break;
					}
				}
				break;

			case OP_CALL:
				v = eval_expr(op->arg);
				v = execute(op->arh.op, v);
				v_set(&Interp.result, v);
				v_delete(v);
				break;

			case OP_CALL_BACK: {
				struct op *tmp;

				v = eval_expr(op->arh.expr);
				TOLABEL(v);
				if (!(tmp = ve_findprev(&Venus, ko_str(v->ko), op->line))) {
					v_delete(v);
					hang();
				}
				v_delete(v);
				v = eval_expr(op->arg);
				v = execute(tmp, v);
				v_set(&Interp.result, v);
				v_delete(v);
				break;
			}

			case OP_CALL_DYN: {
				struct op *tmp;

				v = eval_expr(op->arg);
				TOLABEL(v);
				if (!(tmp = ve_findnext(&Venus, ko_str(v->ko), op->line))) {
					v_delete(v);
					hang();
				}
				v_set_undef(v);
				v = execute(tmp, v);
				v_set(&Interp.result, v);
				v_delete(v);
				break;
			}

			case OP_CLOSE:
				v = eval_expr(op->arg);
				if (V_EXT_P(v)) {
					v_set_n(&Interp.result, io_close(v->magic.ext));
				} else {
					v_set_n(&Interp.result, -1);
					#ifdef EBADF
						errno = EBADF;
					#endif
				}
				v_delete(v);
				break;

			case OP_EXIT: {
				int tmp;

				v = eval_expr(op->arg);
				V_NUM(v);
				tmp = v->num >= 0.0 ? v->num + .5 : EXIT_FAILURE;
				v_delete(v);
				exit(tmp);
			}

			case OP_FLUSH:
				v = eval_expr(op->arg);
				if (!V_EXT_P(v)) {
					v_delete(v);
					v_set_n(&Interp.result, EOF);
					#ifdef EBADF
						errno = EBADF;
					#endif
					break;
				}
				v_set_n(&Interp.result, io_flush(v->magic.ext));
				v_delete(v);
				break;

			case OP_GOBACK:
				v = eval_expr(op->arg);
				TOLABEL(v);
				if ((op = ve_findprev(&Venus, ko_str(v->ko), op->line))) {
					v_delete(v);
					continue;
				}
				v_delete(v);
				hang();
				break;

			case OP_GOTO:
				v = eval_expr(op->arg);
				TOLABEL(v);
				if ((op = ve_findnext(&Venus, ko_str(v->ko), op->line))) {
					v_delete(v);
					continue;
				}
				v_delete(v);
				hang();
				break;

			case OP_ELSE:
			case OP_HANG:
			case OP_FI:
				hang();
				break;

			case OP_IF:
				v = eval_expr(op->arg);
				if (v_true(v)) {
					v_delete(v);
					op = op->arh.op;
					continue;
				}
				v_delete(v);
				break;

			case OP_MODIFY: {
				struct val *tmp = eval_expr(op->arg);
				expr_pp(op->arh.expr->op, op->arh.expr->v.val, tmp);
				v_delete(tmp);
				break;
			}

			case OP_PRINT: {
				IO *fh;

				eval_push(op->arg);
				if (!op->arh.expr) {
					fh = io_incr(Out);
				} else {
					struct val *const tmp = eval_expr(op->arh.expr);

					if (V_EXT_P(tmp)) {
						fh = io_incr(tmp->magic.ext);
					} else {
						v_delete(tmp);
						v_delete(eval_pop());
						v_set_n(&Interp.result, -1);
						#ifdef EBADF
							errno = EBADF;
						#endif
						break;
					}
					v_delete(tmp);
				}
				v = eval_pop();
				V_STR(v);
				v_set_n(&Interp.result, io_write_m(fh, ko_ptr(v->ko), ko_length(v->ko)) + 1u - 1.);
				io_decr(fh);
				v_delete(v);
				break;
			}

			case OP_PUTC: {
				IO *fh;

				eval_push(op->arg);
				if (!op->arh.expr) {
					fh = io_incr(Out);
				} else {
					struct val *const tmp = eval_expr(op->arh.expr);

					if (V_EXT_P(tmp)) {
						fh = io_incr(tmp->magic.ext);
					} else {
						v_delete(tmp);
						v_delete(eval_pop());
						v_set_n(&Interp.result, -1);
						#ifdef EBADF
							errno = EBADF;
						#endif
						break;
					}
					v_delete(tmp);
				}
				v = eval_pop();
				V_NUM(v);

				v_set_n(&Interp.result, io_putc(fh, RINT(v->num)));
				io_decr(fh);
				v_delete(v);
				break;
			}

			case OP_RESET:
				v = eval_expr(op->arg);
				if (!V_EXT_P(v)) {
					v_delete(v);
					v_set_n(&Interp.result, EOF);
					#ifdef EBADF
						errno = EBADF;
					#endif
					break;
				}
				io_clearerr(v->magic.ext);
				v_delete(v);
				break;

			case OP_RETURN:
				if (op->arg->type == unopE && op->arg->op == F_CALL) {
					eval_into(op->arg->right, &Interp.arg);
					op = op->arg->left.op;
					continue;
				}
				v = eval_expr(op->arg);
				depth_restore(curdepth);
				return v;

			case OP_SET_VAL:
				switch (op->arh.expr->type) {
					case varE:
						v_set(op->arh.expr->v.val, op->arg->v.val);
						break;

					case varhashE: {
						struct val *const tmp = eval_expr(op->arh.expr->right);
						struct val *const val = v_undef();
						V_STR(tmp);
						v_set(val, op->arg->v.val);
						sh_put(op->arh.expr->v.hash, ko_ptr(tmp->ko), ko_length(tmp->ko), val);
						v_delete(tmp);
						break;
					}

					default:
						NOTREACHED;
						break;
				}
				break;

			case OP_SYSTEM:
				v = eval_expr(op->arg);
				V_STR(v);
				v_set_n(&Interp.result, system(ko_szp(v->ko)));
				v_delete(v);
				break;

			case OP_TEMP:
				eval_push(op->arg);
				if (!op->arh.expr) {
					v_delete(eval_pop());
				} else switch (op->arh.expr->type) {
					case varE:
						stack_store_del(op->arh.expr->v.val, eval_pop());
						break;

					case varhashE: {
						struct val *addr;
						struct val *const tmp = eval_expr(op->arh.expr->right);
						v = eval_pop();
						V_STR(tmp);
						if (!(addr = sh_get(op->arh.expr->v.hash, ko_ptr(tmp->ko), ko_length(tmp->ko)))) {
							sh_put(op->arh.expr->v.hash, ko_ptr(tmp->ko), ko_length(tmp->ko), addr = v_undef());
						}
						v_delete(tmp);
						stack_store_del(addr, v);
						break;
					}

					default:
						NOTREACHED;
				}
				break;

			case OP_THROW:
				v = eval_expr(op->arg);
				if (!depth_get()) {
					V_STR(v);
					if (ko_at(v->ko, ko_length(v->ko) - 1u) == '\n') {
						io_write_m(Err, ko_ptr(v->ko), ko_length(v->ko));
					} else {
						fprintf(io_fp(Err), "%s: uncaught exception: ", Prog);
						io_write_m(Err, ko_ptr(v->ko), ko_length(v->ko));
						putc('\n', io_fp(Err));
					}
					v_delete(v);
					exit(EXIT_FAILURE);
				}
				v_set(&Interp.result, v);
				v_delete(v);
				longjmp(stack_func(t_context, at)(&Context, 0)->buf, 1);
				break;
		}
		op = op->next;
	}

	exit(0);
}

static void cleanup(void) {
	stack_func(save_pair, end)(&Saved);
}

void run(const struct text *t, size_t argc, char **argv) {
	int status;
	struct val *result;

	v_init(&Interp.arg);
	v_init(&Interp.result);

	Interp.a.argv = xmalloc(argc, sizeof *Interp.a.argv);
	for (Interp.a.argc = 0; Interp.a.argc < argc; ++Interp.a.argc) {
		struct val *const tmp = &Interp.a.argv[Interp.a.argc];
		v_init(tmp);
		v_set_m(tmp, argv[Interp.a.argc], strlen(argv[Interp.a.argc]));
	}

	Interp.match.matches = xmalloc(Interp.match.size = 2, sizeof *Interp.match.matches);
	Interp.match.length = 0;

	Interp.m_start.index = xmalloc(Interp.m_start.size = 0, sizeof *Interp.m_start.index);
	Interp.m_end.index = xmalloc(Interp.m_end.size = 0, sizeof *Interp.m_end.index);

	stack_func(save_pair, init)(&Saved);
	atechit(cleanup);
	atechit(my_x_end);

	result = execute(t->start[0], v_undef());
	V_NUM(result);
	status = result->num >= 0.0 ? result->num + .5 : EXIT_FAILURE;
	v_delete(result);
	exit(status);
}