view src/ploki/main.c @ 8916:0234daffd946

<oerjan> addquote <int-e> I couldn\'t help thinking that maybe if one considers the ramifications in full detail it will turn out that overthinking is often not helpful and therefore, not something to be proud of.
author HackBot
date Sun, 14 Aug 2016 02:31:47 +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);
}