view src/ploki/run.c @ 12292:d51f2100210c draft

<kspalaiologos> `` cat <<<"asmbf && bfi output.b" > /hackenv/ibin/asmbf
author HackEso <hackeso@esolangs.org>
date Thu, 02 Jan 2020 15:38:21 +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);
}