view src/ploki/main.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 "IO.h"
#include "Str.h"
#include "atechit.h"
#include "compile.h"
#include "deparse.h"
#include "expr.h"
#include "inc.h"
#include "main.h"
#include "main_io.h"
#include "main_label.h"
#include "main_opt.h"
#include "main_var.h"
#include "mars.h"
#include "opt.h"
#include "parse.h"
#include "random.h"
#include "re.h"
#include "run.h"
#include "text.h"
#include "transmogrify.h"
#include "val.h"
#include "venus.h"
#include "version.h"
#include "xmalloc.h"
#include "zz.h"

#include <ctype.h>
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NAME     "ploki"
#define WARN(x)  fprintf(stderr, "%s: %s: %s\n", Prog, x, strerror(errno))

const char *Prog = NAME;
IO *In, *Out, *Err;
struct venus Venus;
struct mars Mars;
struct Options Opt;
t_vr_container *Var_plain, *Var_hash;

static int my_mars_end_flag;
static void my_mars_end(void) {
	if (!my_mars_end_flag) {
		ma_end(&Mars);
	}
}

static void my_venus_end(void) {
	ve_end(&Venus);
}

static struct text text;
static void my_text_off(void) {
	text_off(&text);
}

static void xv_delete(void *v) {
	v_delete(v);
}

static void xsh_delete(void *sh) {
	sh_delete(sh);
}

static void var_end(void) {
	if (Var_plain) {
		vr_delete(Var_plain);
		Var_plain = 0;
	}
	if (Var_hash) {
		vr_delete(Var_hash);
		Var_hash = 0;
	}
}

static char *s_lastof(const char *s, const char *set) {
	const char *t = s + strlen(s);
	while (t > s) {
		--t;
		if (strchr(set, *t)) {
			return (char *)t;  /* const correctness is for C++ programmers! */
		}
	}
	return NULL;
}

static void usage(void) {
	printf(
			"Usage: %s [OPTIONS] [FILE [ARG]...]\n"
			"\n"
			"Available options:\n"
			"\n"
			"  -h           print a short help message and exit\n"
			"  -v           print version and configuration info and exit\n"
			"  -MO=Deparse  turn the compiled program back into ploki source\n"
			"  -O           disable any optimizations\n"
			"  -d FLAGS     print debugging info\n"
			"               FLAGS must be one or more of the following:\n"
			"                 h   hash tables\n"
			"                 o   opcode execution\n"
			"                 r   regex engine\n"
			"\n"
			"%s"
			, Prog,
			"Interpret FILE as ploki program and execute it, unless -MO=Deparse is\n"
			"specified. With no FILE, or when FILE is -, read from standard input.\n"
			"Any ARGs are passed through to the program.\n"
		  );
}

static void printinc(void) {
	size_t i;
	for (i = 0; inc_ludes[i]; ++i) {
		printf(" \"%s\"", inc_ludes[i]);
	}
}

int main(int argc, char **argv) {
	IO *f;
	int c;

	if (argc > 0 && argv[0][0]) {
		Prog = argv[0];
	}

	while ((c = opt_get(argc, argv, "-:?M:Od:hv")) != -1) {
		switch (c) {
invalid_option:
			fprintf(stderr, "%s: invalid option `%c'\n", Prog, c);
			return EXIT_FAILURE;

			/* option aliasing */
			case '-':
				if (!opt_arg) {
					goto invalid_option;
				}
				if (!strncmp("help", opt_arg, strlen(opt_arg))) {
					case 'h':
					case '?':
						usage();
						return EXIT_SUCCESS;
				}
				if (!strncmp("version", opt_arg, strlen(opt_arg))) {
					case 'v':
						printf(
							"This is %s v%s\n"
							"%cvsnprintf %csleep %c/dev/urandom %s%s %cM_PI %cM_E\nINC contains:",
							NAME,
							Version,
							HAVE_VSNPRINTF_P  ? '+' : '-',
							HAVE_SLEEP_P      ? '+' : '-',
							HAVE_DEV_URANDOM_P ? '+' : '-',
							DIR_END ? "+DIRSEP:" : "-DIRSEP",
							DIR_END ? DIR_END : "",
							#ifdef M_PI
								'+'
							#else
								'-'
							#endif
							,
							#ifdef M_E
								'+'
							#else
								'-'
							#endif
						);
						printinc();
						fputs("\nWritten by Lukas Mai\n", stdout);
						return EXIT_SUCCESS;
				}
				fprintf(stderr, "%s: invalid option `%s'\n", Prog, opt_arg);
				return EXIT_FAILURE;

			case 'd':
				if (!opt_arg || !opt_arg[0]) {
					fprintf(stderr, "%s: option `%c' requires an argument\n", Prog, c);
					return EXIT_FAILURE;
				} else {
					const char *a = opt_arg;
					for (; *a; ++a) {
						switch (*a) {
							case 'h': Opt.debug |= DBG_HASH;  break;
							case 'o': Opt.debug |= DBG_OPS;   break;
							case 'r': Opt.debug |= DBG_REGEX; break;
							default:
								fprintf(stderr, "%s: option d's argument must be one of [hor], not `%s'\n", Prog, a);
							return EXIT_FAILURE;
						}
					}
				}
				break;

			case 'M':
				if (!opt_arg || strncmp(opt_arg, "O=Deparse", strlen(opt_arg))) {
					goto invalid_option;
				}
				Opt.deparse = 1;
				break;

			case 'O':
				Opt.unoptimize = 1;
				break;

			case '\0':
				c = opt_err;
				goto invalid_option;

			default:
				NOTREACHED;
		}
	}
	argc -= opt_ind;
	argv += opt_ind;

	randseed();

	atechit(xend);
	io_init();
	atechit(io_end);

	In = io_enter("(stdin)", stdin, IO_READ | IO_BUFFERED);
	Out = io_enter("(stdout)", stdout, IO_WRITE | IO_TRUNCATE | IO_AUTOFLUSH);
	Err = io_enter("(stderr)", stderr, IO_WRITE | IO_TRUNCATE | IO_AUTOFLUSH);

	if (!argv[0] || (argv[0][0] == '-' && argv[0][1] == '\0')) {
		f = io_incr(In);
	} else {
		if (!(f = io_open(argv[0], IO_READ | IO_BUFFERED))) {
			WARN(argv[0]);
			return EXIT_FAILURE;
		}
	}

	/* ignore empty files */
	{
		size_t p;
		for (p = 0; isspace(c = io_peek(f, p)); ++p)
			;
		if (c == EOF) {
			if (io_err(f)) {
				WARN(io_name(f, NULL));
				io_decr(f);
				return EXIT_FAILURE;
			} else {
				io_decr(f);
				return EXIT_SUCCESS;
			}
		}
	}

	re_init();
	atechit(re_end);
	atechit(var_end);
	Var_plain = vr_new(xv_delete);
	Var_hash = vr_new(xsh_delete);

	text_on(&text);
	atechit(my_text_off);
	ma_init(&Mars);
	atechit(my_mars_end);
	ve_init(&Venus);
	atechit(my_venus_end);

	{
		size_t tmp = 0;
		char *eop = NULL;
		char bkp = 00;
		if (argv[0]) {
			if (DIR_END && (eop = s_lastof(argv[0], DIR_END))) {
				size_t i;
				bkp = eop[1];
				eop[1] = '\0';
				for (i = 0; inc_ludes[i]; ++i) {
					if (inc_ludes[i][0] == '\0') {
						inc_ludes[i] = argv[0];
					}
				}
			}
		}
		parse(f, &text, &tmp);
		if (eop) {
			eop[1] = bkp;
		}
	}
	io_decr(f);

	expr_init();
	atechit(expr_end);

	compile(&text);
	if (!Opt.unoptimize) {
		transmogrify(&text);
	}

	my_mars_end_flag = 1;
	ma_end(&Mars);

	if (Opt.deparse) {
		deparse(&text);
		return 0;
	}

	run(&text, argc, argv);
}