changeset 4215:7a6bc310b732

<shachaf> tar xjf ploki-0.6.5.1.tar.bz2
author HackBot
date Fri, 20 Dec 2013 22:04:38 +0000
parents 59a88d73e848
children c83a957038d7
files ploki/Compile ploki/GNUmakefile ploki/IGNORE ploki/IO.c ploki/IO.depend ploki/IO.h ploki/MakeSkel ploki/Makefile ploki/README ploki/Str.c ploki/Str.depend ploki/Str.h ploki/TODO ploki/VERSION ploki/atechit.c ploki/atechit.depend ploki/atechit.h ploki/compile.c ploki/compile.depend ploki/compile.h ploki/config.h ploki/deparse.c ploki/deparse.depend ploki/deparse.h ploki/doc/perl-ploki-regex.txt ploki/doc/ploki-expr.txt ploki/doc/ploki-ins.txt ploki/doc/ploki-instr.txt ploki/doc/ploki-prog.txt ploki/doc/ploki-syn.txt ploki/examples/SOME FILE ploki/examples/arg.pk ploki/examples/beer.pk ploki/examples/bf2c.pk ploki/examples/calc.pk ploki/examples/cat.pk ploki/examples/env.pk ploki/examples/fac.pk ploki/examples/fact.pk ploki/examples/facul.pk ploki/examples/fib.pk ploki/examples/fibcheck.pk ploki/examples/foobar.pk ploki/examples/goto.pk ploki/examples/hanoi.pk ploki/examples/hell.pk ploki/examples/hello.pk ploki/examples/insert.pk ploki/examples/io.pk ploki/examples/list.pk ploki/examples/loop.pk ploki/examples/mark.pk ploki/examples/mod.pk ploki/examples/num.pk ploki/examples/omfg.pk ploki/examples/out.pk ploki/examples/perl-util.pk ploki/examples/quine.pk ploki/examples/ref.pk ploki/examples/regex.pk ploki/examples/reset.pk ploki/examples/ret.pk ploki/examples/rev.pk ploki/examples/revrec.pk ploki/examples/rfc822-fun.pk ploki/examples/rfc822.pk ploki/examples/string.pk ploki/examples/subst.pk ploki/examples/throw.pk ploki/examples/try.pk ploki/examples/uncomment.pk ploki/examples/var.pk ploki/examples/write.pk ploki/expr.c ploki/expr.depend ploki/expr.h ploki/hang.c ploki/hang.depend ploki/hang.h ploki/hash.c ploki/hash.depend ploki/hash.h ploki/inc.c ploki/inc.depend ploki/inc.h ploki/indent/ploki.vim ploki/kork.c ploki/kork.depend ploki/kork.h ploki/list.c ploki/list.depend ploki/list.h ploki/main.c ploki/main.depend ploki/main.h ploki/main_io.h ploki/main_label.h ploki/main_opt.h ploki/main_var.h ploki/mars.c ploki/mars.depend ploki/mars.h ploki/match.c ploki/match.depend ploki/match.h ploki/op.c ploki/op.depend ploki/op.h ploki/opt.c ploki/opt.depend ploki/opt.h ploki/parse.c ploki/parse.depend ploki/parse.h ploki/pp.c ploki/pp.depend ploki/pp.h ploki/random.c ploki/random.depend ploki/random.h ploki/re.c ploki/re.depend ploki/re.h ploki/re_block.c.h ploki/run.c ploki/run.depend ploki/run.h ploki/stack.h ploki/strhash.c ploki/strhash.depend ploki/strhash.h ploki/strutil.c ploki/strutil.depend ploki/strutil.h ploki/sub.c ploki/sub.depend ploki/sub.h ploki/syntax/ploki.vim ploki/t/00-compile.pk ploki/t/10-regress.pk ploki/tags ploki/text.c ploki/text.depend ploki/text.h ploki/transmogrify.c ploki/transmogrify.depend ploki/transmogrify.h ploki/try/poly.lhs ploki/try/poly.poly ploki/try/t.pk ploki/val.c ploki/val.depend ploki/val.h ploki/variable.c ploki/variable.depend ploki/variable.h ploki/venus.c ploki/venus.depend ploki/venus.h ploki/version.c ploki/version.c.in ploki/version.depend ploki/version.h ploki/xmalloc.c ploki/xmalloc.depend ploki/xmalloc.h ploki/zz.c ploki/zz.depend ploki/zz.h
diffstat 169 files changed, 15929 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/Compile	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo cc -o ploki *.c -lm
+cc -o ploki *.c -lm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/GNUmakefile	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,22 @@
+.PHONY: all clean realclean distclean dist remake
+
+include MakeSkel
+
+$(OBJ:.o=.depend): %.depend: %.c %.h
+	$(DEPEND) $< >$@
+
+-include $(OBJ:.o=.depend)
+
+Makefile: MakeSkel GNUmakefile $(OBJ:.o=.depend)
+	$(RMF) Makefile
+	$(CP) MakeSkel Makefile
+	$(CAT) *.depend >>Makefile
+
+.PHONY: dist
+dist: Makefile tags distclean
+	plokidir="`$(BASENAME) \"\`$(PWD)\`\"`" \
+	version="`$(CAT) VERSION`" && \
+	cd .. && \
+	$(RMF) "ploki-$$version.tar$(ZIP_EXT)" && \
+	$(TAR) -cf "ploki-$$version.tar" --exclude-from="$$plokidir/IGNORE" "$$plokidir" && \
+	$(ZIP) "ploki-$$version.tar"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/IGNORE	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+.git
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/IO.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,515 @@
+#include "config.h"
+#include "IO.h"
+#include "Str.h"
+#include "main.h"
+#include "main_io.h"
+#include "xmalloc.h"
+#include "zz.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+struct IO {
+	struct IO *prev, *next;
+	enum io_flags mode;
+	FILE *fp;
+	char *name;
+	size_t refs;
+	String *buf;
+	long told;
+	enum {
+		DI_NONE,
+		DI_RD,
+		DI_WR
+	} dirct;
+};
+
+static IO *Root;
+
+static void sanitycheck(enum io_flags m) {
+	#if !DEBUG_P
+		(void)m;
+	#endif
+	assert(
+			(
+			 m & IO_READ ||
+			 m & IO_WRITE ||
+			 m & IO_APPEND
+			) &&
+			!(
+				m & IO_WRITE &&
+				m & IO_APPEND
+			 ) &&
+			(
+			 !(m & IO_BUFFERED) || m & IO_READ
+			) &&
+			(
+			 !(m & IO_AUTOFLUSH) ||
+			 (
+			  m & IO_WRITE ||
+			  m & IO_APPEND
+			 )
+			) &&
+			(
+			 !(m & IO_WRITE) || (m & IO_TRUNCATE || m & IO_READ)
+			) &&
+			(
+			 !(m & IO_TRUNCATE) || m & IO_WRITE
+			)
+		  );
+}
+
+void io_init(void) {
+}
+
+static void io_delete(IO *io) {
+	if (io->mode & IO_BUFFERED) {
+		St_clear(io->buf);
+		xfree(io->buf);
+	}
+	if (io->fp && io->fp != stderr) {
+		if (fclose(io->fp)) {
+			fprintf(io_fp(Err), "%s: %s: %s\n", Prog, io->name, strerror(errno));
+		}
+	}
+	xfree(io->name);
+	if (io->prev) {
+		io->prev->next = io->next;
+	} else {
+		assert(io == Root);
+		Root = io->next;
+	}
+	if (io->next) {
+		io->next->prev = io->prev;
+	}
+	xfree(io);
+}
+
+void io_end(void) {
+	while (Root) {
+		io_delete(Root);
+	}
+}
+
+static char *xstrdup(const char *s) {
+	const size_t len = strlen(s) + 1;
+	char *const tmp = xmalloc(len, sizeof *tmp);
+	memcpy(tmp, s, len);
+	return tmp;
+}
+
+IO *io_enter(const char *name, FILE *fp, enum io_flags mode) {
+	IO *io;
+	sanitycheck(mode);
+	io = xmalloc(1, sizeof *io);
+	io->fp = fp;
+	io->mode = mode;
+	if (mode & IO_BUFFERED) {
+		io->buf = xmalloc(1, sizeof *io->buf);
+		St_init(io->buf);
+		io->told = -1;
+	}
+	io->name = xstrdup(name);
+	io->refs = 1;
+
+	io->dirct = DI_NONE;
+
+	io->prev = NULL;
+	io->next = Root;
+	if (Root) {
+		Root->prev = io;
+	}
+	Root = io;
+	return io;
+}
+
+const char *io_name(const IO *io, String *s) {
+	if (s) {
+		St_cpy_s(s, io->name);
+	}
+	return io->name;
+}
+
+IO *io_open(const char *name, enum io_flags mode) {
+	FILE *fp;
+	char mbuf[4], *p = mbuf;
+	sanitycheck(mode);
+
+	if (mode & IO_APPEND) {
+		*p++ = 'a';
+		if (mode & IO_READ) {
+			*p++ = '+';
+		}
+	} else if (mode & IO_WRITE) {
+		if (mode & IO_TRUNCATE) {
+			*p++ = 'w';
+			if (mode & IO_READ) {
+				*p++ = '+';
+			}
+		} else {
+			assert(mode & IO_READ);
+			*p++ = 'r';
+			*p++ = '+';
+		}
+	} else {
+		assert(mode & IO_READ);
+		*p++ = 'r';
+	}
+
+	if (mode & IO_BINARY) {
+		*p++ = 'b';
+	}
+	*p = '\0';
+
+	if (!(fp = fopen(name, mbuf))) {
+		return NULL;
+	}
+	return io_enter(name, fp, mode);
+}
+
+IO *io_incr(IO *io) {
+	++io->refs;
+	return io;
+}
+
+void io_decr(IO *io) {
+	if (!--io->refs) {
+		io_delete(io);
+	}
+}
+
+int io_close(IO *io) {
+	int ret;
+	assert(io->fp != NULL);
+	ret = fclose(io->fp);
+	io->fp = NULL;
+	if (io->mode & IO_BUFFERED) {
+		St_clear(io->buf);
+		xfree(io->buf);
+		io->mode &= ~IO_BUFFERED;
+	}
+	return ret;
+}
+
+int io_bufred(const IO *io) {
+	return !!(io->mode & IO_BUFFERED);
+}
+
+void io_unbuffer(IO *io) {
+	assert(io->mode & IO_BUFFERED);
+	if (St_len(io->buf) && io->told != -1) {
+		io_seek(io, io->told, SEEK_SET);
+	}
+	St_clear(io->buf);
+	xfree(io->buf);
+	io->mode &= ~IO_BUFFERED;
+}
+
+FILE *io_fp(const IO *io) {
+	return io->fp;
+}
+
+static void bufk(IO *f, size_t n) {
+	String tmp;
+	assert(f->mode & IO_BUFFERED);
+
+	if (St_len(f->buf) >= n || feof(f->fp) || ferror(f->fp)) {
+		return;
+	}
+
+	assert(f->dirct != DI_WR);
+	#if 0
+	if (f->dirct == DI_WR) {
+		io_seek(f, 0, SEEK_CUR);
+	}
+	f->dirct = DI_RD;
+	#endif
+
+	if (!(St_len(f->buf) || f->mode & IO_BINARY)) {
+		f->told = ftell(f->fp);
+	}
+
+	St_init(&tmp);
+	while (
+			St_len(f->buf) < n &&
+			!feof(f->fp) && !ferror(f->fp) &&
+			St_read(&tmp, f->fp, n - St_len(f->buf))
+		  ) {
+		St_cat(f->buf, &tmp);
+	}
+	St_clear(&tmp);
+}
+
+#ifdef EBADF
+	#define BADF ((void)(errno = EBADF))
+#else
+	#define BADF ((void)0)
+#endif
+
+#define XMODE(c, x) \
+do {                \
+	if (!(c)) {     \
+		BADF;       \
+		return (x); \
+	} \
+} while (0)
+
+#define WMODE(f, x) XMODE((f)->mode & IO_WRITE || (f)->mode & IO_APPEND, (x))
+#define PMODE(f, x) XMODE((f)->mode & IO_BUFFERED, (x))
+#define RMODE(f, x) XMODE((f)->mode & IO_READ, (x))
+
+const char *io_bufptr(IO *io) {
+	PMODE(io, NULL);
+	return St_ptr(io->buf);
+}
+
+int io_flush(IO *f) {
+	WMODE(f, EOF);
+	return fflush(f->fp);
+}
+
+int io_err(const IO *f) {
+	return ferror(f->fp);
+}
+
+int io_eof(const IO *f) {
+	return feof(f->fp);
+}
+
+int io_peek(IO *f, size_t pos) {
+	PMODE(f, EOF);
+	bufk(f, pos + 1);
+	if (St_len(f->buf) <= pos) {
+		return EOF;
+	}
+	return ST_INDEX(f->buf, pos);
+}
+
+int io_cmppeek(IO *f, size_t o, const void *p, size_t n) {
+	size_t i;
+	PMODE(f, -1);
+
+	for (i = 0; i < n; ++i) {
+		bufk(f, o + i + 1u);
+		if (ST_INDEX(f->buf, o + i) != i[(const unsigned char *)p]) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int io_xcmp(IO *f, size_t a, size_t b, size_t n) {
+	const size_t max = a > b ? a : b;
+	PMODE(f, -1);
+	bufk(f, max + n);
+	if (St_len(f->buf) < max + n) {
+		return 1;
+	}
+	return memcmp(St_ptr(f->buf) + a, St_ptr(f->buf) + b, n) != 0;
+}
+
+size_t io_read(IO *f, String *s, size_t n) {
+	RMODE(f, -1);
+
+	if (f->mode & IO_BUFFERED) {
+		String null;
+		size_t old;
+
+		bufk(f, n);
+		old = St_len(f->buf);
+		St_init(&null);
+		St_substr(s, f->buf, 0, n, &null);
+		St_clear(&null);
+		old -= St_len(f->buf);
+		if (St_len(f->buf) && old && !(f->mode & IO_BINARY) && f->told != -1) {
+			const long keep = ftell(f->fp);
+			if (fseek(f->fp, f->told, SEEK_SET) != -1) {
+				size_t i;
+				for (i = 0; i < old; ++i) {
+					getc(f->fp);
+				}
+				f->told = ftell(f->fp);
+				f->dirct = DI_RD;
+				fseek(f->fp, keep, SEEK_SET);
+			}
+		}
+		return old;
+	} else {
+		if (f->dirct == DI_WR) {
+			fseek(f->fp, 0, SEEK_CUR);
+		}
+		f->dirct = DI_RD;
+		return St_read(s, f->fp, n);
+	}
+}
+
+int io_getc(IO *f) {
+	RMODE(f, EOF);
+
+	if (f->mode & IO_BUFFERED) {
+		int c;
+		bufk(f, 1);
+		c = St_shift(f->buf);
+		if (St_len(f->buf) && c != EOF && !(f->mode & IO_BINARY) && f->told != -1) {
+			const long keep = ftell(f->fp);
+			if (fseek(f->fp, f->told, SEEK_SET) != -1) {
+				getc(f->fp);
+				f->told = ftell(f->fp);
+				f->dirct = DI_RD;
+				fseek(f->fp, keep, SEEK_SET);
+			}
+		}
+		return c;
+	}
+
+	if (f->dirct == DI_WR) {
+		fseek(f->fp, 0, SEEK_CUR);
+	}
+	f->dirct = DI_RD;
+	return getc(f->fp);
+}
+
+size_t io_getline(IO *f, String *s) {
+	RMODE(f, -1);
+
+	if (f->mode & IO_BUFFERED) {
+		size_t p;
+		size_t old;
+
+		p = St_chr(f->buf, '\n') + 1u;
+		if (p) {
+			old = St_len(f->buf);
+			if (s) {
+				St_cpy_m(s, St_ptr(f->buf), p);
+			}
+			St_del(f->buf, 0, p);
+		} else {
+			for (p = St_len(f->buf); io_peek(f, p) != EOF; ++p) {
+				if (ST_LASTCHAR(f->buf) == '\n') {
+					break;
+				}
+			}
+			old = St_len(f->buf);
+			if (s) {
+				St_cpy(s, f->buf);
+			}
+			St_zero(f->buf);
+		}
+		old -= St_len(f->buf);
+		if (St_len(f->buf) && old && !(f->mode & IO_BINARY) && f->told != -1) {
+			const long keep = ftell(f->fp);
+			if (fseek(f->fp, f->told, SEEK_SET) != -1) {
+				size_t i;
+				for (i = 0; i < old; ++i) {
+					getc(f->fp);
+				}
+				f->dirct = DI_RD;
+				f->told = ftell(f->fp);
+				fseek(f->fp, keep, SEEK_SET);
+			}
+		}
+		return old;
+	} else {
+		int c;
+		size_t n;
+
+		if (f->dirct == DI_WR) {
+			fseek(f->fp, 0, SEEK_CUR);
+		}
+		f->dirct = DI_RD;
+
+		if (s) {
+			St_zero(s);
+		}
+		n = 0;
+		while ((c = getc(f->fp)) != EOF) {
+			if (s) {
+				St_cat_c(s, c);
+			}
+			++n;
+			if (c == '\n') {
+				break;
+			}
+		}
+		if (!n && ferror(f->fp)) {
+			return -1;
+		}
+		return n;
+	}
+}
+
+size_t io_write(IO *f, const String *s) {
+	size_t ret;
+	WMODE(f, -1);
+	if (f->dirct == DI_RD) {
+		fseek(f->fp, 0, SEEK_CUR);
+	}
+	f->dirct = DI_WR;
+	ret = ST_WRITE(s, f->fp);
+	if (f->mode & IO_AUTOFLUSH) {
+		fflush(f->fp);
+	}
+	return ret;
+}
+
+size_t io_write_m(IO *f, const void *p, size_t n) {
+	size_t ret;
+	WMODE(f, -1);
+	if (f->dirct == DI_RD) {
+		fseek(f->fp, 0, SEEK_CUR);
+	}
+	f->dirct = DI_WR;
+	ret = fwrite(p, 1, n, f->fp);
+	if (f->mode & IO_AUTOFLUSH) {
+		fflush(f->fp);
+	}
+	return ret;
+}
+
+size_t io_write_s(IO *f, const char *s) {
+	return io_write_m(f, s, strlen(s));
+}
+
+int io_putc(IO *f, int c) {
+	int ret;
+	WMODE(f, EOF);
+	if (f->dirct == DI_RD) {
+		fseek(f->fp, 0, SEEK_CUR);
+	}
+	f->dirct = DI_WR;
+	ret = putc(c, f->fp);
+	if (f->mode & IO_AUTOFLUSH) {
+		fflush(f->fp);
+	}
+	return ret;
+}
+
+long io_tell(IO *f) {
+	if (f->mode & IO_BUFFERED) {
+		if (f->mode & IO_BINARY) {
+			const long ret = ftell(f->fp);
+			if (ret == -1) {
+				return ret;
+			}
+			return ret - St_len(f->buf);
+		}
+		if (St_len(f->buf)) {
+			return f->told;
+		}
+	}
+	return ftell(f->fp);
+}
+
+int io_seek(IO *f, long off, enum io_whence w) {
+	if (f->mode & IO_BUFFERED) {
+		St_zero(f->buf);
+	}
+	f->dirct = DI_NONE;
+	return fseek(f->fp, off, w);
+}
+
+void io_clearerr(IO *f) {
+	clearerr(f->fp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/IO.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+IO.o: IO.c config.h IO.h Str.h main.h main_io.h xmalloc.h zz.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/IO.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,65 @@
+#ifndef IO_H_
+#define IO_H_
+
+#include "config.h"
+#include "Str.h"
+
+#include <stdio.h>
+
+typedef struct IO IO;
+
+enum io_flags {
+	IO_BINARY    = 1,
+	IO_READ      = 2 * IO_BINARY,
+	IO_WRITE     = 2 * IO_READ,
+	IO_APPEND    = 2 * IO_WRITE,
+	IO_TRUNCATE  = 2 * IO_APPEND,
+	IO_BUFFERED  = 2 * IO_TRUNCATE,
+	IO_AUTOFLUSH = 2 * IO_BUFFERED
+};
+
+enum io_whence {
+	IOS_START = SEEK_SET,
+	IOS_CUR   = SEEK_CUR,
+	IOS_END   = SEEK_END
+};
+
+void io_init(void);
+void io_end(void);
+const char *io_name(const IO *, String *);
+IO *io_enter(const char *, FILE *, enum io_flags);
+IO *io_open(const char *, enum io_flags);
+IO *io_incr(IO *);
+void io_decr(IO *);
+int io_close(IO *);
+
+ATTR_PURE
+int io_bufred(const IO *);
+void io_unbuffer(IO *);
+const char *io_bufptr(IO *);
+
+ATTR_PURE
+FILE *io_fp(const IO *);
+
+int io_flush(IO *);
+ATTR_PURE
+int io_err(const IO *);
+ATTR_PURE
+int io_eof(const IO *);
+int io_peek(IO *, size_t);
+int io_cmppeek(IO *, size_t, const void *, size_t);
+int io_xcmp(IO *, size_t, size_t, size_t);
+
+size_t io_read(IO *, String *, size_t);
+int io_getc(IO *);
+size_t io_getline(IO *, String *);
+size_t io_write(IO *, const String *);
+size_t io_write_m(IO *, const void *, size_t);
+size_t io_write_s(IO *, const char *);
+int io_putc(IO *, int);
+
+long io_tell(IO *);
+int io_seek(IO *, long, enum io_whence);
+void io_clearerr(IO *);
+
+#endif /* IO_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/MakeSkel	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,73 @@
+# vi: set ft=make:
+PREFIX = /usr/local
+INSTDIR = $(PREFIX)/bin
+
+CC = gcc
+#CC = cc
+
+#COPT = -DDEBUGGING -g
+COPT = -g -O2
+#COPT = -xO4 -xalias_level=std -xbuiltin=%all -xdepend -xinline=%auto -xlibmil -xtarget=native
+#COPT = -O
+#COPT =
+
+LDOPT =
+#LDOPT = -s
+
+CPP = $(CC) -E
+#CPP = cpp
+
+DEPEND = $(CPP) -MM
+#DEPEND = $(CC) -xM1
+
+CFLAGS = $(COPT) -W -Wall -Wundef -Wshadow -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -std=gnu9x -pedantic -fstrict-aliasing -pipe
+#CFLAGS = $(COPT) -W -Wall -Wundef -Wendif-labels -Wshadow -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -Wdisabled-optimization -std=gnu99 -pedantic -fstrict-aliasing -pipe
+#CFLAGS = $(COPT) -Xc
+#CFLAGS = $(COPT)
+
+LDFLAGS = $(LDOPT) -lm
+
+ECHO = echo
+CAT = cat
+CP = cp
+RMF = rm -f
+TAR = tar
+CTAGS = ctags
+#CTAGS = ctags --langmap=c:.c.h
+ZIP = bzip2
+ZIP_EXT = .bz2
+BASENAME = basename
+PWD = pwd
+
+# # # # #
+
+OBJ = IO.o Str.o atechit.o compile.o deparse.o expr.o hang.o hash.o inc.o \
+      kork.o list.o main.o mars.o match.o op.o opt.o parse.o pp.o random.o \
+      re.o run.o strhash.o strutil.o sub.o text.o transmogrify.o val.o \
+      variable.o venus.o version.o xmalloc.o zz.o
+
+all: tags ploki
+
+tags: *.c *.h
+	$(CTAGS) *.c *.h
+
+distclean: clean
+	$(RMF) core examples/core a.out
+clean:
+	$(RMF) *.o ploki
+realclean: clean
+	$(RMF) *.depend Makefile
+
+remake: clean all
+
+ploki: $(OBJ)
+	$(CC) -o ploki $(OBJ) $(LDFLAGS)
+
+install: ploki
+	$(CP) ploki '$(INSTDIR)/'
+
+version.c: version.c.in VERSION
+	$(CP) version.c.in version.c
+	$(ECHO) '"'"`$(CAT) VERSION`"'";' >>version.c
+
+########################################################################
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/Makefile	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,135 @@
+# vi: set ft=make:
+PREFIX = /usr/local
+INSTDIR = $(PREFIX)/bin
+
+CC = gcc
+#CC = cc
+
+#COPT = -DDEBUGGING -g
+COPT = -g -O2
+#COPT = -xO4 -xalias_level=std -xbuiltin=%all -xdepend -xinline=%auto -xlibmil -xtarget=native
+#COPT = -O
+#COPT =
+
+LDOPT =
+#LDOPT = -s
+
+CPP = $(CC) -E
+#CPP = cpp
+
+DEPEND = $(CPP) -MM
+#DEPEND = $(CC) -xM1
+
+CFLAGS = $(COPT) -W -Wall -Wundef -Wshadow -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -std=gnu9x -pedantic -fstrict-aliasing -pipe
+#CFLAGS = $(COPT) -W -Wall -Wundef -Wendif-labels -Wshadow -Wpointer-arith -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Winline -Wdisabled-optimization -std=gnu99 -pedantic -fstrict-aliasing -pipe
+#CFLAGS = $(COPT) -Xc
+#CFLAGS = $(COPT)
+
+LDFLAGS = $(LDOPT) -lm
+
+ECHO = echo
+CAT = cat
+CP = cp
+RMF = rm -f
+TAR = tar
+CTAGS = ctags
+#CTAGS = ctags --langmap=c:.c.h
+ZIP = bzip2
+ZIP_EXT = .bz2
+BASENAME = basename
+PWD = pwd
+
+# # # # #
+
+OBJ = IO.o Str.o atechit.o compile.o deparse.o expr.o hang.o hash.o inc.o \
+      kork.o list.o main.o mars.o match.o op.o opt.o parse.o pp.o random.o \
+      re.o run.o strhash.o strutil.o sub.o text.o transmogrify.o val.o \
+      variable.o venus.o version.o xmalloc.o zz.o
+
+all: tags ploki
+
+tags: *.c *.h
+	$(CTAGS) *.c *.h
+
+distclean: clean
+	$(RMF) core examples/core a.out
+clean:
+	$(RMF) *.o ploki
+realclean: clean
+	$(RMF) *.depend Makefile
+
+remake: clean all
+
+ploki: $(OBJ)
+	$(CC) -o ploki $(OBJ) $(LDFLAGS)
+
+install: ploki
+	$(CP) ploki '$(INSTDIR)/'
+
+version.c: version.c.in VERSION
+	$(CP) version.c.in version.c
+	$(ECHO) '"'"`$(CAT) VERSION`"'";' >>version.c
+
+########################################################################
+IO.o: IO.c config.h IO.h Str.h main.h main_io.h xmalloc.h zz.h
+Str.o: Str.c config.h Str.h strutil.h xmalloc.h
+atechit.o: atechit.c zz.h config.h atechit.h main.h
+compile.o: compile.c config.h Str.h compile.h text.h op.h IO.h expr.h \
+  re.h stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h \
+  main_var.h zz.h
+deparse.o: deparse.c config.h Str.h deparse.h text.h op.h IO.h expr.h \
+  re.h stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h \
+  main_io.h main_label.h mars.h venus.h hash.h zz.h
+expr.o: expr.c config.h Str.h expr.h op.h IO.h re.h stack.h xmalloc.h \
+ strhash.h val.h kork.h list.h sub.h variable.h hang.h main_io.h \
+ main_label.h mars.h venus.h hash.h main_var.h match.h pp.h random.h \
+ run.h text.h zz.h
+hang.o: hang.c config.h hang.h
+hash.o: hash.c config.h hash.h main.h main_opt.h random.h xmalloc.h
+inc.o: inc.c config.h inc.h
+kork.o: kork.c config.h IO.h Str.h kork.h strutil.h xmalloc.h
+list.o: list.c list.h config.h val.h IO.h Str.h kork.h sub.h xmalloc.h
+main.o: main.c IO.h config.h Str.h atechit.h compile.h text.h op.h expr.h \
+  re.h stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h \
+  deparse.h inc.h main.h main_io.h main_label.h mars.h venus.h hash.h \
+  main_opt.h main_var.h opt.h parse.h random.h run.h transmogrify.h \
+  version.h zz.h
+mars.o: mars.c Str.h config.h mars.h op.h IO.h expr.h re.h stack.h \
+  xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h
+match.o: match.c IO.h config.h Str.h main_io.h main_opt.h match.h re.h \
+  val.h kork.h list.h sub.h run.h op.h expr.h stack.h xmalloc.h strhash.h \
+  variable.h text.h
+op.o: op.c IO.h config.h Str.h expr.h op.h re.h stack.h xmalloc.h \
+  strhash.h val.h kork.h list.h sub.h variable.h main_label.h mars.h \
+  venus.h hash.h
+opt.o: opt.c opt.h
+parse.o: parse.c IO.h config.h Str.h inc.h main.h main_label.h mars.h \
+  op.h expr.h re.h stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h \
+  variable.h venus.h hash.h parse.h text.h
+pp.o: pp.c IO.h config.h Str.h kork.h list.h main_io.h main_opt.h match.h \
+  re.h val.h sub.h pp.h run.h op.h expr.h stack.h xmalloc.h strhash.h \
+  variable.h text.h
+random.o: random.c config.h random.h
+re.o: re.c config.h IO.h Str.h hash.h main_io.h main_opt.h re.h xmalloc.h \
+ zz.h re_block.c.h
+run.o: run.c config.h IO.h Str.h atechit.h expr.h op.h re.h stack.h \
+  xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h hang.h main.h \
+  main_io.h main_label.h mars.h venus.h hash.h main_opt.h run.h text.h \
+  zz.h
+strhash.o: strhash.c hash.h strhash.h config.h strutil.h xmalloc.h
+strutil.o: strutil.c strutil.h config.h
+sub.o: sub.c expr.h config.h Str.h op.h IO.h re.h stack.h xmalloc.h \
+  strhash.h val.h kork.h list.h sub.h variable.h main_opt.h \
+  transmogrify.h text.h
+text.o: text.c text.h op.h IO.h config.h Str.h expr.h re.h stack.h \
+  xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h
+transmogrify.o: transmogrify.c config.h Str.h expr.h op.h IO.h re.h \
+  stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h \
+  main_label.h mars.h venus.h hash.h text.h transmogrify.h zz.h
+val.o: val.c config.h IO.h Str.h val.h kork.h list.h sub.h xmalloc.h
+variable.o: variable.c config.h Str.h strhash.h variable.h xmalloc.h
+venus.o: venus.c Str.h config.h hash.h op.h IO.h expr.h re.h stack.h \
+  xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h venus.h zz.h
+version.o: version.c version.h
+xmalloc.o: xmalloc.c config.h main.h xmalloc.h
+zz.o: zz.c zz.h config.h main.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/README	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,32 @@
+DESCRIPTION
+
+This is the ploki package. ploki is a programming language defined
+by its implementation and designed by accident. There are short example
+scripts in the examples/ subdirectory. You might want to copy
+syntax/ploki.vim and indent/ploki.vim into your ~/.vim/ directory if you're
+using vim; to get automatic filetype detection, create filetype.vim as
+described in :help new-filetype and add the following entry:
+    au BufNewFile,BufRead *.pk          setf ploki
+
+See the files in doc/ for a description of the language.
+
+BUILDING
+
+You need a C compiler. Compile all *.c files into a single executable called
+'ploki'. That's it.
+
+If you have make, typing 'make ploki' should invoke your C compiler with the
+right arguments. You may need to adjust some configuration variables: CC is
+the C compiler used; CFLAGS are default arguments for the compiler; LDFLAGS
+are default arguments for the linker.
+
+You should edit MakeSkel if you're using GNU make, Makefile otherwise. Note
+that Makefile is autogenerated by GNU make from MakeSkel and *.depend.
+
+There is no 'configure' script; ploki is almost completely standard C and
+should Just Work(TM). But have a look at config.h if you want to tweak the
+autodetection of gcc, C99, presence of /dev/urandom, etc.
+
+
+Have fun!
+Lukas Mai
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/Str.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,794 @@
+#include "config.h"
+#include "Str.h"
+#include "strutil.h"
+#include "xmalloc.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+enum {MAGIC = 4};
+
+#define OFFSET_OFF(s)                                               \
+do {                                                                \
+	if ((s)->offset) {                                              \
+		memmove((s)->buf - (s)->offset, (s)->buf, (s)->length + 1); \
+		(s)->buf -= (s)->offset;                                    \
+		(s)->offset = 0;                                            \
+	}                                                               \
+} while (0)
+
+
+void
+St_init(String *s) {
+	s->buf = xmalloc(s->size = MAGIC, sizeof *s->buf);
+	s->buf[s->offset = s->length = 0] = '\0';
+}
+
+void
+St_fake(String *s, char *p, size_t n) {
+	assert(s != NULL);
+	assert(p != NULL);
+	s->buf = p;
+	s->offset = 0;
+	s->length = s->size = n;
+}
+
+void
+St_clear(String *s) {
+	assert(s != NULL);
+	if (!s->buf) {
+		return;
+	}
+	xfree(s->buf - s->offset);
+	s->buf = NULL;
+	DEBUG(s->offset = s->length = s->size = 0;)
+}
+
+#if 0
+void
+(St_zero)(String *s) {
+	St_zero(s);
+}
+
+char *
+(St_ptr)(const String *s) {
+	return St_ptr(s);
+}
+
+size_t
+(St_len)(const String *s) {
+	return St_len(s);
+}
+#endif
+
+static void
+St_grow(String *s, size_t n) {
+	if (s->size - s->offset <= n) {
+		OFFSET_OFF(s);
+		if (s->size <= n) {
+			s->buf = xrealloc(s->buf, n + 1u);
+			s->size = n + 1u;
+		}
+	}
+}
+
+void
+St_trunc(String *s, size_t n) {
+	if (s->length > n) {
+		s->buf[s->length = n] = '\0';
+	}
+	if (s->size > n + 1 && n >= MAGIC) {
+		OFFSET_OFF(s);
+		s->buf = xrealloc(s->buf, n + 1);
+		s->size = n + 1;
+	}
+}
+
+#if 0
+int
+St_lastchar(const String *s) {
+	return ST_LASTCHAR(s);
+}
+
+int
+St_firstchar(const String *s) {
+	return ST_FIRSTCHAR(s);
+}
+
+int
+St_chop(String *s) {
+	int tmp;
+
+	if (!s->length) {
+		return EOF;
+	}
+	tmp = (unsigned char)s->buf[s->length - 1];
+	s->buf[--s->length] = '\0';
+	return tmp;
+}
+#endif
+
+int
+St_shift(String *s) {
+	int tmp;
+
+	if (!s->length) {
+		return EOF;
+	}
+	tmp = (unsigned char)s->buf[0];
+	++s->offset;
+	++s->buf;
+	--s->length;
+	return tmp;
+}
+
+
+size_t
+St_shiftws(String *s) {
+	size_t n;
+
+	for (n = 0; n < s->length && isspace((unsigned char)s->buf[n]); ++n)
+		;
+	s->offset += n;
+	s->buf += n;
+	s->length -= n;
+
+	return n;
+}
+
+#if 0
+int
+St_index(const String *s, size_t n) {
+	return ST_INDEX(s, n);
+}
+#endif
+
+size_t
+St_chr(const String *s, int c) {
+	const char *tmp;
+	if (!(tmp = memchr(s->buf, c, s->length))) {
+		return -1;
+	}
+	return tmp - s->buf;
+}
+
+#if 0
+size_t
+St_rchr(const String *s, int c) {
+	size_t i;
+
+	for (i = s->length; i; --i) {
+		if (s->buf[i - 1] == c)
+			return i - 1;
+	}
+	return -1;
+}
+#endif
+
+int
+St_cmp(const String *s, const String *t) {
+	return u_cmp(St_ptr(s), St_len(s), St_ptr(t), St_len(t));
+}
+
+int
+St_cmp_m(const String *s, const void *m, size_t n) {
+	return u_cmp(St_ptr(s), St_len(s), m, n);
+}
+
+#if 0
+int
+St_cmp_s(const String *s, const char *tz) {
+	return u_cmp(St_ptr(s), St_len(s), tz, strlen(tz));
+}
+#endif
+
+#define NCMP(s, m, n)                                                  \
+do {                                                                   \
+	return memcmp((s)->buf, m, (s)->length < (n) ? (s)->length : (n)); \
+} while (0)
+
+#if 0
+int
+St_ncmp(const String *s, const String *t) {
+	NCMP(s, t->buf, t->length);
+}
+#endif
+
+int
+St_ncmp_m(const String *s, const void *m, size_t n) {
+	NCMP(s, m, n);
+}
+
+#if 0
+int
+St_ncmp_s(const String *s, const char *tz) {
+	size_t length = strlen(tz);
+
+	NCMP(s, tz, length);
+}
+
+static int
+my_memcasecmp(const void *p, const void *q, size_t n) {
+	const unsigned char *s = p, *t = q;
+	size_t i;
+
+	for (i = 0; i < n; ++i) {
+		if (tolower(s[i]) < tolower(t[i])) {
+			return -1;
+		}
+		if (tolower(s[i]) > tolower(t[i])) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+#define CASECMP(s, m, n)       \
+do {                           \
+	int tmp__;                 \
+	if (!(tmp__ = my_memcasecmp((s)->buf, m, (s)->length < (n) ? (s)->length : (n)))) { \
+		if ((s)->length < (n)) \
+			return -1;         \
+		if ((s)->length > (n)) \
+			return 1;          \
+	}                          \
+	return tmp__;              \
+} while (0)
+
+int
+St_casecmp(const String *s, const String *t) {
+	CASECMP(s, t->buf, t->length);
+}
+
+int
+St_casecmp_m(const String *s, const void *m, size_t n) {
+	CASECMP(s, m, n);
+}
+
+int
+St_casecmp_s(const String *s, const char *tz) {
+	size_t length = strlen(tz);
+
+	CASECMP(s, tz, length);
+}
+
+#define NCASECMP(s, m, n)                 \
+do {                                      \
+	return my_memcasecmp((s)->buf, m, n); \
+} while (0)
+
+int
+St_ncasecmp(const String *s, const String *t) {
+	NCASECMP(s, t->buf, t->length);
+}
+
+int
+St_ncasecmp_m(const String *s, const void *m, size_t n) {
+	NCASECMP(s, m, n);
+}
+
+int
+St_ncasecmp_s(const String *s, const char *tz) {
+	size_t length = strlen(tz);
+
+	NCASECMP(s, tz, length);
+}
+#endif
+
+#define STR(s, m, n)                                 \
+do {                                                 \
+	size_t i__;                                      \
+	if ((n) == 0)                                    \
+		return 0;                                    \
+	if ((s)->length < (n))                           \
+		return -1;                                   \
+	for (i__ = 0; i__ <= (s)->length - (n); ++i__) { \
+		if (!memcmp((s)->buf + i__, m, n))           \
+			return i__;                              \
+	}                                                \
+	return -1;                                       \
+} while (0)
+
+size_t
+St_str(const String *s, const String *t) {
+	STR(s, t->buf, t->length);
+}
+
+size_t
+St_str_m(const String *s, const void *m, size_t n) {
+	STR(s, m, n);
+}
+
+#if 0
+size_t
+St_str_s(const String *s, const char *tz) {
+	size_t length = strlen(tz);
+
+	STR(s, tz, length);
+}
+#endif
+
+#define RSTR(s, m, n)                               \
+do {                                                \
+	size_t i__;                                     \
+	if ((n) == 0)                                   \
+		return (s)->length;                         \
+	if ((s)->length < (n))                          \
+		return -1;                                  \
+	for (i__ = (s)->length - (n) + 1; i__; --i__) { \
+		if (!memcmp((s)->buf + i__ - 1, m, n))      \
+			return i__ - 1;                         \
+	}                                               \
+	return -1;                                      \
+} while (0)
+
+#if 0
+size_t
+St_rstr(const String *s, const String *t) {
+	RSTR(s, t->buf, t->length);
+}
+#endif
+
+size_t
+St_rstr_m(const String *s, const void *m, size_t n) {
+	RSTR(s, m, n);
+}
+
+size_t
+St_rstr_s(const String *s, const char *tz) {
+	size_t length = strlen(tz);
+
+	RSTR(s, tz, length);
+}
+
+size_t
+St_stro_m(const String *s, size_t off, const void *m, size_t n) {
+	size_t i;
+
+	assert(off <= s->length);
+
+	if (n == 0) {
+		return off;
+	}
+
+	if (n > s->length - off) {
+		return -1;
+	}
+
+	for (i = off; i < s->length - n; ++i) {
+		if (memcmp(s->buf + i, m, n) == 0) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+size_t
+St_rstro_m(const String *s, size_t off, const void *m, size_t n) {
+	size_t i;
+
+	assert(off <= s->length);
+
+	if (n == 0) {
+		return off;
+	}
+
+	if (n > s->length) {
+		return -1;
+	}
+
+	i = off;
+	if (i > s->length - n) {
+		i = s->length - n;
+	}
+
+	for (; i + 1u; --i) {
+		if (memcmp(s->buf + i, m, n) == 0) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+#define CPY(s, m, n)                                \
+do {                                                \
+	if ((s)->size - (s)->offset <= (n)) {           \
+		OFFSET_OFF(s);                              \
+		if ((s)->size <= (n)) {                     \
+			(s)->buf = xrealloc((s)->buf, (n) + 1); \
+			(s)->size = (n) + 1;                    \
+		}                                           \
+	}                                               \
+	memcpy((s)->buf, m, (s)->length = (n));         \
+	(s)->buf[n] = '\0';                             \
+} while (0)
+
+void
+St_cpy(String *s, const String *t) {
+	CPY(s, t->buf, t->length);
+}
+
+void
+St_cpy_m(String *s, const void *m, size_t n) {
+	CPY(s, m, n);
+}
+
+void
+St_cpy_s(String *s, const char *tz) {
+	size_t length = strlen(tz);
+
+	CPY(s, tz, length);
+}
+
+void
+St_cpy_c(String *s, int c) {
+	unsigned char tmp = c;
+	CPY(s, &tmp, 1);
+}
+
+static void cat(String *s, const void *m, size_t n) {
+	if (s->size - s->offset <= s->length + n) {
+		OFFSET_OFF(s);
+		if (s->size <= s->length + n) {
+			do {
+				s->size = s->size / 2 * 3 + 1;
+			} while (s->size <= s->length + n);
+			s->buf = xrealloc(s->buf, s->size);
+		}
+	}
+	memcpy(s->buf + s->length, m, n);
+	s->buf[s->length += n] = '\0';
+}
+
+void
+St_cat(String *s, const String *t) {
+	cat(s, t->buf, t->length);
+}
+
+void
+St_cat_m(String *s, const void *m, size_t n) {
+	cat(s, m, n);
+}
+
+void
+St_cat_s(String *s, const char *tz) {
+	cat(s, tz, strlen(tz));
+}
+
+void
+St_cat_c(String *s, int c) {
+	unsigned char tmp = c;
+	cat(s, &tmp, 1);
+}
+
+#define TAC(s, m, n)                                                      \
+do {                                                                      \
+	if ((s)->offset >= (n)) {                                             \
+		(s)->buf -= (n);                                                  \
+		(s)->offset -= (n);                                               \
+	} else if ((s)->size <= (s)->length + (n)) {                          \
+		(s)->buf -= (s)->offset;                                          \
+		(s)->buf = xrealloc((s)->buf, (s)->length + (n) + 1);             \
+		(s)->size = (s)->length + (n) + 1;                                \
+		memmove((s)->buf + (n), (s)->buf + (s)->offset, (s)->length + 1); \
+		(s)->offset = 0;                                                  \
+	} else {                                                              \
+		memmove((s)->buf + (n), (s)->buf, (s)->length + 1);               \
+	}                                                                     \
+	memcpy((s)->buf, m, n);                                               \
+	(s)->length += (n);                                                   \
+} while (0)
+
+#if 0
+void
+St_tac(String *s, const String *t) {
+	TAC(s, t->buf, t->length);
+}
+#endif
+
+void
+St_tac_m(String *s, const void *m, size_t n) {
+	TAC(s, m, n);
+}
+
+void
+St_tac_s(String *s, const char *tz) {
+	size_t length = strlen(tz);
+
+	TAC(s, tz, length);
+}
+
+void
+St_tac_c(String *s, int c) {
+	unsigned char tmp = c;
+	TAC(s, &tmp, 1);
+}
+
+void
+St_reverse(String *s) {
+	size_t i;
+	char tmp;
+
+	for (i = 0; i < s->length / 2; ++i) {
+		tmp = s->buf[i];
+		s->buf[i] = s->buf[s->length - i - 1];
+		s->buf[s->length - i - 1] = tmp;
+	}
+}
+
+#if 0
+void
+St_map(String *s, int (*better)(int)) {
+	size_t i;
+	int tmp;
+
+	for (i = 0; i < s->length; ++i) {
+		tmp = better((unsigned char)s->buf[i]);
+		if (tmp == EOF) {
+			s->buf[s->length = i] = '\0';
+			return;
+		}
+		s->buf[i] = tmp;
+	}
+
+	while ((tmp = better(EOF)) != EOF) {
+		if (s->size <= s->length + 1) {
+			OFFSET_OFF(s);
+			XREALLOC(s->buf, s->size * 2);
+			s->size *= 2;
+		}
+		s->buf[s->length++] = tmp;
+		s->buf[s->length] = '\0';
+	}
+}
+#endif
+
+void
+St_grep(String *s, int (*good)(int)) {
+	size_t r, w;
+
+	for (r = w = 0; r < s->length; ++r) {
+		if (good((unsigned char)s->buf[r])) {
+			s->buf[w++] = s->buf[r];
+		}
+	}
+	s->buf[s->length = w] = '\0';
+}
+
+void
+St_upper(String *s) {
+	size_t i;
+
+	for (i = 0; i < s->length; ++i) {
+		s->buf[i] = toupper((unsigned char)s->buf[i]);
+	}
+}
+
+void
+St_lower(String *s) {
+	size_t i;
+
+	for (i = 0; i < s->length; ++i) {
+		s->buf[i] = tolower((unsigned char)s->buf[i]);
+	}
+}
+
+void
+St_del(String *s, size_t p, size_t n) {
+	if (n == 0 || s->length < p)
+		return;
+	if (s->length < n || s->length < p + n) {
+		n = s->length - p;
+	}
+	if (p + n == s->length) {
+		s->buf[p] = '\0';
+	} else if (p == 0) {
+		s->offset += n;
+		s->buf += n;
+	} else {
+		memmove(s->buf + p, s->buf + n + p, s->length - p - n);
+	}
+	s->length -= n;
+}
+
+#if 0
+#define INS(s, p, m, n)                                                       \
+do {                                                                          \
+	if ((n) == 0 || (s)->length < (p))                                        \
+		return;                                                               \
+	if ((n) <= (s)->offset) {                                                 \
+		(s)->offset -= (n);                                                   \
+		(s)->buf -= (n);                                                      \
+		memmove((s)->buf, (s)->buf + (n), p);                                 \
+	} else {                                                                  \
+		if ((s)->size - (s)->offset <= (s)->length + (n)) {                   \
+			OFFSET_OFF(s);                                                    \
+			XREALLOC((s)->buf, (s)->length + (n) + 1);                        \
+			(s)->size = (s)->length + (n) + 1;                                \
+		}                                                                     \
+		memmove((s)->buf + (p) + (n), (s)->buf + (p), (s)->length - (p) + 1); \
+	}                                                                         \
+	memcpy((s)->buf + (p), m, n);                                             \
+	(s)->length += (n);                                                       \
+} while (0)
+
+void
+St_ins(String *s, size_t p, const String *t) {
+	INS(s, p, t->buf, t->length);
+}
+
+void
+St_ins_m(String *s, size_t p, const void *m, size_t n) {
+	INS(s, p, m, n);
+}
+
+void
+St_ins_s(String *s, size_t p, const char *tz) {
+	size_t length = strlen(tz);
+
+	INS(s, p, tz, length);
+}
+
+void
+St_ins_c(String *s, size_t p, int c) {
+	unsigned char tmp = c;
+	INS(s, p, &tmp, 1);
+}
+#endif
+
+void
+St_substr(String *l, String *s, size_t p, size_t n, const String *r) {
+	if (l) {
+		if (p >= s->length) {
+			St_zero(l);
+		} else {
+			size_t length = n;
+
+			if (p + length > s->length) {
+				length = s->length - p;
+			}
+			St_cpy_m(l, s->buf + p, length);
+		}
+	}
+
+	if (r) {
+		size_t gap = 0;
+
+		if (p + n > s->length) {
+			if (p > s->length) {
+				n = 0;
+				gap = p - s->length;
+			} else {
+				n = s->length - p;
+			}
+		}
+		St_grow(s, s->length + r->length - n + gap);
+		if (gap) {
+			memset(s->buf + s->length, '\0', gap);
+		} else if (r->length != n && s->length - p - n) {
+			memmove(s->buf + p + r->length, s->buf + p + n, s->length - p - n);
+		}
+		memcpy(s->buf + p, r->buf, r->length);
+		s->buf[s->length += r->length - n + gap] = '\0';
+	}
+}
+
+#if HAVE_VSNPRINTF_P
+size_t
+St_xprintf(String *s, const char *fmt, ...) {
+	va_list ap;
+	int tmp;
+
+	va_start(ap, fmt);
+	tmp = vsnprintf(s->buf, s->size, fmt, ap);
+	va_end(ap);
+
+	if (tmp < 0) {
+		do {
+			St_grow(s, s->size * 2);
+
+			va_start(ap, fmt);
+			tmp = vsnprintf(s->buf, s->size, fmt, ap);
+			va_end(ap);
+		} while (tmp < 0);
+	} else if (tmp + 1u >= s->size) {
+		St_grow(s, tmp);
+		s->length = 0;
+
+		va_start(ap, fmt);
+		tmp = vsnprintf(s->buf, s->size, fmt, ap);
+		va_end(ap);
+
+		if (tmp < 0) {
+			s->buf[0] = '\0';
+			return -1;
+		}
+	}
+
+	return s->length = tmp;
+}
+#endif
+
+void
+St_num(String *s, double d) {
+	#if HAVE_VSNPRINTF_P
+		St_xprintf(s, "%.*g", DBL_DIG, d);
+	#else
+		St_grow(s, 255);
+		s->length = sprintf(s->buf, "%.*g", DBL_DIG, d);
+	#endif
+}
+
+#if 0
+size_t
+St_write(const String *s, FILE *fp) {
+	return ST_WRITE(s, fp);
+}
+#endif
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+size_t
+St_read(String *s, FILE *fp, size_t n) {
+	char buf[4096];
+	size_t red;
+
+	St_zero(s);
+	while (n && !feof(fp) && (red = fread(buf, sizeof *buf, MIN(n, sizeof buf / sizeof *buf), fp))) {
+		St_cat_m(s, buf, red);
+		n -= red;
+	}
+	return s->length;
+}
+
+#if 0
+int
+St_getline(String *s, FILE *fp, int n) {
+	int c;
+
+	OFFSET_OFF(s);
+	for (s->length = 0; (c = getc(fp)) != EOF; ++s->length) {
+		if (s->size <= s->length + 1) {
+			XREALLOC(s->buf, s->size * 2);
+			s->size *= 2;
+		}
+		s->buf[s->length] = c;
+		if (c == n)
+			break;
+	}
+	s->buf[s->length] = '\0';
+
+	return s->length != 0;
+}
+
+void
+St_getfile(String *s, FILE *fp) {
+	size_t tmp;
+
+	OFFSET_OFF(s);
+	if (s->size <= BUFSIZ) {
+		XREALLOC(s->buf, BUFSIZ + 1);
+		s->size = BUFSIZ + 1;
+	}
+
+	for (s->length = 0; (tmp = fread(s->buf + s->length, 1, BUFSIZ, fp)); ) {
+		s->length += tmp;
+		if (s->size <= s->length + BUFSIZ + 1) {
+			XREALLOC(s->buf, s->size * 2);
+			s->size *= 2;
+		}
+	}
+	s->buf[s->length] = '\0';
+}
+#endif
+
+size_t
+St_hash(const String *s, size_t h) {
+	return u_hash(St_ptr(s), St_len(s), h);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/Str.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+Str.o: Str.c config.h Str.h strutil.h xmalloc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/Str.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,144 @@
+#ifndef STR_H_
+#define STR_H_
+
+#include "config.h"
+
+#include <stdio.h>
+
+typedef struct {
+	char *buf;
+	size_t length;
+	size_t size;
+	size_t offset;
+} String;
+
+void    St_init         (String *);
+void    St_fake         (String *, char *, size_t);
+void    St_clear        (String *);
+#if 0
+void    St_zero         (String *);
+ATTR_PURE
+char    *St_ptr         (const String *);
+ATTR_PURE
+size_t  St_len          (const String *);
+void    St_grow         (String *, size_t);
+#endif
+void    St_trunc        (String *, size_t);
+#if 0
+ATTR_PURE
+int     St_lastchar     (const String *);
+ATTR_PURE
+int     St_firstchar    (const String *);
+int     St_chop         (String *);
+#endif
+int     St_shift        (String *);
+size_t  St_shiftws      (String *);
+#if 0
+ATTR_PURE
+int     St_index        (const String *, size_t);
+#endif
+ATTR_PURE
+size_t  St_chr          (const String *, int);
+#if 0
+size_t  St_rchr         (const String *, int);
+#endif
+ATTR_PURE
+int     St_cmp          (const String *, const String *);
+ATTR_PURE
+int     St_cmp_m        (const String *, const void *, size_t);
+#if 0
+ATTR_PURE
+int     St_cmp_s        (const String *, const char *);
+ATTR_PURE
+int     St_ncmp         (const String *, const String *);
+#endif
+ATTR_PURE
+int     St_ncmp_m       (const String *, const void *, size_t);
+#if 0
+ATTR_PURE
+int     St_ncmp_s       (const String *, const char *);
+ATTR_PURE
+int     St_casecmp      (const String *, const String *);
+ATTR_PURE
+int     St_casecmp_m    (const String *, const void *, size_t);
+ATTR_PURE
+int     St_casecmp_s    (const String *, const char *);
+ATTR_PURE
+int     St_ncasecmp     (const String *, const String *);
+ATTR_PURE
+int     St_ncasecmp_m   (const String *, const void *, size_t);
+ATTR_PURE
+int     St_ncasecmp_s   (const String *, const char *);
+#endif
+ATTR_PURE
+size_t  St_str          (const String *, const String *);
+ATTR_PURE
+size_t  St_str_m        (const String *, const void *, size_t);
+#if 0
+ATTR_PURE
+size_t  St_str_s        (const String *, const char *);
+ATTR_PURE
+size_t  St_rstr         (const String *, const String *);
+#endif
+ATTR_PURE
+size_t  St_rstr_m       (const String *, const void *, size_t);
+ATTR_PURE
+size_t  St_rstr_s       (const String *, const char *);
+ATTR_PURE
+size_t  St_stro_m       (const String *, size_t, const void *, size_t);
+ATTR_PURE
+size_t  St_rstro_m      (const String *, size_t, const void *, size_t);
+void    St_cpy          (String *, const String *);
+void    St_cpy_m        (String *, const void *, size_t);
+void    St_cpy_s        (String *, const char *);
+void    St_cpy_c        (String *, int);
+void    St_cat          (String *, const String *);
+void    St_cat_m        (String *, const void *, size_t);
+void    St_cat_s        (String *, const char *);
+void    St_cat_c        (String *, int);
+#if 0
+void    St_tac          (String *, const String *);
+#endif
+void    St_tac_m        (String *, const void *, size_t);
+void    St_tac_s        (String *, const char *);
+void    St_tac_c        (String *, int);
+void    St_reverse      (String *);
+#if 0
+void    St_map          (String *, int (*)(int));
+#endif
+void    St_grep         (String *, int (*)(int));
+void    St_upper        (String *);
+void    St_lower        (String *);
+void    St_del          (String *, size_t, size_t);
+#if 0
+void    St_ins          (String *, size_t, const String *);
+void    St_ins_m        (String *, size_t, const void *, size_t);
+void    St_ins_s        (String *, size_t, const char *);
+void    St_ins_c        (String *, size_t, int);
+#endif
+void    St_substr       (String *, String *, size_t, size_t, const String *);
+#if HAVE_VSNPRINTF_P
+ATTR_SPRINTF
+size_t  St_xprintf      (String *, const char *, ...);
+#endif
+void    St_num          (String *, double);
+#if 0
+size_t  St_write        (const String *, FILE *);
+#endif
+size_t  St_read         (String *, FILE *, size_t);
+#if 0
+int     St_getline      (String *, FILE *, int);
+void    St_getfile      (String *, FILE *);
+#endif
+ATTR_PURE
+size_t  St_hash         (const String *, size_t);
+
+#define St_ptr(s)          (0 + (s)->buf)
+#define St_len(s)          (0 + (s)->length)
+#define St_zero(s)         ((void)((s)->buf[(s)->length = 0] = '\0'))
+#define ST_LASTCHAR(s)     ((s)->length ? (int)(unsigned char)(s)->buf[(s)->length - 1] : EOF)
+#define ST_FIRSTCHAR(s)    ((s)->length ? (int)(unsigned char)(s)->buf[0] : EOF)
+#define ST_INDEX(s, n)     ((size_t)(n) < (s)->length ? (int)(unsigned char)(s)->buf[(size_t)(n)] : EOF)
+#define ST_WRITE(s, fp)    fwrite((s)->buf, 1, (s)->length, fp)
+
+#endif /* STR_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/TODO	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,29 @@
+fix write/seek semantics for Z buffers
+
+add sort function
+
+add new value type: hashes
+@TYPE OF hash : "hash"
+- create:
+@TAN list; #<key1 value1 key2 value2#>
+@SIN list; #<key1 key2#>
+@COS list; #<value1 value2#>
+- decompose:
+@ATAN hash; -> #<key2 value2 key1 value1#>
+@ASIN hash; -> #<key2 key1#>
+@ACOS hash; -> #<value2 value1#>
+- push entries:
+hash + hashmod
+- pop entries:
+hash - hashmod
+- replace (-+) entries:
+hash _ hashmod
+- index:
+hash . key
+- slice:
+hash . #<key1 key2#>
+- remove:
+hash % key
+hash % #<key1 key2#>
+- get depth:
+hash / key
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/VERSION	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+0.6.5.1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/atechit.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,27 @@
+#include "zz.h"
+#include "atechit.h"
+#include "main.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+enum {MAGIC = 23};
+static size_t Aused;
+static void (*Afunc[MAGIC])(void);
+
+static void bah(void) {
+	for (; Aused; Afunc[--Aused]())
+		;
+}
+
+void atechit(void (*f)(void)) {
+	if (Aused >= sizeof Afunc / sizeof *Afunc) {
+		NOTREACHED;
+	}
+	Afunc[Aused++] = f;
+	if (Aused == 1 && atexit(bah)) {
+		fprintf(stderr, "%s: atexit(): error message\n", Prog);
+		bah();
+		exit(EXIT_FAILURE);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/atechit.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+atechit.o: atechit.c zz.h config.h atechit.h main.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/atechit.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,6 @@
+#ifndef ATECHIT_H_
+#define ATECHIT_H_
+
+void atechit(void (*)(void));
+
+#endif /* ATECHIT_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/compile.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,171 @@
+#include "config.h"
+#include "Str.h"
+#include "compile.h"
+#include "expr.h"
+#include "main_var.h"
+#include "op.h"
+#include "text.h"
+#include "zz.h"
+
+#include <assert.h>
+
+static struct op *end_if(struct text *code, size_t *n) {
+	for (; *n < code->length; ++*n) {
+		struct op *const p = code->start[*n];
+		switch (p->type) {
+			case OP_IF:
+				++*n;
+				p->arh.op = code->start[*n];
+				p->next = end_if(code, n);
+				break;
+
+			case OP_ELSE: {
+				size_t tmp;
+				tmp = ++*n;
+				p->next = end_if(code, n);
+				p->type = OP_NOP;
+				if (tmp < code->length) {
+					return code->start[tmp];
+				}
+				return p;
+			}
+
+			case OP_FI:
+				p->type = OP_NOP;
+				return p;
+
+			default:
+				break;
+		}
+	}
+
+	return code->start[0];
+}
+
+static void resolve(struct expr *e) {
+	if (!e) {
+		return;
+	}
+
+	switch (e->type) {
+		case literE:
+			break;
+
+		case varE:
+			e->v.val = vr_data(Var_plain, e->v.tent);
+			assert(e->v.val != NULL);
+			break;
+
+		case varhashE:
+			e->v.hash = vr_data(Var_hash, e->v.tent);
+			resolve(e->right);
+			break;
+
+		case symbolE:
+			if (e->op == S_ARGV) {
+				resolve(e->right);
+			}
+			break;
+
+		case unopE:
+			resolve(e->right);
+			break;
+
+		case binopE:
+			resolve(e->left.expr);
+			resolve(e->right);
+			break;
+
+		case listE:
+			resolve(e->right);
+			resolve(e->left.expr);
+			break;
+	}
+}
+
+static void op_resolve(struct op *o) {
+	switch (o->type) {
+		case OP_NOP:
+		case OP_GOBACK:
+		case OP_GOTO:
+		case OP_HANG:
+			break;
+
+		case OP_ASSIGN:
+		case OP_CALL_BACK:
+		case OP_MODIFY:
+		case OP_PRINT:
+		case OP_PUTC:
+		case OP_TEMP:
+			resolve(o->arh.expr);
+			resolve(o->arg);
+			break;
+
+		case OP_CALL:
+		case OP_CALL_DYN:
+		case OP_CLOSE:
+		case OP_EXIT:
+		case OP_IF:
+		case OP_RETURN:
+		case OP_SYSTEM:
+		case OP_THROW:
+			resolve(o->arg);
+			break;
+
+		default:
+			NOTREACHED;
+			break;
+	}
+}
+
+void compile(struct text *code) {
+	size_t i;
+
+	for (i = 0; i < code->length; ++i) {
+		struct op *const p = code->start[i];
+
+		op_getop(p);
+
+		if (!p->next && p->type != OP_EXIT && i + 1 < code->length) {
+			p->next = code->start[i + 1];
+		}
+	}
+
+	if (!i) {
+		struct op tmp;
+		op_init(&tmp);
+		text_push(code, &tmp);
+	}
+
+	vr_freeze(Var_plain);
+	vr_freeze(Var_hash);
+
+	for (i = 0; i < code->length; ++i) {
+		struct op *const p = code->start[i];
+
+		switch (p->type) {
+			case OP_IF:
+				++i;
+				p->arh.op = code->start[i];
+				p->next = end_if(code, &i);
+				break;
+
+			case OP_ELSE:
+				++i;
+				p->next = end_if(code, &i);
+				p->type = OP_NOP;
+				break;
+
+			case OP_FI:
+				p->type = OP_NOP;
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	for (i = 0; i < code->length; ++i) {
+		op_resolve(code->start[i]);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/compile.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+compile.o: compile.c config.h Str.h compile.h text.h op.h IO.h expr.h \
+  re.h stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h \
+  main_var.h zz.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/compile.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,8 @@
+#ifndef COMPILE_H_
+#define COMPILE_H_
+
+#include "text.h"
+
+void compile(struct text *);
+
+#endif /* COMPILE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/config.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,114 @@
+#ifndef CONFIG_H_
+#define CONFIG_H_
+
+#ifdef DEBUGGING
+	#define DEBUG(x) x
+	#define DEBUG_P 1
+#else
+	#define DEBUG(x)
+	#define DEBUG_P 0
+	#define NDEBUG 1
+#endif
+
+#if defined _WIN32 || defined __WIN32__ || defined WIN32
+	#define DIR_END "\\/"
+#elif defined __unix__ || defined __linux__ || defined __unix || defined unix
+	#define DIR_END "/"
+#else
+	/* XXX */
+	#define DIR_END 0
+#endif
+
+#if defined __POSIX__ || defined __unix__ || defined __linux__ || defined __unix || defined unix
+	#define HAVE_SLEEP_P 1
+	#define SLEEP_HEADER <unistd.h>
+	#define DO_SLEEP pause()
+#elif defined _WIN32 || defined __WIN32__ || defined WIN32
+	#define HAVE_SLEEP_P 1
+	#define SLEEP_HEADER <windows.h>
+	#define DO_SLEEP Sleep(INFINITE)
+#else
+	#define HAVE_SLEEP_P 0
+	#define DO_SLEEP ((void)0)
+#endif
+
+#if defined __linux__ || defined __sun__ || defined __sun
+	#define HAVE_DEV_URANDOM_P 1
+#else
+	#define HAVE_DEV_URANDOM_P 0
+#endif
+
+#ifndef INC_PREFIX
+	#if defined __unix__ || defined __linux__ || defined __unix || defined unix
+		#define INC_PREFIX "/usr/local/lib/ploki/"
+	#else
+		#define INC_PREFIX 0
+	#endif
+#endif
+
+#define INC_PREFIX_LIST \
+{                       \
+	"",                 \
+	INC_PREFIX,         \
+	0                   \
+}
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ + 0 >= 199901L
+	#define HAVE_C99_P 1
+	#define HAVE_C99(x) x
+#else
+	#define HAVE_C99_P 0
+	#define HAVE_C99(x)
+#endif
+
+#ifdef __GNUC__
+	#define HAVE_GCC_P 1
+	#define HAVE_GCC(x) x
+#else
+	#define HAVE_GCC_P 0
+	#define HAVE_GCC(x)
+#endif
+
+#if HAVE_GCC_P
+	#if __GNUC__ == 2 && __GNUC_MINOR__ >= 7 || __GNUC__ > 2
+		#define ATTR(x) __attribute__(x)
+	#else
+		#define ATTR(x)
+	#endif
+
+	#if __GNUC__ == 2 && __GNUC_MINOR__ >= 96 || __GNUC__ > 2
+		#define ATTR_MALLOC __attribute__((__malloc__))
+		#define ATTR_PURE   __attribute__((__pure__))
+	#else
+		#define ATTR_MALLOC
+		#define ATTR_PURE
+	#endif
+#else
+	#define ATTR(x)
+	#define ATTR_MALLOC
+	#define ATTR_PURE
+#endif
+
+#define ATTR_UNUSED   ATTR((__unused__))
+#define ATTR_NORETURN ATTR((__noreturn__))
+#define ATTR_SPRINTF  ATTR((__format__(__printf__, 2, 3)))
+#define ATTR_CONST    ATTR((__const__))
+
+#if HAVE_C99_P
+	#define HAVE_VSNPRINTF(x) x
+	#define HAVE_VSNPRINTF_P 1
+#elif defined __GLIBC__
+	#include <features.h>
+	#if defined __USE_BSD || defined __USE_ISOC99 || defined __USE_UNIX98
+		#define HAVE_VSNPRINTF(x) x
+		#define HAVE_VSNPRINTF_P 1
+	#else
+		#define HAVE_VSNPRINTF(x)
+		#define HAVE_VSNPRINTF_P 0
+	#endif
+#else
+	#define HAVE_VSNPRINTF(x)
+	#define HAVE_VSNPRINTF_P 0
+#endif
+
+#endif /* CONFIG_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/deparse.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,688 @@
+#include "config.h"
+#include "Str.h"
+#include "deparse.h"
+#include "expr.h"
+#include "main_io.h"
+#include "main_label.h"
+#include "text.h"
+#include "val.h"
+#include "venus.h"
+#include "xmalloc.h"
+#include "zz.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <assert.h>
+
+#if HAVE_VSNPRINTF_P
+#define MAKE_LABEL(s, c)                \
+do {                                    \
+	static unsigned long seq__;         \
+	St_init(s);                         \
+	St_xprintf(s, "%c%lu", c, seq__++); \
+} while (0)
+#else
+#define MAKE_LABEL(s, c)        \
+do {                            \
+	static unsigned long seq__; \
+	St_init(s);                 \
+	St_num(s, seq__++);         \
+	St_tac_c(s, c);             \
+} while (0)
+#endif
+
+ATTR_CONST
+static int display(enum t_binop b) {
+	switch (b) {
+		case B_SPARK_SPOT:      return '!';
+		case B_DOUBLE_OH_SEVEN: return '%';
+		case B_AMPERSAND:       return '&';
+		case B_SPARK:           return '\'';
+		case B_SPLAT:           return '*';
+		case B_INTERSECTION:    return '+';
+		case B_TAIL:            return ',';
+		case B_WORM:            return '-';
+		case B_SPOT:            return '.';
+		case B_SLAT:            return '/';
+		case B_TWO_SPOT:        return ':';
+		case B_HYBRID:          return ';';
+		case B_ANGLE:           return '<';
+		case B_HALF_MESH:       return '=';
+		case B_RIGHT_ANGLE:     return '>';
+		case B_U_TURN:          return '[';
+		case B_U_TURN_BACK:     return ']';
+		case B_SHARK_FIN:       return '^';
+		case B_FLATWORM:        return '_';
+		case B_BACKSPARK:       return '`';
+		case B_EMBRACE:         return '{';
+		case B_SPIKE:           return '|';
+		case B_BRACELET:        return '}';
+		case B_SQIGGLE:         return '~';
+		default: break;
+	}
+	NOTREACHED;
+}
+
+static void dump_str(const String *s) {
+	size_t i;
+
+	io_write_m(Out, "\"", 1);
+	for (i = 0; i < St_len(s); ++i) {
+		const unsigned char c = ST_INDEX(s, i);
+		if (c != '\\' && c != '"' && isprint(c)) {
+			io_write_m(Out, &c, 1);
+		} else {
+			io_write_m(Out, "\\", 1);
+			switch (c) {
+				case '"':
+				case '\\': io_write_m(Out, &c , 1); break;
+				case '\a': io_write_m(Out, "a", 1); break;
+				case '\b': io_write_m(Out, "b", 1); break;
+				case '\f': io_write_m(Out, "f", 1); break;
+				case '\n': io_write_m(Out, "n", 1); break;
+				case '\r': io_write_m(Out, "r", 1); break;
+				case '\t': io_write_m(Out, "t", 1); break;
+				case '\v': io_write_m(Out, "v", 1); break;
+				default:   fprintf(io_fp(Out), "%03o", c); break;
+			}
+		}
+	}
+	io_write_m(Out, "\"", 1);
+}
+
+static void dump_ko(const struct kork *k) {
+	String tmp;
+	St_fake(&tmp, (char *)ko_ptr(k), ko_length(k));
+	dump_str(&tmp);
+}
+
+static void to_id(struct kork *k, size_t n) {
+	static char id[] =
+		"abcdefghijklmnopqrstuvwxyz"
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+		"$"
+		;
+	const size_t base = sizeof id - 1;
+	do {
+		ko_cat_c(k, id[n % base]);
+		n /= base;
+	} while (n);
+}
+
+static void make_var(struct val *v) {
+	static size_t seq;
+
+	ko_decouple(v->ko);
+	to_id(v->ko, seq++);
+	v->type = V_STR_K;
+}
+
+static void dump_expr(const struct expr *e, int inlist) {
+	assert(e != NULL);
+
+	switch (e->type) {
+		case literE:
+			if (V_EXT_P(e->v.val)) {
+				fprintf(io_fp(Out), "?%s?", io_name(e->v.val->magic.ext, NULL));
+			} else if (V_SUB_P(e->v.val)) {
+				fprintf(io_fp(Out), "?CODE(%p)?", (void *)e->v.val->magic.sub);
+			} else if (V_STR_P(e->v.val)) {
+				dump_ko(e->v.val->ko);
+			} else if (V_NUM_P(e->v.val)) {
+				if (e->v.val->num < 0.0) {
+					fprintf(io_fp(Out), "@NEG %g", -e->v.val->num);
+				} else {
+					fprintf(io_fp(Out), "%g", e->v.val->num);
+				}
+			} else {
+				#if 0
+				io_write_m(Out, "()", 2);
+				#endif
+			}
+			break;
+
+		case varE:
+			if (!V_STR_P(e->v.val)) {
+				assert(e->v.val->type == V_UNDEF);
+				make_var(e->v.val);
+			}
+			io_write_m(Out, ko_ptr(e->v.val->ko), ko_length(e->v.val->ko));
+			break;
+
+		case varhashE: {
+			struct val *tmp;
+			if (!(tmp = sh_get(e->v.hash, "name", 4))) {
+				tmp = v_undef();
+				make_var(tmp);
+				sh_put(e->v.hash, "name", 4, tmp);
+			}
+			io_write_m(Out, ko_ptr(tmp->ko), ko_length(tmp->ko));
+			io_write_m(Out, "(", 1);
+			dump_expr(e->right, 0);
+			io_write_m(Out, ")", 1);
+			break;
+		}
+
+		case symbolE:
+			io_write_m(Out, "\\", 1);
+			switch (e->op) {
+				case S_NUL:                                break;
+				case S_ARG:    io_write_m(Out, "@", 1);    break;
+				case S_ARGC:   io_write_m(Out, "ARG", 3);  break;
+
+				case S_ARGV:
+					io_write_m(Out, "ARG:", 4);
+					if (e->right->type == binopE) {
+						io_write_m(Out, "(", 1);
+						dump_expr(e->right, 0);
+						io_write_m(Out, ")", 1);
+					} else {
+						dump_expr(e->right, 0);
+					}
+					break;
+
+				case S_ERR:    io_write_m(Out, "!", 1);    break;
+				case S_EULER:  io_write_m(Out, "E", 1);    break;
+				case S_LUDOLF: io_write_m(Out, "PI", 2);   break;
+				case S_MATCH:
+					fprintf(io_fp(Out), "%lu", (unsigned long)e->left.bonus);
+					break;
+				case S_RAND:   io_write_m(Out, "?", 1);    break;
+				case S_RESULT: io_write_m(Out, "_", 1);    break;
+				case S_STDIN:  io_write_m(Out, "EING", 4); break;
+				case S_STDOUT: io_write_m(Out, "AUSG", 4); break;
+				case S_STDERR: io_write_m(Out, "FEHL", 4); break;
+
+				default:       NOTREACHED;                 break;
+			}
+			break;
+
+		case unopE:
+			switch (e->op) {
+				case F_EXP:    io_write_m(Out, "\\E^", 3); break;
+				case F_LOWER:  io_write_m(Out, "\\L" , 2); break;
+				case F_QUOTE:  io_write_m(Out, "\\Q" , 2); break;
+				case F_RE_ESC: io_write_m(Out, "\\R" , 2); break;
+				case F_UPPER:  io_write_m(Out, "\\U" , 2); break;
+				case F_MATCH: {
+					String tmp;
+					io_write_m(Out, "(", 1);
+					dump_expr(e->right, 0);
+					io_write_m(Out, " ~ ", 3);
+					St_init(&tmp);
+					re_decompile(e->left.rx, &tmp);
+					dump_str(&tmp);
+					St_clear(&tmp);
+					io_write_m(Out, ")", 1);
+					return;
+				}
+
+				default:
+					io_write_m(Out, "@", 1);
+					switch (e->op) {
+						case F_CALL:    io_write(Out, &e->left.op->txt);
+						case F_NUL:     break;
+						case F_ABS:     io_write_m(Out, "ABS", 3);     break;
+						case F_ACOS:    io_write_m(Out, "ACOS", 4);    break;
+						case F_ASIN:    io_write_m(Out, "ASIN", 4);    break;
+						case F_ATAN:    io_write_m(Out, "ATAN", 4);    break;
+						case F_ATAN2:   io_write_m(Out, "ATAN2", 5);   break;
+						case F_CATCH:   io_write_m(Out, "EVAL", 4);    break;
+						case F_CHR:     io_write_m(Out, "CHR", 3);     break;
+						case F_COS:     io_write_m(Out, "COS", 3);     break;
+						case F_DEFINED: io_write_m(Out, "DEF-P", 5);   break;
+						case F_EOF:     io_write_m(Out, "EDD-P", 5);   break;
+						case F_ERROR:   io_write_m(Out, "ERR-P", 5);   break;
+						case F_FREEZE:  io_write_m(Out, "OMFG", 4);    break;
+						case F_GETC:    io_write_m(Out, "GET", 3);     break;
+						case F_GETENV:  io_write_m(Out, "ENV", 3);     break;
+						case F_GETS:    io_write_m(Out, "LEGS", 4);    break;
+						case F_HANG: {
+							size_t i;
+							String tmp;
+
+							St_init(&tmp);
+							for (St_num(&tmp, i = 1);
+									ve_findnext(&Venus, &tmp, 0);
+									St_num(&tmp, ++i))
+								;
+							fprintf(io_fp(Out), "(%s)", St_ptr(&tmp));
+							St_clear(&tmp);
+							break;
+						}
+						case F_INT:     io_write_m(Out, "INT", 3);     break;
+						case F_IO:      io_write_m(Out, "IO-P", 4);    break;
+						case F_LENGTH:  io_write_m(Out, "LENGTH", 6);  break;
+						case F_LOG10:   io_write_m(Out, "LG", 2);      break;
+						case F_LOG:     io_write_m(Out, "LN", 2);      break;
+						case F_MOEND:   io_write_m(Out, "+", 1);       break;
+						case F_MOSTART: io_write_m(Out, "-", 1);       break;
+						case F_NEG:     io_write_m(Out, "NEG", 3);     break;
+						case F_NOT:     io_write_m(Out, "NOT", 3);     break;
+						case F_NUM:     io_write_m(Out, "NUM", 3);     break;
+						case F_OPEN:    io_write_m(Out, "APERS", 5);   break;
+						case F_OPENR:   io_write_m(Out, "LAPERS", 6);  break;
+						case F_OPENW:   io_write_m(Out, "SAPERS", 6);  break;
+						case F_ORD:     io_write_m(Out, "ORD", 3);     break;
+						case F_REMOVE:  io_write_m(Out, "REMOVE", 6);  break;
+						case F_RENAME:  io_write_m(Out, "RENAEM", 6);  break;
+						case F_REVERSE: io_write_m(Out, "REVERSE", 7); break;
+						case F_SEEK:    io_write_m(Out, "SUCH", 4);    break;
+						case F_SIN:     io_write_m(Out, "SIN", 3);     break;
+						case F_SQRT:    io_write_m(Out, "SPQR", 4);    break;
+						case F_STR:     io_write_m(Out, "STR", 3);     break;
+						case F_TAN:     io_write_m(Out, "TAN", 3);     break;
+						case F_TELL:    io_write_m(Out, "SAG", 3);     break;
+						case F_TYPEOF:  io_write_m(Out, "TYPE OF", 7); break;
+						default:
+							NOTREACHED;
+							break;
+					}
+					break;
+			}
+			if (e->right->type != unopE || e->op == F_NUL) {
+				io_write_m(Out, "(", 1);
+				dump_expr(e->right, 0);
+				io_write_m(Out, ")", 1);
+			} else {
+				io_write_m(Out, " ", 1);
+				dump_expr(e->right, 0);
+			}
+			break;
+
+		case binopE:
+			dump_expr(e->left.expr, 0);
+
+			if (e->op == B_XMATCH) {
+				io_write_m(Out, " ?o~ ", 5);
+			} else {
+				fprintf(io_fp(Out), "%s%c ", &" "[e->op == B_TAIL], display(e->op));
+			}
+
+			if (e->right->type == binopE) {
+				io_write_m(Out, "(", 1);
+				dump_expr(e->right, 0);
+				io_write_m(Out, ")", 1);
+			} else {
+				dump_expr(e->right, 0);
+			}
+			break;
+
+		case listE:
+			if (!inlist) {
+				io_write_m(Out, "#<", 2);
+			}
+			if (e->right) {
+				io_write_m(Out, "(", 1);
+				dump_expr(e->right, 0);
+				io_write_m(Out, ")", 1);
+				if (e->left.expr) {
+					io_write_m(Out, " ", 1);
+					dump_expr(e->left.expr, 1);
+				}
+			}
+			if (!inlist) {
+				io_write_m(Out, "#>", 2);
+			}
+			break;
+
+	}
+}
+
+static void dump_op(struct op *op) {
+	switch (op->type) {
+		case OP_NOP:
+			io_write_m(Out, "REM", 3);
+			break;
+
+		case OP_SET_VAL:
+		case OP_ASSIGN:
+			io_write_m(Out, "LET ", 4);
+			if (op->arh.expr) {
+				dump_expr(op->arh.expr, 0);
+				io_write_m(Out, " ", 1);
+				dump_expr(op->arg, 0);
+			} else {
+				io_write_m(Out, "(", 1);
+				dump_expr(op->arg, 0);
+				io_write_m(Out, ")", 1);
+			}
+			break;
+
+		case OP_CALL:
+			if (op->arh.op) {
+				fprintf(io_fp(Out), "%s ", St_ptr(&op->arh.op->txt));
+				dump_expr(op->arg, 0);
+			} else {
+				io_write_m(Out, "LET () ", 7);
+				dump_expr(op->arg, 0);
+				if (op->next) {
+					io_write_m(Out, "\nEND", 4);
+				}
+			}
+			break;
+
+		case OP_CALL_BACK:
+			io_write_m(Out, "ABRUF (", 7);
+			dump_expr(op->arh.expr, 0);
+			io_write_m(Out, ") ", 2);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_CALL_DYN:
+			io_write_m(Out, "ANRUF ", 6);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_CLOSE:
+			io_write_m(Out, "CLAUDS ", 7);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_ELSE:
+			NOTREACHED;
+			break;
+
+		case OP_EXIT:
+			if (op->arg->type == literE && !op->arg->v.val->type) {
+				io_write_m(Out, "END", 3);
+			} else {
+				io_write_m(Out, "END ", 4);
+				dump_expr(op->arg, 0);
+			}
+			break;
+
+		case OP_FI:
+			NOTREACHED;
+			break;
+
+		case OP_FLUSH:
+			io_write_m(Out, "FLUSH ", 6);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_GOBACK:
+			io_write_m(Out, "GOFOR ", 6);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_GOTO:
+			io_write_m(Out, "GOTO ", 5);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_HANG:
+			if (St_ptr(&op->txt)) {
+				fprintf(io_fp(Out), "NEXT %s", St_ptr(&op->txt));
+			} else {
+				String tmp;
+				MAKE_LABEL(&tmp, '*');
+				fprintf(io_fp(Out), "FOR %s NEXT %s", St_ptr(&tmp), St_ptr(&tmp));
+				St_clear(&tmp);
+			}
+			break;
+
+		case OP_IF:
+			if (op->arg->type == unopE && op->arg->op == F_NOT) {
+				io_write_m(Out, "IF ", 3);
+				dump_expr(op->arg->right, 0);
+			} else {
+				io_write_m(Out, "IF @NOT ", 8);
+				if (op->arg->type == binopE) {
+					io_write_m(Out, "(", 1);
+					dump_expr(op->arg, 0);
+					io_write_m(Out, ")", 1);
+				} else {
+					dump_expr(op->arg, 0);
+				}
+			}
+
+			if (op->next) {
+				fprintf(io_fp(Out), "\n  NEXT %s\nFI", St_ptr(&op->next->txt));
+			} else {
+				io_write_m(Out, "\n  END\nFI", 9);
+			}
+			break;
+
+		case OP_MODIFY:
+			io_write_m(Out, "LET ", 4);
+			dump_expr(op->arh.expr, 0);
+			if (op->arh.expr->op == B_XMATCH) {
+				io_write_m(Out, " ?o~= ", 6);
+			} else {
+				fprintf(io_fp(Out), " %c= ", display(op->arh.expr->op));
+			}
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_PRINT:
+			io_write_m(Out, "WUNT ", 5);
+			if (op->arh.expr) {
+				dump_expr(op->arh.expr, 0);
+				io_write_m(Out, " ", 1);
+				dump_expr(op->arg, 0);
+			} else if (op->arg->type != binopE) {
+				dump_expr(op->arg, 0);
+			} else {
+				io_write_m(Out, "(", 1);
+				dump_expr(op->arg, 0);
+				io_write_m(Out, ")", 1);
+			}
+			break;
+
+		case OP_PUTC:
+			io_write_m(Out, "SET ", 4);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_RESET:
+			io_write_m(Out, "RESET ", 6);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_RETURN:
+			io_write_m(Out, "KTHX ", 5);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_SYSTEM:
+			io_write_m(Out, "# ", 2);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_TEMP:
+			io_write_m(Out, "LEET ", 5);
+			dump_expr(op->arh.expr, 0);
+			io_write_m(Out, " ", 1);
+			dump_expr(op->arg, 0);
+			break;
+
+		case OP_THROW:
+			io_write_m(Out, "IACS ", 5);
+			dump_expr(op->arg, 0);
+			break;
+
+		default:
+			NOTREACHED;
+			break;
+	}
+}
+
+static struct {
+	size_t length;
+	size_t size;
+	struct op **ops;
+} seen;
+
+#define PUSH_SEEN(p) do {                                                \
+	if (seen.length >= seen.size) {                                      \
+		seen.ops = xrealloc(seen.ops, seen.size *= 2);                   \
+	}                                                                    \
+	seen.ops[seen.length++] = (p);                                       \
+} while (0)
+
+static int walk(const struct expr *e) {
+	for (;;) {
+		switch (e->type) {
+			case literE:
+			case symbolE:
+				return 0;
+
+			case varE:
+				return 0;
+
+			case varhashE:
+				e = e->right;
+				continue;
+
+			case unopE:
+				if (e->op == F_NUL) {
+					return 1;
+				} else if (e->op == F_CALL && !St_ptr(&e->left.op->txt)) {
+					MAKE_LABEL(&e->left.op->txt, 'f');
+					PUSH_SEEN(e->left.op);
+				}
+				e = e->right;
+				continue;
+
+			case binopE:
+				return walk(e->left.expr) | walk(e->right);
+
+			case listE:
+				return e->right && (walk(e->right) | (e->left.expr && walk(e->left.expr)));
+		}
+	}
+}
+
+static void do_stuff(struct op *op) {
+	for (; op && !op->line; op = op->next) {
+		op->line = 1;
+
+		if (St_ptr(&op->txt)) {
+			fprintf(io_fp(Out), "FOR %s ", St_ptr(&op->txt));
+			if (op->type != OP_NOP) {
+				dump_op(op);
+			} else {
+				io_write_m(Out, "REM", 4);
+			}
+			io_write_m(Out, "\n", 1);
+		} else {
+			dump_op(op);
+			#if 0
+			if (op->type != OP_NOP)
+			#endif
+				io_write_m(Out, "\n", 1);
+		}
+
+		if (op->type == OP_IF) {
+			if (op->arh.op) {
+				if (op->arh.op->line) {
+					fprintf(io_fp(Out), "NEXT %s\n", St_ptr(&op->arh.op->txt));
+				} else {
+					do_stuff(op->arh.op);
+				}
+			}
+		} else if (op->type == OP_CALL) {
+			if (op->arh.op && !op->arh.op->line) {
+				PUSH_SEEN(op->arh.op);
+			}
+		} else if (op->type == OP_HANG) {
+			return;
+		}
+
+		if (op->next) {
+			if (op->next->line && op->type != OP_IF && op->type != OP_RETURN) {
+				fprintf(io_fp(Out), "NEXT %s\n", St_ptr(&op->next->txt));
+			}
+		} else if (
+				op->type != OP_EXIT &&
+				op->type != OP_CALL &&
+				op->type != OP_CALL_BACK &&
+				op->type != OP_CALL_DYN &&
+				op->type != OP_HANG &&
+				op->type != OP_RETURN
+				) {
+			io_write_m(Out, "END\n", 4);
+		}
+	}
+}
+
+enum {MAGIC = 23};
+void deparse(const struct text *t) {
+	size_t i;
+	int computed_jumps;
+
+	computed_jumps = 0;
+	seen.ops = xmalloc(seen.size = MAGIC, sizeof *seen.ops);
+	seen.length = 0;
+
+	for (i = 0; i < t->length; ++i) {
+		struct op *op = t->start[i];
+
+		op->line = 0;
+
+		if (
+				op->type == OP_GOBACK ||
+				op->type == OP_GOTO ||
+				op->type == OP_CALL_DYN
+		   ) {
+			walk(op->arg);
+			computed_jumps |= 1;
+		} else if (op->type == OP_CALL_BACK) {
+			walk(op->arh.expr);
+			walk(op->arg);
+			computed_jumps |= 1;
+		} else if (OP_1ARG_P(op->type)) {
+			computed_jumps |= walk(op->arg);
+			if (op->type == OP_CALL && op->arh.op) {
+				if (!St_ptr(&op->arh.op->txt)) {
+					MAKE_LABEL(&op->arh.op->txt, 'c');
+				}
+			}
+		} else if (OP_2ARG_P(op->type)) {
+			if (op->arh.expr) {
+				computed_jumps |= walk(op->arh.expr);
+			}
+			computed_jumps |= walk(op->arg);
+		}
+
+		if (
+				op->next &&
+				(op->next != t->start[i + 1] || op->type == OP_CALL) &&
+				!St_ptr(&op->next->txt)
+		   ) {
+			MAKE_LABEL(&op->next->txt, 'x');
+		}
+	}
+
+	if (!computed_jumps) {
+		do_stuff(t->start[0]);
+		for (i = 0; i < seen.length; ++i) {
+			do_stuff(seen.ops[i]);
+		}
+	} else {
+		for (i = 0; i < t->length; ++i) {
+			struct op *op = t->start[i];
+			const String *tmp;
+
+			if ((tmp = ve_label(&Venus, op))) {
+				if (St_ptr(&op->txt)) {
+					fprintf(io_fp(Out), "    FOR %s\n", St_ptr(&op->txt));
+				}
+				fprintf(io_fp(Out), "%s%s", St_ptr(tmp), &" "[!St_len(tmp)]);
+			} else {
+				if (St_ptr(&op->txt)) {
+					fprintf(io_fp(Out), "FOR %s ", St_ptr(&op->txt));
+				}
+			}
+
+			dump_op(op);
+			io_write_m(Out, "\n", 1);
+
+			if (op->next && op->next != t->start[i + 1] && op->type != OP_IF) {
+				fprintf(io_fp(Out), "NEXT %s\n", St_ptr(&op->next->txt));
+			}
+		}
+	}
+	xfree(seen.ops);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/deparse.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+deparse.o: deparse.c config.h Str.h deparse.h text.h op.h IO.h expr.h \
+  re.h stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h \
+  main_io.h main_label.h mars.h venus.h hash.h zz.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/deparse.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,8 @@
+#ifndef DEPARSE_H_
+#define DEPARSE_H_
+
+#include "text.h"
+
+void deparse(const struct text *);
+
+#endif /* DEPARSE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/doc/perl-ploki-regex.txt	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,157 @@
+COMPARISON BETWEEN PERL AND PLOKI REGEXES
+
+
+General:
+
+ploki doesn't support perl's /cgimosx regex switches:
+/c and /g don't make sense in ploki (strings don't have an associated pos());
+/i can be emulated by writing \L str ~ "lowercaseregex";
+/m and /s aren't needed (^, A!, $, Z! match at beginning-of-string,
+beginning-of-line, end-of-string, end-of-line respectively; . matches any
+character (use '^\n' if you want any character except newline));
+/o is mostly equivalent to ploki's ?o~ operator;
+/x is the default in ploki: comments and whitespace are mostly ignored
+(except when it's escaped, in a character class, in a quantifier (like "*?"
+or ":2,:") or inside "^]", "&]", "?]" and "[number]").
+
+
+
+[Perl]                              [ploki]
+
+
+Escaping:
+
+\x                                  x!
+No word character is special. No escaped non-word character is special.
+
+
+Alternation:
+
+foo|bar                             foo|bar
+
+
+Repetition:
+
+x*                                  x*
+x+                                  x+
+x?                                  x?
+x*?                                 x*?
+x+?                                 x+?
+x??                                 x??
+x{n}                                x:n:
+x{n,}                               x:n,:
+x{n,m}                              x:n,m:
+
+
+Grouping:
+
+(?:foo)                             (foo)
+
+
+Character classes:
+
+[ab0-9\-\]']                        'ab0-9-!]'!'
+[^a-z]                              '^a-z'
+.                                   '^\n'
+.  (with /s)                        .
+[[:alnum:]]                         '[:alnum:]'
+
+Ploki provides the following POSIXish character classes (inside an ordinary
+character class only):
+[:alnum:]    alphanumeric char
+[:alpha:]    alphabetic char
+[:cntrl:]    control char
+[:digit:]    digit
+[:graph:]    printable char (except space)
+[:lower:]    lowercase char
+[:print:]    printable char (including space)
+[:punct:]    punctuation char ([:graph:] without [:alnum:])
+[:space:]    whitespace char
+[:upper:]    uppercase char
+[:xdigit:]   hex digit
+Every POSIXish subclass [:foo:] can be negated by writing [:^foo:] (this is
+compatible with Perl).
+
+In addition there are the following built-in character classes (inside and
+outside of user-defined character classes):
+
+                                     q!   (equivalent to '[:alpha:]')
+                                     c!   (equivalent to '[:cntrl:]')
+\d                                   d!   (equivalent to '[:digit:]')
+                                     l!   (equivalent to '[:lower:]')
+                                     p!   (equivalent to '[:print:]')
+\s                                   s!   (equivalent to '[:space:]') [1]
+                                     u!   (equivalent to '[:upper:]')
+                                     x!   (equivalent to '[:xdigit:]')
+\w                                   w!   (equivalent to '_[:alnum:]')
+
+They can be negated by using the corresponding uppercase letter, e.g. D!
+matches a non-digit character.
+
+[1] Perl's \s does not include \v (vertical tab).
+
+
+Independent groups:
+
+(?>foo)                              <foo>
+
+
+Capturing:
+
+(foo)                                {foo}
+
+
+Backreferences:
+
+\1, \2, ...                          0!, 1!, ...
+
+Note that ploki's backreferences start with 0 *counting from the right*,
+i.e. after a successful match against "{{f}o{o}}", \0 is "foo", \1 is "o"
+and \2 is "f".
+
+
+Assertions:
+
+^  (without /m)                      ^
+\A                                   ^
+^  (with /m)                         A!
+\z                                   $
+$  (with /m)                         Z!  (roughly)
+\b                                   b!
+\B                                   B!
+(?=foo)                              [foo&]
+(?!foo)                              [foo^]
+(?<=foo)                             NOT IMPLEMENTED
+(?<!foo)                             NOT IMPLEMENTED
+
+There's a special assertion that doesn't exist in Perl: [N] succeeds if
+N! is set, i.e. if the Nth capturing group succeeded.
+
+
+Selection:
+
+(?(cond)yes-pattern|no-pattern)      [no-pattern|yes-patterncond?]
+(?(cond)yes-pattern)                 [yes-patterncond?]
+
+Note that in ploki "cond" can be any piece: [oof?] matches "foo" if the next
+character is "f". Perl's special case of (?(N)yes|no) can be written as
+[no|yes[N]?] in ploki; Perl's m{(\()?[^()]+(?(1)\))} corresponds to ploki's
+"{(!}?'^()'+[)![0]?]".
+
+
+Comments:
+
+(?#foo)                              [foo#]
+
+
+Debugging:
+
+perl has a very nice module/command-line option: perl -Mre=debug (or perl
+-Mre=debugcolor) will show how perl compiles and matches your regexes. ploki
+offers a far less powerful feature: ploki -dr displays a human-readable form
+of every regex being compiled. It doesn't show how it matches, however. (On
+the other hand I think the regex output of ploki -dr is much more readable
+than what perl -Mre=debug produces.)
+
+
+# vi: set tw=76 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/doc/ploki-expr.txt	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,508 @@
+                      *** THE PLOKI LANGUAGE: EXPRESSIONS ***
+
+
+GENERAL
+
+ploki is an imperative, unstructured language, similar to BASIC. Some of its
+features are borrowed from Perl. ploki's data structures are fully dynamic,
+i.e. they resize themselves when necessary. Strings know their length and
+may contain binary data.
+
+
+VALUES
+
+There are six types of values:
+1. String: A (possibly empty) sequence of characters.
+   Any value can be converted to a string.
+2. Number: A (double precision) floating point number.
+   Any value can be converted to a string.
+3. IO handle: Can be read from or written to.
+4. List: A (possibly empty) sequence of values.
+5. Bound expression: See @OMFG below.
+6. Undefined.
+
+Values aren't necessarily one thing or another. There's no place to declare
+a variable to be of type "string", type "number", or anything else. In fact,
+you can't declare variables at all. Every variable you use is initialized
+with the undefined value ("undef" for short).
+
+
+Truth
+
+0, "0", "", #<#> and undef are false. Any other value is true.
+
+
+Conversions
+
+Conversions happen as follows:
+  * number -> string: decimal floating point, the "normal" way
+  * IO handle -> string: filename
+  * list -> string: concatenation of the stringified list elements
+  * bound expression -> string: undefined results
+  * undef -> string: "" (the empty string)
+
+  * string -> number: initial whitespace is skipped, the rest of the string
+                      is interpreted as a decimal floating point number
+                      (as done by strtod())
+  * IO handle -> number: undefined results
+  * list -> number: sum of the numified list elements
+  * bound expression -> number: undefined results
+  * undef -> number: 0.0
+
+
+Constructors
+
+Anything starting with a digit or a period followed by a digit is taken to
+be the beginning of a number (again, as parsed by strtod(), which means that
+scientific notation (e.g. 1e-23) is allowed). String literals start with '"'
+and end with '"' or end-of-line. The following escape sequences are
+recognized:
+
+  \OOO octal char (where OOO matches [0-7]{1,3})
+  \xXX hex char (where XX matches [0-9a-fA-F]{1,2})
+  \a   alarm
+  \b   backspace
+  \f   form feed
+  \n   newline
+  \r   carriage return
+  \t   tab
+  \v   vertical tab
+
+  \cX  the character resulting from (toupper(X) + 64) % 128
+       (this depends on the character set used)
+
+  \V<value>
+       The stringified result of evaluating <value>. In other words, this
+       can be used to interpolate expressions in strings.
+       Example: "2 + 2 = \V(2 + 2)" yields "2 + 2 = 4".
+
+
+Lists start with #< and end with #> or end-of-line. Example: #<2 +1 (3 +4)#>
+is equivalent to #<2 1 7#>.
+
+The empty value yields undef: `-2' is parsed as `  - 2', undef is converted
+to 0.0, and the result is 0.0 - 2.0, which is -2.
+
+
+Special symbols
+
+There are the following special symbols, similar to Perl's punctuation
+variables:
+
+  * \N (where N is a non-empty sequence of digits)
+    Contains the substring that was matched by the Nth capturing group in
+    the previous pattern match.
+  * \!
+    If used numerically, yields the current value of C's errno variable, or
+    in other words, if a system or library call fails, it sets this
+    variable. If used as a string, yields the corresponding system error
+    string (i.e. strerror(errno)).
+  * \?
+    Yields a random number in the range [0,1).
+  * \_
+    Contains the return value of the last command.
+  * \@
+    The argument the current function was called with.
+  * \ARG
+    Number of command line options (C's argc).
+  * \AUSG
+    The standard output IO handle (open mode: "WF").
+  * \EING
+    The standard input IO handle (open mode: "RZ").
+  * \E
+    Euler's number: 2.718281828...
+  * \FEHL
+    The standard error IO handle (open mode: "WF").
+  * \PI
+    pi: 3.14159265...
+  * \
+    undef
+
+
+VARIABLES
+
+There are two kinds of variables: plain variables and subscripted variables.
+A plain variable has the form [A-Za-z$]+, i.e. one or more alphabetic
+characters (where '$' is considered alphabetic). A subscripted variable
+looks like a plain variable immediately followed by an opening paren "(", an
+expression (the result of which is converted to a string) and possibly a
+closing paren ")".
+
+
+UNARY OPERATORS
+
+Each unary operator takes a value and returns a value. The only exception is
+@OMFG, which is more a constructor than an operator.
+
+  * \ARG:
+    Takes an integer and returns the corresponding command line argument
+    (like C's argv[]).
+  * \L
+    Returns a lowercased version of its operand (like Perl's lc).
+  * \Q
+    Returns its operand with all non-word characters backslashed, i.e. any
+    character not matching [a-zA-Z0-9_] will be preceded by a \ in the
+    returned string (like Perl's quotemeta).
+  * \R
+    Returns its operand with all non-word characters regex-escaped, i.e. any
+    character not matching [a-zA-Z0-9_] will be followed by a ! in the
+    returned string (like s/(\W)/$1!/g in Perl).
+  * \U
+    Returns an uppercased version of its operand (like Perl's uc).
+  * @+
+    For an operand N, returns the offset of the character after the portion
+    of the string matched by the Nth capturing group in the last pattern
+    matching.
+  * @-
+    For an operand N, returns the offset of the portion of the string
+    matched by the Nth capturing group in the last pattern matching.
+  * @ABS
+    Returns the absolute value of its operand.
+  * @ACOS
+    Returns the arc cosine of its operand.
+  * @APERS
+    Takes a list of two elements, a filename and a mode string. Opens the
+    specified file and returns an IO handle or undef on error. If the mode
+    string contains "A", the file is created if necessary and opened for
+    appending (i.e. every write goes to the end of the file); if the mode
+    string contains "W", the file is truncated/created and opened for
+    writing; otherwise the file is opened for reading, and the open fails if
+    the file doesn't exist. If the mode string contains "+", reading is
+    added for "A" and "W", and writing otherwise. "R+" is almost always
+    preferred for read/write access; "W+" would clobber the file first. The
+    resulting IO handle defaults to text mode; add "B" to open the file in
+    binary mode. Adding "F" turns on autoflush mode, i.e. the stream is
+    flushed after every write. If the file is opened for reading and the
+    mode string contains "Z", the resulting stream can be used with the
+    regex match operator (~).
+  * @ASIN
+    Returns the arc sine of its operand.
+  * @ATAN
+    Returns the arc tangent of its operand.
+  * @ATAN2
+    Takes a list of two elements #<y x#> and returns the arc tangent of x
+    and y. It is similar to @ATAN (y / x), except that the signs of both
+    arguments are used to determine the quadrant of the result.
+  * @CHR
+    Takes an integer and returns the character represented by that number in
+    the character set.
+  * @COS
+    Returns the cosine of its operand.
+  * @DEF-P
+    Returns "" if its operand is undef, 1 otherwise.
+  * @EDD-P
+    Returns 1 if the end-of-file indicator for its operand (which must be an
+    IO handle) is set, "" otherwise.
+  * @ENV or \ENV
+    Returns the value of the specified environment variable.
+  * @ERR-P
+    Returns 1 if the error indicator for its operand (which must be an IO
+    handle) is set, "" otherwise.
+  * @EVAL
+    Evaluates and returns its operand. If an exception is thrown during the
+    evaluation of its operand, assigns the exception value to \_ and returns
+    undef.
+  * @GET
+    Takes an IO handle, reads one character, and returns its numeric value or
+    -1 on end-of-file or error.
+  * @INT
+    Returns the integer portion of its operand.
+  * @IO-P
+    Returns 1 if its operand is an IO handle, "" otherwise.
+  * @LAPERS
+    Takes a filename, opens it for reading, and returns the resulting IO
+    handle or undef on failure. Equivalent to @APERS #<filename "RB"#>.
+  * @LEGS
+    Takes an IO handle, reads a line and returns it. Returns "" on
+    end-of-file and undef on error.
+  * @LENGTH
+    If passed a list, returns the number of elements; otherwise, returns the
+    length of its operand in characters.
+  * @LG
+    Returns the base-10 logarithm of its operand.
+  * @LN
+    Returns the natural logarithm (base e) of its operand.
+  * @NEG
+    Performs arithmetic negation.
+  * @NOT
+    Returns 1 if its operand is false, "" otherwise.
+  * @NUM
+    Converts its operand to a number.
+  * @ORD
+    Returns the numeric value of the first character of its operand.
+  * @OMFG
+    Does not evaluate its operand but wraps it up in a bound expression by
+    replacing all variables by their current value.
+  * @REMOVE
+    Tries to remove the specified file. Returns 1 for success, "" for
+    failure.
+  * @RENAEM
+    Takes a list of two filenames #<OLD NEW#> and tries to rename OLD to
+    NEW. Returns 1 for success, "" for failure.
+  * @REVERSE
+    If passed a list, returns a list consisting of the elements of the
+    original list in the opposite order. Otherwise, returns a string with
+    all the characters in the opposite order.
+  * @SAG
+    Returns a value representing the current file position of its operand
+    (which must be an IO handle) suitable as the second argument of @SUCH.
+    If the file was opened in binary mode, this is the number of bytes from
+    the beginning of the file.
+  * @SAPERS
+    Takes a filename, opens it for writing, and returns the resulting IO
+    handle or undef on failure. The file is created if it doesn't exist and
+    truncated to length 0 otherwise. Equivalent to @APERS #<filename
+    "WBF"#>.
+  * @SIN
+    Returns the sine of its operand.
+  * @SQRT
+    Returns the square root of its operand.
+  * @STR
+    Converts its operand to a string.
+  * @SUCH
+    Takes a list of two or three elements, #<IO POS WHENCE#>. WHENCE
+    defaults to 0 if omitted. Sets the current file position of IO (which
+    must be an IO handle) to POS (relative to WHENCE). WHENCE must be one of
+    0 (beginning of the file), 1 (current position) or 2 (end of file). If
+    IO is in text mode, POS must be a value returned by @SAG and WHENCE must
+    be 0, or POS must be 0. If IO is in binary mode, the new position
+    (measured in bytes from the beginning of the file) is obtained by adding
+    POS to the position specified by WHENCE. In this case POS=0 and WHENCE=2
+    may not work, depending on your C library.
+  * @TAN
+    Returns the tangent of its operand.
+  * @TYPE OF
+    Returns a string describing the type of its operand: "string" for
+    strings, "number" for numbers, "stream" for IO handles, "list" for
+    lists, "stuff" for bound expressions, "nothing" for undef.
+  * @<label> (where <label> is a static label)
+    Calls the static label <label> with its operand.
+  * @
+    Calls the dynamic label specified by its operand.
+
+
+BINARY OPERATORS
+
+Some of the operators (marked with (SL)) below double as string and list
+operators. They do the same thing with strings and lists by treating strings
+as lists of characters. Boolean and comparison operators return 1 for true
+and "" for false.
+
+ _   (SL) If given two lists, it concatenates them. Otherwise, it converts
+     its operands to strings and concatenates them.
+
+ +   Adds two numbers.
+ -   Subtracts two numbers.
+ *   Multiplies two numbers.
+ /   Divides two numbers.
+ %   Returns the modulus (remainder from division) of two numbers.
+ ^   Exponentiation.
+
+ [   (SL) Given a string/list B and an integer N, returns the
+     substring/sublist of B starting at index N. Negative indices start from
+     the end of the string/list.
+     Example: "foo" [ 1 yields "oo"; "foobar" [ -2 yields "ar".
+
+ ]   (SL) Given a string/list B and an integer N, returns the
+     substring/sublist of B having a length of N. Negative lengths start
+     from the end of the string/list.
+     Example: "foo" ] 1 yields "f"; "foobar" ] -2 yields "foob"
+
+ <   Numeric less than.
+ >   Numeric greater than.
+ {   (SL) String/list less than.
+ }   (SL) String/list greater than.
+ =   Numeric equality.
+ !   Numeric inequality.
+ :   (SL) String/list equality.
+ ;   (SL) String/list inequality.
+
+ &   Logical and.
+ |   Logical or.
+
+ ~   Pattern match. The left operand is the string or IO stream to be
+     matched, the right operand is a string forming a regular expression
+     (see PATTERNS below).  On failure, "" is returned. On success, returns
+     the position of the match and sets \_ to the position after the match.
+
+ ?o~ Similar to ~ above, except that the right operand is only evaluated the
+     first time it is executed. This means that (foo ?o~ bar) won't see any
+     changes to bar after the first match attempt (like /o in Perl).
+
+ `   Given a number X and an integer N (which must be in the range 2 .. 36),
+     returns a string representation of X in base N.
+ '   Given a string S and an integer N (which must be in the range 2 .. 36),
+     interprets S as a number in base N and returns the result.
+
+  (the empty operator) or
+ .   (SL) This one is really three different operators, depending on the
+     type of its left operand:
+      - IO handle: Attempts to read and return N bytes where N is the right
+        operand.
+      - Bound expression: Evaluates the bound expression with \@ temporarily
+        set to the right operand.
+      - String/list: Returns the element at the index specified by the right
+        operand.
+
+ ,   Returns its right operand.
+
+
+PATTERNS
+
+The ~ operator tries to match a string or IO handle against a pattern. A
+match succeeds if the pattern matches a substring of the matched string.
+When matching an IO handle (which must have been opened with "Z"), the
+matched characters are removed from the input stream if the match succeeds.
+
+Whitespace in the pattern is mostly ignored (that is, the pattern " a b" is
+equivalent to "ab"). Exceptions are marked [S] below. There is a special
+kind of "whitespace": everything between "[" and "#]" is ignored (except for
+"!"s), so you can write "foo [ a comment with [! in it #] bar" instead of
+"foobar".
+
+A search pattern consists of one or more branches, separated by "|". It
+succeeds if one of its branches succeeds. The branches are tried in the
+order they're specified.
+
+A branch is a (possibly empty) sequence of pieces. It succeeds if all of the
+pieces match in turn.
+
+A piece is an atom, possibly followed by a quantifier. A missing quantifier
+means the atom must match exactly once.
+
+A quantifier is one of the following (N, M are nonnegative integers):
+  *       0 or more of the preceding atom (greedy)
+  *?      0 or more of the preceding atom (nongreedy)   [S]
+  +       1 or more of the preceding atom (greedy)
+  +?      1 or more of the preceding atom (nongreedy)   [S]
+  ?       0 or 1 of the preceding atom (greedy)
+  ??      0 or 1 of the preceding atom (nongreedy)      [S]
+  :N:     exactly N of the preceding atom               [S]
+  :N:?    exactly N of the preceding atom               [S]
+  :N,:    N or more of the preceding atom (greedy)      [S]
+  :N,:?   N or more of the preceding atom (nongreedy)   [S]
+  :N,M:   at least N but not more than M of the preceding atom (greedy)
+          [S]
+  :N,M:?  at least N but not more than M of the preceding atom (nongreedy)
+          [S]
+
+An atom is either a character class, an assertion, a selection, a subgroup,
+a capturing group, a backreference, an independent subgroup, an
+abbreviation, an escaped character, or an ordinary character.
+
+A character class is either a built-in class or a custom class. A built-in
+class is one of the following:
+  .      a character
+  c!     a control character
+  C!     a non-control character
+  d!     a digit
+  D!     a non-digit
+  l!     a lowercase character
+  L!     a non-lowercase character
+  p!     a printable character
+  P!     a non-printable character
+  q!     an alphanumeric character
+  Q!     a non-alphanumeric character
+  s!     a space character
+  S!     a non-space character
+  u!     an uppercase character
+  U!     a non-uppercase character
+  w!     a word character, i.e. one of a-zA-Z0-9_
+  W!     a non-word character
+  x!     a hex digit, i.e. one of 0-9a-fA-F
+  X!     a non-hex character
+
+[S] A custom class is a (possibly empty) sequence of characters, enclosed in
+'' (single quotes). It matches one of the listed characters, unless the
+first character is '^', in which case it matches one of the not listed
+characters. Whitespace is significant inside character classes. A range of
+characters can be specified by putting a '-' between the endpoints. In
+addition, all built-in classes listed above (except for .) and the following
+subclasses are available:
+
+  [:alnum:]   equivalent to q!
+  [:^alnum:]  equivalent to Q!
+  [:alpha:]   equivalent to a!
+  [:^alpha:]  equivalent to A!
+  [:cntrl:]   equivalent to c!
+  [:^cntrl:]  equivalent to C!
+  [:digit:]   equivalent to d!
+  [:^digit:]  equivalent to D!
+  [:graph:]   equivalent to '^P! ', i.e. any printable character except space
+  [:^graph:]  equivalent to 'P! ', i.e. any non-printable character or space
+  [:lower:]   equivalent to l!
+  [:^lower:]  equivalent to L!
+  [:print:]   equivalent to p!
+  [:^print:]  equivalent to P!
+  [:punct:]   equivalent to '^P!q! ', i.e. any non-alphanumeric, non-space
+              printable character
+  [:^punct:]  equivalent to 'P!q! ', i.e. a non-printable character, an
+              alphanumeric character or space
+  [:space:]   equivalent to s!
+  [:^space:]  equivalent to S!
+  [:upper:]   equivalent to u!
+  [:^upper:]  equivalent to U!
+  [:xdigit:]  equivalent to x!
+  [:^xdigit:] equivalent to X!
+
+To include a literal ^, !, - or ' in a character class, escape it with a
+following !. Example: '!!'!' is a class that matches ! or '.
+
+An assertion is something that doesn't consume parts of the matched string,
+i.e. it matches with zero length. There are built-in assertions and custom
+assertions. A built-in assertion is one of the following:
+
+  ^   at beginning of string
+  $   at end of string
+  A!  at beginning of line
+  Z!  at end of line
+  b!  at a word boundary
+  B!  not at a word boundary
+
+A custom assertion is either a positive assertion, a negative assertion or a
+backreference check. A positive assertion consists of "[", followed by a
+pattern, followed by "&]" [S].  It succeeds (without consuming parts of the
+matched string) if the enclosed pattern succeeds. A negative assertion
+consists of "[", followed by a pattern, followed by "^]" [S]. It succeeds if
+the enclosed pattern fails. A backreference check consists of "[", followed
+by a number, followed by "]" [S]. It succeeds if the corresponding capturing
+group succeeded.
+
+A selection consists of "[", optionally followed by a pattern followed by
+"|", followed by a branch, followed by an atom, followed by "?]" [S]. It
+matches as follows: First the atom is tried. If it matches, the preceding
+branch is tried; otherwise matching continues with the preceding pattern if
+specified.  In other words, it looks like
+"[no-pattern|yes-pattern(conditition)?]" or "[yes-pattern(condition)?]".
+Example: "{(!}?'^()'+[)![0]?]" matches a chunk of non-parentheses, possibly
+included in parentheses themselves.
+
+A subgroup is a parenthesized pattern. It succeeds if the enclosed pattern
+succeeds.
+
+A capturing group is a pattern surrounded by "{" and "}". It succeeds if the
+enclosed pattern succeeds. As a side effect, it sets \N, @-(N) and @+(N)
+where N is the number of the capturing group. Capturing groups are numbered
+from the right, counting the closing braces (starting with 0).
+
+A backreference is N! where N is a non-negative integer. It succeeds if it
+can match the same substring that was matched by the Nth capturing group.
+
+An independent subgroup is a pattern, surrounded by "<" and ">". It succeeds
+if the enclosed pattern succeeds, but when it does, it won't backtrack in
+the enclosed pattern.
+
+An abbreviation is a (possibly empty) sequence of characters surrounded by
+"`" and "`". All metacharacters lose their special meaning inside an
+abbreviation, except for ` (which marks the end of the abbreviation) and !
+(which escapes the previous character, as usual). `abcd` is equivalent to
+(a(b(cd?)?)?)?, i.e. an abbreviation for x matches the longest possible
+initial substring of x.
+
+[S] An escaped character is a special character like "!" or "+" (or
+whitespace), followed by an "!". It matches that character literally.
+
+An ordinary character matches itself.
+
+
+# vi: set tw=76 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/doc/ploki-ins.txt	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,9 @@
+                      ### THE PLOKI LANGUAGE: INSERT ###
+
+
+The "INSERT( DA) file HERE" directive includes the contents of "file"
+verbatim at that point in the program. If " DA" is specified, "file" is
+taken to be an absolute filename; otherwise, it is searched for in the
+INC_PREFIX_LIST specified in config.h at compile time. On MS Windows and
+Unix-like systems it defaults to the directory where the program is located;
+otherwise it's empty.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/doc/ploki-instr.txt	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,97 @@
+                    %%% THE PLOKI LANGUAGE: INSTRUCTIONS %%%
+
+
+Execution starts at the first line of the program. Instructions are executed
+in the order they appear in the program. Executing end-of-file is equivalent
+to END 0, i.e. successful exit.
+
+There are the following instructions ([X] means X is optional):
+
+  * LET [Variable] Expression
+    Evaluates Expression and assigns the result to Variable (or \_ if
+    Variable is omitted).
+
+  * WUNT [Value] Expression
+    Writes the result of Expression to Value (which must yield an IO handle)
+    if specified; otherwise it writes to \AUSG (standard output). Sets \_.
+
+  * IF Expression
+    Jumps to the corresponding ELSE or FI instruction if Expression yields
+    false. "Corresponding" means it skips intervening IF-FI groups. If no
+    ELSE or FI can be found, jumps to the beginning of the program.
+
+  * ELSE
+    Jumps to the corresponding FI instruction if present or to the beginning
+    of the program otherwise.
+
+  * FI, END IF
+    Serves as an end point for IF and ELSE jumps. Does nothing.
+
+  * GOTO Expression
+    Jumps to the next line whose dynamic label is equal to the result of
+    Expression. Search wraps around at end-of-file.
+
+  * GOFOR Expression
+    Jumps to the previous line whose dynamic label is equal to the result of
+    Expression. Search wraps around at beginning-of-file.
+
+  * CLAUDS Expression
+    Closes the IO handle returned by Expression. Sets \_.
+
+  * SET [Value] Expression
+    Writes the character represented by the number returned by Expression to
+    the IO handle specified by Value (or \AUSG if Value is omitted). Sets
+    \_.
+
+  * #!
+    A comment. The rest of the line is ignored.
+
+  * REM
+    A comment. The rest of the line is ignored. REM comments may be nested.
+
+  * # Expression
+    Calls the C function system(3) with the result of Expression, i.e. runs
+    the local command line interpreter. Sets \_.
+
+  * NEXT Label
+    Jumps to Label, which must be a static label.
+
+  * ANRUF Expression
+    Calls the next instruction with the dynamic label specified by
+    Expression, i.e. it remembers the instruction it came from. Search wraps
+    around at end-of-file. Sets \_ on returning.
+
+  * ABRUF Value Expression
+    Calls the previous instruction with the dynamic label specified by
+    Value, passing Expression as argument. Search wraps around at
+    beginning-of-file. Sets \_ on returning.
+
+  * KTHX Expression
+    Returns Expression from the current call. Equivalent to END if not in a
+    call.
+  
+  * LEET [Variable] Expression
+    Similar to LET but the old value of Variable (if any) is restored at the
+    following KTHX.
+
+  * END Expression
+    Exits the program with a status of Expression. If Expression is -1,
+    exits with EXIT_FAILURE.
+
+  * IACS Expression
+    Throws Expression as an exception.
+
+  * FLUSH Expression
+    Forces a write of all buffered data for the IO handle returned by
+    expression, which must be opened for writing. Sets \_.
+
+  * RESET Expression
+    Clears the end-of-file and error flags for the IO handle returned by
+    Expression.
+
+  * Label Expression
+    Calls the instruction with the static label Label, setting \@ to
+    Expression. Sets \_ on returning.
+
+  * Expression
+    Prints the result of Expression to \AUSG.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/doc/ploki-prog.txt	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,19 @@
+                      ### THE PLOKI INTERPRETER ###
+
+Usage: ./ploki [OPTIONS] [FILE [ARG]...]
+
+Available options:
+
+  -h           print a short help message and exit
+  -v           print version and configuration info and exit
+  -MO=Deparse  turn the compiled program back into ploki source
+  -O           disable any optimizations
+  -d FLAGS     print debugging info
+               FLAGS must be one or more of the following:
+                 h   hash tables
+                 o   opcode execution
+                 r   regex engine
+
+Interpret FILE as ploki program and execute it, unless -MO=Deparse is
+specified. With no FILE, or when FILE is -, read from standard input.
+Any ARGs are passed through to the program.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/doc/ploki-syn.txt	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,137 @@
+                      === THE PLOKI LANGUAGE: SYNTAX ===
+
+
+GENERAL STRUCTURE:
+
+A ploki program consists of a (possibly empty) sequence of ordinary lines or
+include directives.
+
+An include directive is either absolute or relative. An absolute include
+directive is optional whitespace, followed by "INSERT DA", followed by one
+or more non-whitespace characters, followed by "HERE", followed by optional
+whitespace. A relative include directive is optional whitespace, followed by
+"INSERT", followed by optional whitespace, followed by one or more
+non-whitespace characters, followed by "HERE", followed by optional
+whitespace.
+
+An ordinary line consists of optional whitespace, a label, and stuff.
+
+Stuff consists of an instruction and the rest of the line. Any "??/\n"
+sequence (that's four characters: ?, ?, / and newline) in stuff is ignored
+unless the instruction is REM.
+Example:
+WUNT "foo??/
+bar"
+is equivalent to
+WUNT "foobar"
+.
+
+There are two types of labels, static and dynamic.
+ * A static label consists of the three letters "FOR", whitespace and a
+   non-empty sequence of non-whitespace characters which hasn't been defined
+   as a static label yet, followed by optional whitespace.
+ * A dynamic label consists of a possibly empty sequence of digits, followed
+   by optional whitespace.
+
+
+INSTRUCTIONS:
+
+These are the available instructions (in no particular order):
+
+  * "#!":
+    A comment. The rest of the line is ignored.
+
+  * "REM":
+    A comment. The rest of the line is ignored but scanned for "REM" ...
+    "\n" pairs. In other words, REM comments may nest.
+
+  * "LET", "LEET":
+    Assignment. The rest of the line is an optional lvalue, followed by an
+    expression.
+  
+  * "WUNT", "SET":
+    Output. The rest of the line is an optional value, followed by an
+    expression.
+
+  * "IF":
+    Conditional jump. The rest of the line starts with an expression.
+
+  * "ELSE":
+    Jump. The rest of the line is ignored.
+
+  * "FI", "END IF":
+    Jump target. The rest of the line is ignored.
+
+  * "#":
+    System call. The rest of the line starts with an expression.
+
+  * "GOTO", "GOFOR":
+    Computed jump. The rest of the line starts with an expression.
+
+  * "NEXT":
+    Static jump. The rest of the line must start with the name of a static
+    label.
+
+  * "ANRUF":
+    Computed call. The rest of the line starts with an expression.
+
+  * "ABRUF":
+    Computed call. The rest of the line is a value, followed by an
+    expression.
+
+  * "END":
+    Exit. The rest of the line starts with an expression.
+
+  * "KTHX":
+    Return. The rest of the line starts with an expression.
+
+  * "CLAUDS":
+    Close. The rest of the line starts with an expression.
+
+  * "FLUSH":
+    Flush. The rest of the line starts with an expression.
+
+  * "RESET":
+    Clear error/eof flags. The rest of the line starts with an expression.
+
+  * "IACS":
+    Throw. The rest of the line starts with an expression.
+
+  * Anything else:
+    Output. The rest of the line starts with an expression.
+
+
+EXPRESSION SYNTAX:
+
+An expression either an expression followed by a binary operator and a
+value, or a value. A value is either an lvalue or an rvalue. An lvalue is a
+plain variable or a hash variable. An rvalue is a special symbol, a
+constructor, a unary operator followed by a value, a binary operator
+followed by a value, or a "(", followed by an expression, optionally
+followed by ")". A plain variable is an identifier; a hash variable is an
+identifier, immediately followed by "(", an expression and an optional ")".
+An identifier is a non-empty sequence of "a" .. "z", "A" .. "Z", "$".
+
+Special symbols: \<DIGITS> (where <DIGITS> is a non-empty sequence of
+digits), \!, \?, \_, \@, \ARG, \AUSG, \EING, \E, \FEHL, \PI, \,  (the empty
+string).
+
+A constructor is
+- a floating point number as recognized by strtod() -OR-
+- a string starting with '"', followed by a 0 or more of the following: 
+  "\V" followed by a value, or "\" followed by a non-newline character, or a
+  non-'"' character;
+  optionally terminated by '"' -OR-
+- a list starting with "#<", followed by 0 or more values, optionally
+  terminated by "#>".
+
+Unary operators: \ARG:, \ENV, \L, \Q, \R, \U, @-, @+, @ABS, @ACOS, @APERS,
+@ASIN, @ATAN, @ATAN2, @CHR, @COS, @DEF-P, @EDD-P, @ENV, @ERR-P, @EVAL, @GET,
+@INT, @IO-P, @LAPERS, @LEGS, @LENGTH, @LG, @LN, @NEG, @NOT, @NUM, @ORD,
+@OMFG, @REMOVE, @RENAEM, @REVERSE, @SAG, @SAPERS, @SIN, @SQRT, @STR, @SUCH,
+@TAN, @TYPE OF, @<LABEL> (where <LABEL> is the name of a static label), @.
+
+Binary operators (separated by spaces):
++ - * / % ^ _ < [ { > ] } = ! : ; & | ~ ` ' . , ?o~
+
+# vi: set tw=76 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/SOME FILE	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,4 @@
+REM vi: set ft=ploki:
+GOTO 666
+FOR foo KTHX \@ * 2
+666 #!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/arg.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,7 @@
+#!../ploki
+LET i @NEG 1
+FOR arg LET i i+1
+IF i < \ARG
+	WUNT ("arg "_ i _ " = " _ \ARG:i _ "\n")
+	NEXT arg
+FI
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/beer.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,10 @@
+#!../ploki
+LET bottle("")  " bottles of beer"
+LET bottle(1)   " bottle of beer"
+LET n 99
+FOR beer WUNT (n_bottle(n=1)_" on the wall,\n"_n_bottle(n=1)_",\ntake one down and pass it around,\n")
+LET n n-1
+WUNT (n_bottle(n=1)_" on the wall.\n\n")
+IF n
+    NEXT beer
+FI
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/bf2c.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,52 @@
+#!/usr/local/bin/ploki
+REM brainfuck to c translator
+
+INSERT perl-util.pk HERE
+
+LET tr("[") "while (*p) {\n"
+LET tr("]") "}\n"
+LET tr("<") "--p;\n"
+LET tr(">") "++p;\n"
+LET tr("+") "++*p;\n"
+LET tr("-") "--*p;\n"
+LET tr(".") "putchar(*p);\n"
+LET tr(",") "*p = getchar();\n"
+LET tr("0") "*p = 0;\n"
+
+"#include <stdio.h>\nint main(void) {\nstatic unsigned char A[30000];\nunsigned char *p = A;\n" _ @s///g #<@clean @balanced @s///g #<@input \ARG:1 "'^[]<>.,+-'+" ""#> "'[]<>.,0+-'" @OMFG tr(\@)#> _ "return 0;\n}\n"
+END
+
+FOR clean LEET s \@
+	LEET $c$ 0
+	10 LET s @s///g #<@s///g #<s "{^|]!}[!'^[]'*]!" @OMFG @countrepl \0#> "{(^|]!)[!'^]'*}[!'^[]'*]!" @OMFG @countrepl \0#>
+	IF $c$
+		LET $c$ 0
+		GOFOR 10
+	FI
+KTHX @s///g #<s "[!'+-']!" "0"#>
+
+FOR countrepl LET $c$ += 1
+KTHX \@
+
+FOR balanced LEET s @s///g #<\@ "'^[]'+" ""#>
+	LEET $c$ 0
+	10 LET s @s///g #<s "[!]!" @OMFG @countrepl ""#>
+	IF $c$
+		LET $c$ 0
+		GOFOR 10
+	FI
+	IF s ; ""
+		IACS "\V\ARG:0: error: unbalanced brackets
+	FI
+KTHX \@
+
+FOR input LEET fh
+	IF @TYPE OF \@ : "nothing"
+		LET fh \EING
+	ELSE
+		LET fh @LAPERS \@
+		IF @NOT fh
+			IACS "\V\ARG:0: \V\@: \V\!
+		FI
+	FI
+KTHX fh . @NEG 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/calc.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,192 @@
+#!../ploki
+FOR main "> "
+	LET line @LEGS \EING
+	IF line ; ""
+		LET px @parse line
+		LET s @shiftws (px . 1)
+		IF s [ -1 : "\n"
+			LET s ]= -1
+		FI
+		IF @TYPE OF (px . 0) : "list"
+			IF s : ""
+				@eval (px . 0) _"
+			ELSE
+				"error: trailing garbage at --> \Vs
+			FI
+		ELSE
+			IF s : ""
+				LET s " at end of input"
+			ELSE
+				LET s " at --> \Vs"
+			FI
+			"error: \V(px . 0)\Vs
+		FI
+		NEXT main
+	FI
+	"EOF
+KTHX
+
+FOR shiftws LEET s \@
+	IF s ~ "^{s!+}"
+		KTHX s [ @+0
+	FI
+KTHX s
+
+FOR parse LEET s \@
+	LEET first @mult s
+	IF @TYPE OF (first . 0) ; "list"
+		KTHX first
+	FI
+	LET s first . 1
+	LEET op
+	LEET second
+	10 IF s ~ "^s!*{'+-'}"
+		LET op "b\V\0"
+		LET s [= @+0
+		LET second @mult s
+		IF @TYPE OF (second . 0) ; "list"
+			KTHX second
+		FI
+		LET s second . 1
+		LET first #<#<op (first . 0) (second . 0)#> s#>
+		GOFOR 10
+	FI
+KTHX first
+
+FOR mult LEET s \@
+	LEET first @pow s
+	IF @TYPE OF (first . 0) ; "list"
+		KTHX first
+	FI
+	LET s first . 1
+	LEET op
+	LEET second
+	10 IF s ~ "^s!*{'/%'|*![*!^]}"
+		LET op "b\V\0"
+		LET s [= @+0
+		LET second @pow s
+		IF @TYPE OF (second . 0) ; "list"
+			KTHX second
+		FI
+		LET s second . 1
+		LET first #<#<op (first . 0) (second . 0)#> s#>
+		GOFOR 10
+	FI
+KTHX first
+
+FOR pow LEET s \@
+	LEET first @term s
+	IF @TYPE OF (first . 0) ; "list"
+		KTHX first
+	FI
+	LET s first . 1
+	IF s ~ "^s!*{^!|*!*!}"
+		LET s [= @+0
+		LEET second @pow s
+		IF @TYPE OF (second . 0) ; "list"
+			KTHX second
+		FI
+		LET s second . 1
+		KTHX #<#<"b^" (first . 0) (second . 0)#> s#>
+	FI
+KTHX first
+
+FOR term LEET s \@
+	IF s ~ "^s!*(!{}"
+		LET s [= @+0
+		LEET tmp @parse s
+		IF @TYPE OF (tmp . 0) ; "list"
+			KTHX tmp
+		FI
+		LET s tmp . 1
+		IF s ~ "^s!*)!{}"
+			LET s [= @+0
+			KTHX #<(tmp . 0) s#>
+		FI
+		KTHX #<"`)' expected" s#>
+	FI
+	IF s ~ "^s!*{d!+(.!d!*)?|.!d!+}"
+		LET s [= @+0
+		KTHX #<#<"n" @NUM \0#> s#>
+	FI
+	IF s ~ "^s!*{'+-'|log|exp|a?(sin|cos|tan)}"
+		LEET op \0
+		LET s [= @+0
+		LEET tmp @pow s
+		IF @TYPE OF (tmp . 0) ; "list"
+			KTHX tmp
+		FI
+		KTHX #<#<"u\Vop" (tmp . 0)#> (tmp . 1)#>
+	FI
+	IF s ~ "^s!*pi{}"
+		LET s [= @+0
+		KTHX #<#<"n" \PI#> s#>
+	FI
+	IF s ~ "^s!*e{}"
+		LET s [= @+0
+		KTHX #<#<"n" \E#> s#>
+	FI
+KTHX #<"invalid value" s#>
+
+FOR eval LEET e \@
+	IF e . 0 : "n"
+		KTHX e . 1
+	FI
+	IF e . 0 . 0 : "u"
+		LEET right @eval (e . 1)
+		LEET op e . 0 [ 1
+		IF op : "+"
+			KTHX right
+		FI
+		IF op : "-"
+			KTHX @NEG right
+		FI
+		IF op : "log"
+			KTHX @LN right
+		FI
+		IF op : "exp"
+			KTHX \E ^ right
+		FI
+		IF op : "sin"
+			KTHX @SIN right
+		FI
+		IF op : "cos"
+			KTHX @COS right
+		FI
+		IF op : "tan"
+			KTHX @TAN right
+		FI
+		IF op : "asin"
+			KTHX @ASIN right
+		FI
+		IF op : "acos"
+			KTHX @ACOS right
+		FI
+		IF op : "atan"
+			KTHX @ATAN right
+		FI
+	FI
+	IF e . 0 . 0 : "b"
+		LEET left @eval (e . 1)
+		LEET right @eval (e . 2)
+		LEET op e . 0 . 1
+		IF op : "+"
+			KTHX left + right
+		FI
+		IF op : "-"
+			KTHX left - right
+		FI
+		IF op : "*"
+			KTHX left * right
+		FI
+		IF op : "/"
+			KTHX left / right
+		FI
+		IF op : "%"
+			KTHX left % right
+		FI
+		IF op : "^"
+			KTHX left ^ right
+		FI
+	FI
+KTHX
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/cat.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,42 @@
+#!../ploki
+NEXT main-program
+
+FOR cat LET line @LEGS \@
+    IF line ; ""
+        WUNT line
+        NEXT cat
+    END IF
+KTHX
+
+FOR open
+    IF \@ : "-"
+        KTHX \EING
+    FI
+KTHX @LAPERS \@
+
+FOR main-program
+LET status 0
+IF \ARG < 2
+    cat \EING
+    END status
+FI
+LET i 0
+FOR arg LET i i+1
+IF i < \ARG
+    LET fh @open \ARG:i
+    IF @NOT fh
+        WUNT (\ARG:0_": "_\ARG:i_": "_\!_"\n")
+	LET status status+1
+	NEXT arg
+    FI
+    cat fh
+    IF fh ! \EING
+        CLAUDS fh
+		IF \_
+            WUNT (\ARG:0_": "_\ARG:i_": "_\!_"\n")
+	        LET status status+1
+        FI
+    FI
+    NEXT arg
+FI
+END status
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/env.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,2 @@
+#!../ploki
+"Your home directory is \"\V\ENV"HOME"\"\nYour current working directory is \"\V@ENV"PWD"\"\n"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/fac.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,21 @@
+"calculate the faculty of [1..8]: "ß{
+REM faculty program in false and ploki
+REM Lukas Mai, 11.09.2003
+REM} [$1=$[\%1\]?~[$1-f;!*]?]f: ^'0-$$0>~\8>|$ {
+LET n @LEGS(\EING)
+REM}
+"result: "ß{
+IF n < 1 | (n > 8)
+  REM} ~[\f;!.]?[
+  "illegal input"ß{
+ELSE
+  LET f 1
+  FOR loop IF n > 0
+    LET f f * n
+    LET n n - 1
+    NEXT loop
+  FI
+  WUNT f
+FI
+REM} ]?"
+"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/fact.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,20 @@
+#!../ploki
+LET n \ARG:1
+FOR $main$1$loop LET x @kt n
+LET n n/x
+"\Vx "
+IF @NOT(n > 1)
+	"
+	END
+FI
+NEXT $main$1$loop
+
+FOR kt LEET i 2
+FOR kt$1$loop IF \@ / 2 < i
+	KTHX \@
+FI
+IF \@ % i = 0
+	KTHX i
+FI
+LET i i + 1
+NEXT kt$1$loop
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/facul.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,8 @@
+#!/usr/local/bin/ploki
+@x \ARG:1 . 1 _"
+END
+FOR x IF \@ < 2
+		KTHX @OMFG \@
+	FI
+	LEET x \@
+KTHX @OMFG (@x (x - 1) . (x * \@))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/fib.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,13 @@
+#!../ploki 
+LET n 1
+FOR loop WUNT (@fib(n) _ " ")
+LET n n + 1
+IF n > 13
+  WUNT "\n"
+  END
+FI
+NEXT loop
+FOR fib IF \@ > 2
+    KTHX @INT(@SQRT 1.25 + .5 * @fib(\@ - 1))
+  FI
+KTHX 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/fibcheck.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,14 @@
+#!../ploki
+REM fibonacci sequence tester
+REM sample input:
+REM x x xx xxx xxxxx
+REM x x x
+FOR line LET line @LEGS \EING
+	IF line ; ""
+		IF line ~ "^s!*({[S!|[{S!}|[2!|1![1]?]{0!}[2]?][0]?]}s!+)+$"
+			"fibonacci sequence!
+		ELSE
+			"nope...
+		FI
+		NEXT line
+	FI
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/foobar.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+#!../ploki
+REM should print "foobar" :-)
+\Q"foo\c"\V 	
"ar""_"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/goto.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,15 @@
+#!../ploki
+WUNT "1..3
+GOTO 0
+01 WUNT "ERROR MESSAGE
+10 END
+10 WUNT "ERROR MESSAGE
+WUNT "ok 1
+0  GOTO 10
+20 WUNT "ok 3
+20 GOTO 20
+20 GOTO 0
+20 WUNT "ERROR MESSAGE
+10 WUNT "ok 2
+10 GOTO 20
+00 GOTO 10
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/hanoi.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,25 @@
+#!../ploki
+IF \ARG < 2
+	WUNT "Usage: \V\ARG:0 NUMBER\n"
+	END -1
+FI
+LET a "a"
+LET b "b"
+LET c "c"
+move \ARG:1
+WUNT "\n"
+END
+FOR move IF \@ > 0
+		LEET ax a
+		LEET bx b
+		LEET cx c
+		LET b cx
+		LET c bx
+		move \@ - 1
+		WUNT (ax_bx_" ")
+		LET a cx
+		LET b bx
+		LET c ax
+		move \@ - 1
+	FI
+KTHX
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/hell.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,2 @@
+#!../ploki
+"Hello, world!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/hello.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+#!../ploki
+10 WUNT \AUSG "Hello, world!\n"
+20 END 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/insert.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+#!../ploki
+INSERT SOME FILE HERE
+@foo 2 _ "
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/io.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,4 @@
+#!../ploki
+LETs@LEGS(\EING)
+WUNTs
+IF:s
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/list.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,27 @@
+#!../ploki
+NEXT main
+
+FOR join LEET s ""
+LEET i 0
+IF i < @LENGTH (\@ . 1)
+	LET s \@ . 1 . 0
+	LET i i+1
+	FOR join-loop IF i < @LENGTH (\@ . 1)
+		LET s s _ (\@ . 0) _ (\@ . 1 . i)
+		LET i i+1
+		NEXT join-loop
+	FI
+FI
+KTHX s
+	
+FOR map LEET r #<#>
+LEET i 0
+FOR map-loop IF i < @LENGTH (\@ . 1)
+	LET r r _ #<(\@ . 0 . (\@ . 1 . i))#>
+	LET i i+1
+	NEXT map-loop
+FI
+KTHX r
+
+FOR main LET list #<1 -2 3 52#>
+@join #<" " @map #<@OMFG(\@ * 2) list#>#> _"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/loop.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+IF
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/mark.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,19 @@
+#!../ploki
+FOREACH LET$@LEGS\EING
+IF$:
+END
+FIX
+IF;\ARG:1
+LETO
+FOR; LETP $[O~\R\ARG:1
+IFP
+LET P+O+P
+LET$ $]P_"\33[1m\V ($[P]@LENGTH\ARG:1)\033[m"_($[(P+@LENGTH\ARG:1
+LETO7+@LENGTH\ARG:1+P
+IF$[O;
+NEXT;
+FISH
+FITS
+END IF BEGIN ELSE
+$
+NEXTEACH
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/mod.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,10 @@
+#!../ploki
+LET i 1
+i_"
+LET i += 2
+i_"
+LET a -= 4
+a_"
+LET *= 3
+LET a a/ 2
+a_"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/num.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,6 @@
+#!../ploki
+IF \ARG < 4
+    WUNT "Usage: \V\ARG:0 FROM-BASE TO-BASE NUMBER
+    END 1
+FI
+@REVERSE(\ARG:3'\ARG:1`\ARG:2)_"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/omfg.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,8 @@
+#!../ploki
+WUNT "2+3\t-3+24\t2+24\t-3+3
+LET a @ADD 2
+LET a$ @ADD @NEG 3
+WUNT \AUSG a.3 _ "\t" _ (a$.24) _"\t "_(a.24)_"\t "_(a$.3)_"
+END
+FOR ADD LEET X \@
+KTHX @OMFG(X + \@)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/out.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+#!../ploki
+WUNT \AUSG "STDOUT
+WUNT \FEHL "STDERR
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/perl-util.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,167 @@
+GOTO 666
+
+FOR map LEET f \@ . 0
+	LEET list \@ . 1
+	LEET result #<#>
+	LEET i 0
+	10 IF i < @LENGTH list
+		LET result _= f . (list . i)
+		LET i += 1
+		GOFOR 10
+	FI
+KTHX result
+
+FOR grep LEET f \@ . 0
+	LEET list \@ . 1
+	LEET result #<#>
+	LEET i 0
+	10 IF i < @LENGTH list
+		IF f . (list . i)
+			LET result _= #<(list . i)#>
+		FI
+		LET i += 1
+		GOFOR 10
+	FI
+KTHX result
+
+FOR s/// LEET str \@ . 0
+	LEET pat \@ . 1
+	LEET repl \@ . 2
+	LEET ms str ~ pat
+	IF ms
+		LEET me \_
+		IF @TYPE OF repl : "stuff"
+			LET repl .= str [ ms ] (me - ms)
+		FI
+		LET str str ] ms _ repl _ (str [ me)
+	FI
+KTHX str
+
+FOR s///g LEET str \@ . 0
+	LEET pat \@ . 1
+	LEET repl \@ . 2
+	LEET new
+	LEET p 0
+	LEET ms
+	LEET me
+	10 LET ms str [ p ~ pat
+	IF ms
+		LET me \_
+		LET new repl
+		IF @TYPE OF new : "stuff"
+			LET new .= str [ p [ ms ] (me - ms)
+		FI
+		LET new @STR new
+		LET str str ] (p + ms) _ new _ (str [ (p + me))
+		LET p += ms = me + ms + @LENGTH new
+		IF @NOT (p > @LENGTH str)
+			GOFOR 10
+		FI
+	FI
+KTHX str
+
+200 LEET b \@.0 _ @REVERSE (\@.1)
+	LEET result #<#>
+	LEET d
+	LEET i 0
+	LEET j @LENGTH b - 1
+	210 IF i < j
+		LET d cmp.#<(b.i) (b.j)#>
+		IF d < 0
+			LET result _= b [ i ] 1
+			LET i += 1
+		ELSE
+			IF d > 0
+				LET result _= b [ j ] 1
+				LET j -= 1
+			ELSE
+				LET result _= b [ i ] 1
+				LET i += 1
+				LET result _= b [ j ] 1
+				LET j -= 1
+			FI
+		FI
+		GOFOR 210
+	ELSE
+		IF i = j
+			LET result _= b [ i ] 1
+		FI
+	FI
+KTHX result
+
+FOR sort LEET cmp \@.0
+	LEET list \@.1
+	IF @LENGTH list < 2
+		KTHX list
+	FI
+	LEET mid @INT (@LENGTH list / 2)
+	ABRUF 200 #<@sort #<cmp (list ] mid)#> @sort #<cmp (list [ mid)#>#>
+KTHX \_
+
+FOR join LEET sep \@.0
+	LEET list \@.1
+	LEET result ""
+	IF @LENGTH list
+		LET result _= list.0
+		LEET i 1
+		10 IF i < @LENGTH list
+			LET result _= sep
+			LET result _= list.i
+			LET i += 1
+			GOFOR 10
+		FI
+	FI
+KTHX result
+
+FOR split LEET pat \@.0
+	LEET str \@.1
+	LEET result #<#>
+	IF "" ~ pat
+		LEET i 0
+		10 IF i < @LENGTH str
+			LET result _= #<(str . i)#>
+			LET i += 1
+			GOFOR 10
+		FI
+	ELSE
+		LEET p 0
+		LEET ms
+		20 LET ms str [ p ~ pat
+		IF ms
+			LET result _= #<(str [ p ] ms)#>
+			LET p += \_
+			GOFOR 20
+		FI
+		LET result _= #<(str [ p)#>
+	FI
+KTHX result
+
+FOR dumper LEET x \@
+	IF @TYPE OF x : "number"
+		KTHX x
+	FI
+	IF @TYPE OF x : "stream"
+		KTHX "{IO}" _ x
+	FI
+	IF @TYPE OF x : "stuff"
+		KTHX "{EXPR}?"
+	FI
+	IF @TYPE OF x : "nothing"
+		KTHX "()"
+	FI
+	IF @TYPE OF x : "list"
+		KTHX "#<" _ @join #<" " @map #<@OMFG #<@dumper \@#> x#>#> _ "#>"
+	FI
+KTHX "\"" _ @s///g #<x "'P!\\\"'" @OMFG "\\\V@REVERSE (@ORD \@ ` 8)"#> _"\""
+
+FOR x LEET s \@ . 0
+	LEET n \@ . 1
+	LEET res ""
+	10 IF n > 0
+		LET res _= s
+		LET n -= 1
+		GOFOR 10
+	FI
+KTHX res
+
+666
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/quine.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,14 @@
+#!../ploki
+REM My first quine! Lukas Mai, 07.03.2003
+LET b "b"
+LET n "n"
+LET v "v"
+LET bb "\\"
+LET nn "\n"
+LET vv "\""
+LET bn "#!../ploki"
+LET vvv "REM My first quine! Lukas Mai, 07.03.2003"
+LET vb "LET "
+LET nv " "
+LET bnv "WUNT (bn_nn _vvv_nn _vb_b_nv_vv_b_vv_nn _vb_n_nv_vv_n_vv_nn _vb_v_nv_vv_v_vv_nn _vb_b_b_nv_vv_bb_bb_vv_nn _vb_n_n_nv_vv_bb_n_vv_nn _vb_v_v_nv_vv_bb_vv_vv_nn _vb_b_n_nv_vv_bn_vv_nn _vb_v_v_v_nv_vv_vvv_vv_nn _vb_v_b_nv_vv_vb_vv_nn _vb_n_v_nv_vv_nv_vv_nn _vb_b_n_v_nv_vv_bnv_vv_nn _bnv_nn)"
+WUNT (bn_nn _vvv_nn _vb_b_nv_vv_b_vv_nn _vb_n_nv_vv_n_vv_nn _vb_v_nv_vv_v_vv_nn _vb_b_b_nv_vv_bb_bb_vv_nn _vb_n_n_nv_vv_bb_n_vv_nn _vb_v_v_nv_vv_bb_vv_vv_nn _vb_b_n_nv_vv_bn_vv_nn _vb_v_v_v_nv_vv_vvv_vv_nn _vb_v_b_nv_vv_vb_vv_nn _vb_n_v_nv_vv_nv_vv_nn _vb_b_n_v_nv_vv_bnv_vv_nn _bnv_nn)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/ref.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,17 @@
+GOTO 496
+FOR *&ref-assign*& LET $(\@ . 0) \@ . 1
+KTHX \@ . 1
+FOR *&ref-call&* LEET id \@ . 0
+	LEET arg \@ . 1
+	IF \L arg : "set"
+		LEET ref $$ref$$(id)
+		KTHX @OMFG @*&ref-assign*& #<ref \@#>
+	FI
+KTHX $($$ref$$(id))
+FOR ref LEET val \@
+	LET $$ref$cnt$$ += 1
+	LEET ref @OMFG @*&ref-call&* #<$$ref$cnt$$ \@#>
+	LET $$ref$$($$ref$cnt$$) ref
+	LET $(ref) \@
+KTHX ref
+496 #!
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/regex.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,2 @@
+#!../ploki -dr
+LET "dummy" ~ \ARG:1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/reset.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,14 @@
+#!../ploki
+IF @NOT ("foo" ~ "{.}0!") | (\0 ; "o")
+	"error
+	END -1
+FI
+IF "foo" ~ "b"
+	"error
+	END -1
+FI
+IF @DEF-P \0
+	"ARGH: match variable not reset
+	END -1
+FI
+"ok
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/ret.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,56 @@
+#!../ploki
+REM regular expression tester
+NEXT main:
+
+FOR trymatch
+	LEET txt \@ . 0
+	LEET pat \@ . 1
+	LEET ms txt ~ pat
+	IF ms
+		LET me \_
+		"matched at pos \Vms [\V(txt ] ms)\033[1m\V(txt [ ms ] (me - ms))\033[m\V(txt [ me ] 255)]
+		IF @DEF-P \0
+			WUNT "\\0<\V@-0, \V@+0>: \V\0
+		FI
+		IF @DEF-P \1
+			WUNT "\\1<\V@-1, \V@+1>: \V\1
+		FI
+		IF @DEF-P \2
+			WUNT "\\2<\V@-2, \V@+2>: \V\2
+		FI
+	ELSE
+		WUNT "no match
+	FI
+KTHX
+
+FOR main:
+	IF \ARG < 2
+		WUNT "Usage: \V\ARG:0 PATTERN [FILE]
+		END @NEG1
+	FI
+	LET pat \ARG:1
+	IF \ARG > 2
+		LET fh @LAPERS \ARG:2
+		IF @NOT fh
+			WUNT \AUSG \ARG:0_": "_\ARG:2_": "_\!_"
+			END @NEG1
+		FI
+		FOR buf LET s fh . 4096
+		IF s ; ""
+#!			"appending [\V@TYPE OFs:\V@LENGTHs]\Vs to \Vtxt
+			LET txt _= s
+			NEXT buf
+		FI
+#!		"matching /\Vpat/ against \Vtxt
+		trymatch #<txt pat#>
+	ELSE
+		"pattern: /\Vpat/
+		FOR line LET line @LEGS \EING
+		IF line ; ""
+			IF line [ -1 : "
+				LET line line ] -1
+			FI
+			trymatch #<line pat#>
+			NEXT line
+		FI
+	FI
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/rev.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,7 @@
+#!../ploki
+FOR line LET line @LEGS \EING
+IF line ; ""
+	LET s s _ line
+	NEXT line
+FI
+WUNT @REVERSE s
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/revrec.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,7 @@
+#!../ploki
+FOR f LEET c @GET \EING
+	IF c > -1
+		f
+		SET c
+	FI
+KTHX
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/rfc822-fun.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,79 @@
+#!../ploki
+REM adaption of Jeffrey Friedl's RFC 822 email regex
+REM (taken from _Mastering Regular Expressions_)
+
+GOTO 1000
+
+FOR esc        KTHX "\\"
+FOR Period     KTHX ".!"
+FOR OpenBr     KTHX "[!"
+FOR CloseBr    KTHX "]!"
+FOR OpenParen  KTHX "(!"
+FOR CloseParen KTHX ")!"
+FOR NonASCII   KTHX "\x80-\xff"
+FOR ctl        KTHX "\000-\037"
+FOR CRlist     KTHX "\n\015"
+
+FOR qtext        KTHX "'^\V@esc()\V@NonASCII()\V@CRlist()\"'"
+FOR dtext        KTHX "'^\V@esc()\V@NonASCII()\V@CRlist()[]'"
+FOR quoted$pair  KTHX @esc() _ "'^\V@NonASCII()'"
+
+FOR ctext    KTHX "'^\V@esc()\V@NonASCII()\V@CRlist())('"
+
+FOR Cnested  KTHX @OpenParen() _ @ctext() _ "*(\V@quoted$pair()\V@ctext()*)*" _ @CloseParen()
+
+FOR comment  KTHX @OpenParen() _ @ctext() _ "*((\V@quoted$pair()|\V@Cnested())\V@ctext()*)*" _ @CloseParen()
+
+FOR X          KTHX "' \t'*(\V@comment()' \t'*)*"
+
+FOR atom$char  KTHX "'^() <>\\@,;:\".\V@esc()[]\V@ctl()\V@NonASCII()'"
+FOR atom       KTHX "<\V@atom$char()+>"
+
+FOR quoted$str  KTHX "\"\V@qtext()*(\V@quoted$pair()\V@qtext()*)*\""
+
+FOR word        KTHX "(\V@atom()|\V@quoted$str())"
+
+FOR domain$ref  KTHX @atom()
+
+FOR domain$lit  KTHX @OpenBr() _ "(\V@dtext()|\V@quoted$pair())*" _ @CloseBr()
+
+FOR sub$domain  KTHX "(\V@domain$ref()|\V@domain$lit())" _ @X()
+
+FOR domain  KTHX @sub$domain() _ "(\V@Period()\V@X()\V@sub$domain())*"
+
+FOR route   KTHX "@" _ @X() _ @domain() _ "(,\V@X()\V@domain())*:!" _ @X()
+
+FOR local$part  KTHX @word() _ @X() _ "(\V@Period()\V@X()\V@word()\V@X())*"
+
+FOR addr$spec    KTHX @local$part() _ "@" _ @X() _ @domain()
+
+FOR route$addr   KTHX "<!(\V@route())?\V@addr$spec()>!"
+
+FOR phrase$ctl   KTHX "\000-\010\012-\037"
+
+FOR phrase$char  KTHX "'^()<>@,;:\".\V@esc()[]\V@NonASCII()\V@phrase$ctl()'"
+
+FOR phrase       KTHX @word() _ @phrase$char() _ "*((\V@comment()|\V@quoted$str())\V@phrase$char()*)*"
+
+FOR mailbox  KTHX @X() _ "(\V@addr$spec()|\V@phrase()\V@route$addr())"
+
+
+1000 REM *** main code ***
+
+REM ******************
+REM ** test snippet **
+REM ******************
+
+LET status 0
+LET i 1
+FOR main-loop IF i < \ARG
+		IF \ARG:i ~ ("^" _ @mailbox() _ "$")
+			"`\V\ARG:i' is syntactically valid.
+		ELSE
+			"`\V\ARG:i' is syntactically invalid.
+			LET status @NEG 1
+		FI
+		LET i += 1
+		NEXT main-loop
+	FI
+	END status
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/rfc822.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,75 @@
+#!../ploki
+REM adaption of Jeffrey Friedl's RFC 822 email regex
+REM (taken from _Mastering Regular Expressions_)
+
+LET $esc        "\\"
+LET $Period     ".!"
+LET $OpenBr     "[!"
+LET $CloseBr    "]!"
+LET $OpenParen  "(!"
+LET $CloseParen ")!"
+LET $NonASCII   "\x80-\xff"
+LET $ctl        "\000-\037"
+LET $CRlist     "\n\015"
+
+LET $qtext        "'^\V$esc\V$NonASCII\V$CRlist\"'"
+LET $dtext        "'^\V$esc\V$NonASCII\V$CRlist[]'"
+LET $quoted$pair  $esc _ "'^\V$NonASCII'"
+
+LET $ctext    "'^\V$esc\V$NonASCII\V$CRlist)('"
+
+LET $Cnested  $OpenParen _ $ctext _ "*(\V$quoted$pair\V$ctext*)*" _ $CloseParen
+
+LET $comment  $OpenParen _ $ctext _ "*((\V$quoted$pair|\V$Cnested)\V$ctext*)*" _ $CloseParen
+
+LET $X          "' \t'*(\V$comment' \t'*)*"
+
+LET $atom$char  "'^() <>\\@,;:\".\V$esc[]\V$ctl\V$NonASCII'"
+LET $atom       "<\V$atom$char+>"
+
+LET $quoted$str  "\"\V$qtext*(\V$quoted$pair\V$qtext*)*\""
+
+LET $word        "(\V$atom|\V$quoted$str)"
+
+LET $domain$ref  $atom
+
+LET $domain$lit  $OpenBr _ "(\V$dtext|\V$quoted$pair)*" _ $CloseBr
+
+LET $sub$domain  "(\V$domain$ref|\V$domain$lit)" _ $X
+
+LET $domain  $sub$domain _ "(\V$Period\V$X\V$sub$domain)*"
+
+LET $route   "@" _ $X _ $domain _ "(,\V$X\V$domain)*:!" _ $X
+
+LET $local$part  $word _ $X _ "(\V$Period\V$X\V$word\V$X)*"
+
+LET $addr$spec    $local$part _ "@" _ $X _ $domain
+
+LET $route$addr   "<!(\V$route)?\V$addr$spec>!"
+
+LET $phrase$ctl   "\000-\010\012-\037"
+
+LET $phrase$char  "'^()<>@,;:\".\V$esc[]\V$NonASCII\V$phrase$ctl'"
+
+LET $phrase       $word _ $phrase$char _ "*((\V$comment|\V$quoted$str)\V$phrase$char*)*"
+
+LET $mailbox  $X _ "(\V$addr$spec|\V$phrase\V$route$addr)"
+
+
+REM ******************
+REM ** test snippet **
+REM ******************
+
+LET status 0
+LET i 1
+FOR main-loop IF i < \ARG
+		IF \ARG:i ~ ("^" _ $mailbox _ "$")
+			"`\V\ARG:i' is syntactically valid.
+		ELSE
+			"`\V\ARG:i' is syntactically invalid.
+			LET status @NEG 1
+		FI
+		LET i += 1
+		NEXT main-loop
+	FI
+	END status
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/string.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,12 @@
+#!../ploki
+LET pat "\"{<'^\"\\'*><(\\.<'^\"\\'*>)*>}\""
+FOR line LET line @LEGS \EING
+IF line ; ""
+	FOR match LET p line ~ pat
+	IF p
+		WUNT "> `\V\0'
+		LET line line [ (p + 2 + @LENGTH \0)
+		NEXT match
+	FI
+	NEXT line
+FI
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/subst.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,14 @@
+#!../ploki
+INSERT perl-util.pk HERE
+NEXT main
+
+FOR mod "double \V\@ -> \V(\@ * 2)
+KTHX \@ * 2
+
+FOR main
+	LET s @LEGS \EING
+	IF s : ""
+		END
+	FI
+	@s///g #<s ".!?d!+" @OMFG(@mod \@)#>
+	NEXT main
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/throw.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,12 @@
+#!../ploki
+LET a @EVAL @foo 2
+a_priv_"
+LET a @EVAL @foo 0
+a_", "_\__priv_"
+END
+FOR foo LEET priv " ERROR MESSAGE"
+	IF \@ > 0
+		KTHX \@ * 2
+	ELSE
+		IACS "error"
+	FI
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/try.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,13 @@
+#!../ploki
+LET pat "foo{.*}bar"
+LET i 0
+FOR loop IF i < 2
+	IF "afoo42bar" ?o~ pat
+		"match \Vi -- \V\0
+	ELSE
+		"no match \Vi
+	FI
+	LET i += 1
+	LET pat "{a}"
+	NEXT loop
+FI
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/uncomment.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,30 @@
+#!../ploki
+REM Usage: uncomment.pk [FILE [...]]
+REM Opens each FILE in turn, removes C (and C++) comments, and prints
+REM the results to stdout. Reads from stdin if FILE is omitted.
+
+INSERT perl-util.pk HERE
+LET status 0
+IF \ARG < 2
+	@delcom \EING
+ELSE
+	LET i 1
+	FOR main-loop LET fh @APERS #<\ARG:i "R"#>
+	IF @NOT fh
+		WUNT \FEHL "\V\ARG:0: \V\ARG:i: \V\!
+		LET status @NEG 1
+	ELSE
+		@delcom fh
+		CLAUDS fh
+	FI
+	LET i += 1
+	IF i < \ARG
+		NEXT main-loop
+	FI
+FI
+
+END status
+
+FOR delcom LEET fh \@
+	LEET s fh . @NEG 1
+KTHX @s///g #<s "/((\\|?!?!/)\n!)*(/((\\|?!?!/)\n|'^\n')*|*!'^*'**!+((\\|?!?!/)\n!)*('^/*''^*'**!+((\\|?!?!/)\n!)*)*{/})|{(\"((\\|?!?!/).|'^\"')*\"|'!((\\|?!?!/).|'^'!')*'!|.)'^'!\"/'*}" @OMFG (" " ] @DEF-P \1 _ \0)#>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/var.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,4 @@
+#!../ploki
+"> "
+LETdesoxyribonukleinsaeure("lysergsäurediethylamid")@LEGS\EING
+"you said: "_desoxyribonukleinsaeure("lysergsäurediethylamid")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/examples/write.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,13 @@
+#!../ploki
+LET fname "foo.txt"
+LET fh @SAPERS fname
+IF @NOT @IO-P fh
+  WUNT "\Vfname: \V\!\n"
+  END -1
+FI
+WUNT fh, (\ENV"USER"_" ("_\ENV"HOME"_") "_\ENV"PWD"_"\n"_\?_"\n")
+CLAUDS fh
+IF \_
+  WUNT "\Vfname: \V\!\n"
+  END -1
+FI
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/expr.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1080 @@
+#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();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/expr.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,4 @@
+expr.o: expr.c config.h Str.h expr.h op.h IO.h re.h stack.h xmalloc.h \
+ strhash.h val.h kork.h list.h sub.h variable.h hang.h main_io.h \
+ main_label.h mars.h venus.h hash.h main_var.h match.h pp.h random.h \
+ run.h text.h zz.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/expr.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,181 @@
+#ifndef EXPR_H_
+#define EXPR_H_
+
+#include "config.h"
+#include "Str.h"
+#include "op.h"
+#include "re.h"
+#include "stack.h"
+#include "strhash.h"
+#include "val.h"
+#include "variable.h"
+
+#include <stddef.h>
+#include <setjmp.h>
+
+enum t_expr {
+	literE,
+	varE,
+	varhashE,
+	symbolE,
+	unopE,
+	binopE,
+	listE
+};
+
+enum t_func {
+	F_CALL = -1,
+	F_NUL,
+	F_ABS,
+	F_ACOS,
+	F_ASIN,
+	F_ATAN,
+	F_ATAN2,
+	F_CATCH,
+	F_CHR,
+	F_COS,
+	F_DEFINED,
+	F_EOF,
+	F_ERROR,
+	F_EXP,
+	F_FREEZE,
+	F_GETC,
+	F_GETENV,
+	F_GETS,
+	F_HANG,
+	F_INT,
+	F_IO,
+	F_LENGTH,
+	F_LOG,
+	F_LOG10,
+	F_LOWER,
+	F_MATCH,
+	F_MOEND,
+	F_MOSTART,
+	F_NEG,
+	F_NOT,
+	F_NUM,
+	F_OPEN,
+	F_OPENR,
+	F_OPENW,
+	F_ORD,
+	F_QUOTE,
+	F_RE_ESC,
+	F_REMOVE,
+	F_RENAME,
+	F_REVERSE,
+	F_SEEK,
+	F_SIN,
+	F_SQRT,
+	F_STR,
+	F_TAN,
+	F_TELL,
+	F_TYPEOF,
+	F_UPPER
+};
+
+enum t_symbol {
+	S_NUL,
+	S_ARG,
+	S_ARGC,
+	S_ARGV,
+	S_ERR,
+	S_EULER,
+	S_LUDOLF,
+	S_MATCH,
+	S_RAND,
+	S_RESULT,
+	S_STDIN,
+	S_STDOUT,
+	S_STDERR
+};
+
+enum t_binop {
+	B_AMPERSAND,
+	B_ANGLE,
+	B_BACKSPARK,
+	B_BRACELET,
+	B_DOUBLE_OH_SEVEN,
+	B_EMBRACE,
+	B_FLATWORM,
+	B_HALF_MESH,
+	B_HYBRID,
+	B_INTERSECTION,
+	B_RIGHT_ANGLE,
+	B_SHARK_FIN,
+	B_SLAT,
+	B_SPARK,
+	B_SPARK_SPOT,
+	B_SPIKE,
+	B_SPLAT,
+	B_SPOT,
+	B_SQIGGLE,
+	B_TAIL,
+	B_TWO_SPOT,
+	B_U_TURN,
+	B_U_TURN_BACK,
+	B_WORM,
+	B_XMATCH
+};
+
+struct expr {
+	enum t_expr type;
+	int op;
+	union {
+		t_vr_cookie tent;
+		struct val *val;
+		t_strhash *hash;
+	} v;
+	struct expr *right;
+	union {
+		struct expr *expr;
+		struct op *op;
+		t_regex *rx;
+		size_t bonus;
+	} left;
+};
+
+void expr_init(void);
+void expr_end(void);
+
+void free_expr(struct expr *);
+struct expr *get_expr(struct op *);
+struct expr *get_lval(struct op *);
+struct expr *get_iobj(struct op *);
+
+void eval_push(struct expr *);
+void eval_into(struct expr *, struct val *);
+struct val *eval_pop(void);
+struct val *eval_expr(struct expr *);
+
+ATTR_PURE
+enum t_binop expr_binop(int);
+
+void expr_pp(enum t_binop, struct val *, struct val *);
+
+typedef struct {
+	jmp_buf buf;
+	size_t depth;
+} t_context;
+
+stack_declare(t_context, extern)
+extern stack(t_context) Context;
+
+#define OPERATORS "_+-*/%^[]<>=!{}:;&|~`'.,"
+#define OPERATOR_P(c) strchr(OPERATORS, c)
+
+#define TOLABEL(v)                            \
+do {                                          \
+	V_NUM(v);                                 \
+	V_xxx_OFF(v);                             \
+	(v)->num = floor((v)->num + .5);          \
+	(v)->type = V_NUM_K;                      \
+	v_ok_str(v);                              \
+	ko_grep(v->ko, isdigit);                  \
+	while (ko_at(v->ko, 0) == '0') {          \
+		ko_shift(v->ko, 1);                   \
+	}                                         \
+	(v)->type = V_STR_K;                      \
+} while (0)
+
+#endif /* EXPR_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/hang.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,12 @@
+#include "config.h"
+#include "hang.h"
+
+#if HAVE_SLEEP_P
+	#include SLEEP_HEADER
+#endif
+
+void hang(void) {
+	for (;;) {
+		DO_SLEEP;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/hang.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+hang.o: hang.c config.h hang.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/hang.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,9 @@
+#ifndef HANG_H_
+#define HANG_H_
+
+#include "config.h"
+
+ATTR_NORETURN
+void hang(void);
+
+#endif /* HANG_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/hash.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,273 @@
+#include "config.h"
+#include "hash.h"
+#include "main.h"
+#include "main_opt.h"
+#include "random.h"
+#include "xmalloc.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <assert.h>
+
+struct h_entry {
+	size_t hash;
+	void *key;
+	void *value;
+};
+
+struct h_node {
+	struct h_entry entry;
+	struct h_node *next;
+};
+
+enum {MAGIC = 112};
+
+void h_init(
+		Hash *h,
+		size_t (*hash)(const void *, size_t),
+		int (*cmp)(const void *, const void *),
+		void (*delk)(void *),
+		void (*delv)(void *)
+		) {
+	size_t i;
+
+	h->entries = 0;
+	h->brk = -1;
+	h->iter = -1;
+	h->iterptr = NULL;
+	h->seed = randval() * (size_t)-1;
+	h->delk = delk;
+	h->delv = delv;
+	assert(hash != NULL);
+	h->hash = hash;
+	assert(cmp != NULL);
+	h->cmp = cmp;
+	h->table = xmalloc(h->newsize = h->size = MAGIC, sizeof *h->table);
+	for (i = 0; i < h->size; ++i) {
+		h->table[i] = NULL;
+	}
+}
+
+#define DELKV(p)                   \
+do {                               \
+	if (h->delk) {                 \
+		h->delk((p)->entry.key);   \
+	}                              \
+	if (h->delv) {                 \
+		h->delv((p)->entry.value); \
+	}                              \
+} while (0)
+
+void h_end(Hash *h) {
+	size_t size, b;
+
+	if (!h->table)
+		return;
+
+	for (b = 0, size = h->newsize; h->newsize; h->newsize--) {
+		struct h_node *p;
+
+		if ((p = h->table[h->newsize - 1])) {
+			++b;
+			for (; p; p = h->table[h->newsize - 1]) {
+				h->table[h->newsize - 1] = p->next;
+				DELKV(p);
+				xfree(p);
+			}
+		}
+	}
+	xfree(h->table);
+	h->table = NULL;
+
+	if (Opt.debug & DBG_HASH) {
+		fprintf(stderr, "%s: hash[%lu/%lu/%lu]\n", Prog, (unsigned long)h->entries, (unsigned long)b, (unsigned long)size);
+	}
+}
+
+ATTR_PURE
+static size_t xclip(const Hash *h, const size_t hash_unc) {
+	size_t hash;
+
+	hash = hash_unc % h->size;
+	if (hash > h->brk) {
+		hash = hash_unc % h->newsize;
+	}
+
+	return hash;
+}
+
+#if 0
+ATTR_PURE
+static size_t xhash(const Hash *h, const void *key) {
+	return xclip(h, h->hash(key, h->seed));
+}
+#endif
+
+#define XRESIZN(h) ((h)->brk + 1u)
+#define XSTEP(h)   do { if (XRESIZN(h)) xstep(h); } while (0)
+
+static void xstep(Hash *h) {
+	struct h_node *rev = NULL;
+	struct h_node *p;
+	struct h_node *tmp;
+
+	assert(h->table != NULL);
+
+	for (p = h->table[h->brk]; p; p = tmp) {
+		tmp = p->next;
+		p->next = rev;
+		rev = p;
+	}
+	h->table[h->brk--] = NULL;
+
+	if (!XRESIZN(h)) {
+		h->size = h->newsize;
+	}
+
+	for (p = rev; p; p = tmp) {
+		const size_t i = xclip(h, p->entry.hash);
+		tmp = p->next;
+		p->next = h->table[i];
+		h->table[i] = p;
+	}
+}
+
+static void xincentries(Hash *h) {
+	h->entries++;
+	if (!(XRESIZN(h)) && h->entries / h->size > 3u) {
+		size_t i;
+		h->table = xrealloc(h->table, h->newsize = h->size * 2u + 1u);
+		for (i = h->size; i < h->newsize; ++i) {
+			h->table[i] = NULL;
+		}
+		h->brk = h->size - 1u;
+		xstep(h);
+	}
+}
+
+int h_get(Hash *h, const void *key, void **res) {
+	XSTEP(h);
+	{
+		const size_t hash_unc = h->hash(key, h->seed);
+		const size_t hash = xclip(h, hash_unc);
+		struct h_node *p;
+
+		for (p = h->table[hash]; p; p = p->next) {
+			if (hash_unc == p->entry.hash && h->cmp(p->entry.key, key) == 0) {
+				if (res) {
+					*res = p->entry.value;
+				}
+				return H_OK;
+			}
+		}
+	}
+	return H_NOENT;
+}
+
+int h_del(Hash *h, const void *key) {
+	XSTEP(h);
+	{
+		const size_t hash_unc = h->hash(key, h->seed);
+		const size_t hash = xclip(h, hash_unc);
+		struct h_node **p;
+
+		for (p = &h->table[hash]; *p; p = &(*p)->next) {
+			if (hash_unc == (*p)->entry.hash && h->cmp((*p)->entry.key, key) == 0) {
+				struct h_node *tmp = *p;
+				*p = (*p)->next;
+				DELKV(tmp);
+				xfree(tmp);
+				h->entries--;
+				return H_OK;
+			}
+		}
+	}
+
+	return H_NOENT;
+}
+
+#if 0
+void h_push(Hash *h, void *key, void *val) {
+	XSTEP(h);
+	{
+		const size_t hash_unc = h->hash(key, h->seed);
+		const size_t hash = xclip(h, hash_unc);
+		struct h_node *p;
+
+		p = xmalloc(1, sizeof *p);
+		p->entry.hash = hash_unc;
+		p->entry.key = key;
+		p->entry.value = val;
+		p->next = h->table[hash];
+		h->table[hash] = p;
+
+		xincentries(h);
+	}
+}
+#endif
+
+int h_put(Hash *h, void *key, void *val, int replace) {
+	XSTEP(h);
+	{
+		const size_t hash_unc = h->hash(key, h->seed);
+		const size_t hash = xclip(h, hash_unc);
+		struct h_node **p;
+
+		for (p = &h->table[hash]; *p; p = &(*p)->next) {
+			if (hash_unc == (*p)->entry.hash && h->cmp((*p)->entry.key, key) == 0) {
+				if (replace) {
+					DELKV(*p);
+					(*p)->entry.key = key;
+					(*p)->entry.value = val;
+					return H_OK;
+				} else {
+					return H_EXIST;
+				}
+			}
+		}
+
+		*p = xmalloc(1, sizeof **p);
+
+		(*p)->entry.hash = hash_unc;
+		(*p)->entry.key = key;
+		(*p)->entry.value = val;
+		(*p)->next = NULL;
+
+		xincentries(h);
+	}
+
+	return H_OK;
+}
+
+void h_reset(Hash *h) {
+	h->iter = -1;
+	h->iterptr = NULL;
+}
+
+int h_nextkv(Hash *h, void **k, void **v) {
+	if (h->iterptr) {
+		*k = h->iterptr->entry.key;
+		*v = h->iterptr->entry.value;
+		h->iterptr = h->iterptr->next;
+		return H_OK;
+	}
+
+	for (h->iter++; h->iter < h->newsize; h->iter++) {
+		if ((h->iterptr = h->table[h->iter])) {
+			*k = h->iterptr->entry.key;
+			*v = h->iterptr->entry.value;
+			h->iterptr = h->iterptr->next;
+			return H_OK;
+		}
+	}
+
+	h->iter = -1;
+	return H_NOENT;
+}
+
+#if 0
+size_t (h_entries)(const Hash *h) {
+	return h_entries(h);
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/hash.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+hash.o: hash.c config.h hash.h main.h main_opt.h random.h xmalloc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/hash.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,47 @@
+#ifndef HASH_H_
+#define HASH_H_
+
+#include <stddef.h>
+
+typedef struct {
+	size_t entries, size, newsize;
+	size_t brk;
+	size_t seed;
+	size_t iter;
+	struct h_node *iterptr;
+	struct h_node **table;
+	size_t (*hash)(const void *, size_t);
+	int (*cmp)(const void *, const void *);
+	void (*delk)(void *);
+	void (*delv)(void *);
+} Hash;
+
+enum {
+	H_OK,
+	H_EXIST,
+	H_NOENT
+};
+
+void h_init(
+		Hash *,
+		size_t (*)(const void *, size_t),
+		int (*)(const void *, const void *),
+		void (*)(void *),
+		void (*)(void *)
+		);
+void h_end(Hash *);
+int h_get(Hash *, const void *, void **);
+int h_del(Hash *, const void *);
+#if 0
+void h_push(Hash *, void *, void *);
+#endif
+int h_put(Hash *, void *, void *, int);
+void h_reset(Hash *);
+int h_nextkv(Hash *, void **, void **);
+
+#if 0
+size_t h_entries(const Hash *);
+#define h_entries(h) ((h)->entries + 0)
+#endif
+
+#endif /* HASH_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/inc.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,4 @@
+#include "config.h"
+#include "inc.h"
+
+const char *inc_ludes[] = INC_PREFIX_LIST;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/inc.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+inc.o: inc.c config.h inc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/inc.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,6 @@
+#ifndef INC_H_
+#define INC_H_
+
+extern const char *inc_ludes[];
+
+#endif /* INC_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/indent/ploki.vim	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,43 @@
+" Only load this one if no other indent file was loaded.
+if exists("b:did_indent")
+    finish
+endif
+
+let b:did_indent = 1
+setlocal indentexpr=GetPlokiIndent()
+setlocal indentkeys=!^F,o,O,0=ELSE,0=END\ IF,0=FI,0=KTHX
+
+" Only define the function once.
+if exists("*GetPlokiIndent")
+    finish
+endif
+
+function GetPlokiIndent()
+    if v:lnum == 1
+		return 0
+	endif
+
+	let plnum = v:lnum - 1
+	let prevline = getline(plnum)
+	while plnum > 0 && prevline =~ '^\s*$'
+		let plnum = plnum - 1
+		let prevline = getline(plnum)
+	endwhile
+
+	let ind = indent(plnum)
+	let thisline = getline(v:lnum)
+
+	if prevline =~ '^FOR\%(\s*\S\+\)\@>\%(\s*KTHX\)\@!'
+		let ind = ind + &sw
+	endif
+	if prevline =~ '^\s*\%(FOR\s*\S\+\|\d*\)\s*\%(IF\|ELSE\)'
+		let ind = ind + &sw
+	endif
+	if thisline =~ '^\s*\%(FOR\s*\S\+\|\d*\)\s*\%(ELSE\|END IF\|FI\)'
+		let ind = ind - &sw
+	elseif ind == &sw && thisline =~ '^\s*\%(\d\+\s*\)\=KTHX'
+		let ind = ind - &sw
+	endif
+
+	return ind
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/kork.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,237 @@
+#include "config.h"
+#include "IO.h"
+#include "Str.h"
+#include "kork.h"
+#include "strutil.h"
+#include "xmalloc.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct ko_dolphin dolphin;
+typedef struct kork kork;
+
+static dolphin nil = {
+	{ "FLIPPER", 1, 1, 0 },
+	0
+};
+
+static dolphin *incr(dolphin *dp) {
+	++dp->refs;
+	return dp;
+}
+
+static void decr(dolphin *dp) {
+	if (dp->refs) {
+		--dp->refs;
+		return;
+	}
+
+	St_clear(&dp->str);
+	xfree(dp);
+}
+
+kork *ko_new(void) {
+	kork *const k = xmalloc(1, sizeof *k);
+	k->kdp = incr(&nil);
+	k->offset = 0;
+	k->length = 0;
+	return k;
+}
+
+kork *ko_dup(const kork *old) {
+	kork *const k = xmalloc(1, sizeof *k);
+	*k = *old;
+	incr(k->kdp);
+	return k;
+}
+
+void ko_decouple(kork *k) {
+	dolphin *const dp = k->kdp;
+	if (!k->kdp->refs) {
+		return;
+	}
+	k->kdp = xmalloc(1, sizeof *k->kdp);
+	St_init(&k->kdp->str);
+	St_cpy_m(&k->kdp->str, St_ptr(&dp->str) + k->offset, k->length);
+	k->kdp->refs = 0;
+	k->offset = 0;
+	decr(dp);
+}
+
+void ko_delete(kork *k) {
+	decr(k->kdp);
+	xfree(k);
+}
+
+size_t (ko_length)(const kork *k) {
+	return ko_length(k);
+}
+
+int ko_at(const kork *k, size_t i) {
+	return i < k->length ? ST_INDEX(&k->kdp->str, k->offset + i) : EOF;
+}
+
+int ko_lastchar(const kork *k) {
+	return k->length ? ST_INDEX(&k->kdp->str, k->offset + k->length - 1u) : EOF;
+}
+
+int ko_cmp(const kork *k1, const kork *k2) {
+	return u_cmp(
+			St_ptr(&k1->kdp->str) + k1->offset, k1->length,
+			St_ptr(&k2->kdp->str) + k2->offset, k2->length
+			);
+}
+
+void (ko_zero)(kork *k) {
+	ko_zero(k);
+}
+
+const char *ko_ptr(const kork *k) {
+	return St_ptr(&k->kdp->str) + k->offset;
+}
+
+static void offoff(kork *k) {
+	ko_decouple(k);
+	if (k->offset) {
+		St_del(&k->kdp->str, 0, k->offset);
+		k->offset = 0;
+	}
+	assert(St_len(&k->kdp->str) >= k->length);
+}
+
+const String *ko_str(kork *k) {
+	offoff(k);
+	St_trunc(&k->kdp->str, k->length);
+	return &k->kdp->str;
+}
+
+const char *ko_szp(kork *k) {
+	if (k->kdp->refs && k->offset + k->length != St_len(&k->kdp->str)) {
+		offoff(k);
+	}
+	return St_ptr(&k->kdp->str) + k->offset;
+}
+
+void ko_grep(kork *k, int (*pred)(int)) {
+	offoff(k);
+	St_grep(&k->kdp->str, pred);
+	k->length = St_len(&k->kdp->str);
+}
+
+void ko_shift(kork *k, size_t n) {
+	if (n > k->length) {
+		n = k->length;
+	}
+	k->offset += n;
+	k->length -= n;
+}
+
+void ko_num(kork *k, double d) {
+	offoff(k);
+	St_num(&k->kdp->str, d);
+	k->length = St_len(&k->kdp->str);
+}
+
+void ko_cpy_m(kork *k, const void *p, size_t n) {
+	if (!n) {
+		ko_zero(k);
+		return;
+	}
+	offoff(k);
+	St_cpy_m(&k->kdp->str, p, n);
+	k->length = St_len(&k->kdp->str);
+}
+
+void ko_cpy(kork *k, const kork *z) {
+	ko_cpy_m(k, ko_ptr(z), ko_length(z));
+}
+
+void ko_cpy_s(kork *k, const char *s) {
+	ko_cpy_m(k, s, strlen(s));
+}
+
+void ko_cpy_c(kork *k, char c) {
+	offoff(k);
+	St_cpy_c(&k->kdp->str, c);
+	k->length = 1;
+}
+
+void ko_cat_m(kork *k, const void *p, size_t n) {
+	if (!n) {
+		return;
+	}
+	if (k->kdp->refs && k->offset + k->length != St_len(&k->kdp->str)) {
+		offoff(k);
+	}
+	assert(k->offset + k->length <= St_len(&k->kdp->str));
+	St_trunc(&k->kdp->str, k->offset + k->length);
+	St_cat_m(&k->kdp->str, p, n);
+	k->length += n;
+}
+
+void ko_cat(kork *k, const kork *z) {
+	ko_cat_m(k, ko_ptr(z), ko_length(z));
+}
+
+void ko_cat_c(kork *k, char c) {
+	ko_cat_m(k, &c, 1);
+}
+
+void ko_reverse(kork *k) {
+	offoff(k);
+	St_reverse(&k->kdp->str);
+}
+
+size_t ko_getline(IO *io, kork *k) {
+	size_t tmp;
+	offoff(k);
+	tmp = io_getline(io, &k->kdp->str);
+	k->length = St_len(&k->kdp->str);
+	return tmp;
+}
+
+size_t ko_read(IO *io, kork *k, size_t n) {
+	size_t tmp;
+	offoff(k);
+	tmp = io_read(io, &k->kdp->str, n);
+	k->length = St_len(&k->kdp->str);
+	return tmp;
+}
+
+void ko_lower(kork *k) {
+	offoff(k);
+	St_lower(&k->kdp->str);
+}
+
+void ko_upper(kork *k) {
+	offoff(k);
+	St_upper(&k->kdp->str);
+}
+
+size_t ko_chr(const kork *k, int c) {
+	const char *const p = memchr(St_ptr(&k->kdp->str) + k->offset, c, k->length);
+	if (!p) {
+		return -1;
+	}
+	return p - (St_ptr(&k->kdp->str) + k->offset);
+}
+
+void ko_shiftws(kork *k) {
+	while (
+			k->length &&
+			isspace((unsigned char)St_ptr(&k->kdp->str)[k->offset])
+		  ) {
+		++k->offset;
+		--k->length;
+	}
+}
+
+void ko_trunc(kork *k, size_t n) {
+	if (n >= k->length) {
+		return;
+	}
+	k->length = n;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/kork.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+kork.o: kork.c config.h IO.h Str.h kork.h strutil.h xmalloc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/kork.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,63 @@
+#ifndef KORK_H_
+#define KORK_H_
+
+#include "config.h"
+#include "IO.h"
+#include "Str.h"
+
+#include <stddef.h>
+
+struct ko_dolphin {
+	String str;
+	size_t refs;
+};
+
+struct kork {
+	struct ko_dolphin *kdp;
+	size_t offset, length;
+};
+
+struct kork *ko_new(void);
+struct kork *ko_dup(const struct kork *);
+void ko_decouple(struct kork *);
+void ko_delete(struct kork *);
+
+ATTR_PURE
+size_t ko_length(const struct kork *);
+ATTR_PURE
+int ko_at(const struct kork *, size_t);
+ATTR_PURE
+int ko_lastchar(const struct kork *);
+ATTR_PURE
+int ko_cmp(const struct kork *, const struct kork *);
+
+void ko_zero(struct kork *);
+
+ATTR_PURE
+const char *ko_ptr(const struct kork *);
+const String *ko_str(struct kork *);
+const char *ko_szp(struct kork *);
+void ko_grep(struct kork *, int (*)(int));
+void ko_shift(struct kork *, size_t);
+void ko_num(struct kork *k, double);
+void ko_cpy_m(struct kork *, const void *, size_t);
+void ko_cpy(struct kork *, const struct kork *);
+void ko_cpy_s(struct kork *, const char *);
+void ko_cpy_c(struct kork *, char);
+void ko_cat_m(struct kork *, const void *, size_t);
+void ko_cat(struct kork *, const struct kork *);
+void ko_cat_c(struct kork *, char);
+void ko_reverse(struct kork *);
+size_t ko_getline(IO *, struct kork *);
+size_t ko_read(IO *, struct kork *, size_t);
+void ko_lower(struct kork *);
+void ko_upper(struct kork *);
+ATTR_PURE
+size_t ko_chr(const struct kork *, int);
+void ko_shiftws(struct kork *);
+void ko_trunc(struct kork *, size_t);
+
+#define ko_length(k) ((k)->length + 0u)
+#define ko_zero(k) ((void)((k)->length = 0u))
+
+#endif /* KORK_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/list.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,219 @@
+#include "list.h"
+#include "val.h"
+#include "xmalloc.h"
+
+#include <string.h>
+#include <assert.h>
+
+typedef struct li_whale whale;
+
+static whale *incr(whale *wp) {
+	assert(wp->refs > 0);
+	++wp->refs;
+	return wp;
+}
+
+static void decr(whale *wp) {
+	assert(wp->refs > 0);
+	if (!--wp->refs) {
+		while (wp->length) {
+			--wp->length;
+			v_delete(wp->field[wp->length]);
+		}
+		xfree(wp->field);
+		xfree(wp);
+	}
+}
+
+enum {MAGIC = 7};
+
+struct list *li_new(void) {
+	struct list *l = xmalloc(1, sizeof *l);
+	l->lwp = xmalloc(1, sizeof *l->lwp);
+	l->lwp->field = xmalloc(l->lwp->size = MAGIC, sizeof *l->lwp->field);
+	l->lwp->length = 0;
+	l->lwp->refs = 1;
+	l->offset = 0;
+	l->length = 0;
+	return l;
+}
+
+struct list *li_dup(const struct list *k) {
+	struct list *l = xmalloc(1, sizeof *l);
+	l->lwp = incr(k->lwp);
+	l->offset = k->offset;
+	l->length = k->length;
+	return l;
+}
+
+static void decouple(struct list *l, size_t slack) {
+	size_t i, n;
+	whale *const wp = l->lwp;
+	struct val **const field = wp->field + l->offset, **nfield;
+	assert(wp->refs > 0);
+	if (wp->refs == 1) {
+		return;
+	}
+	l->lwp = xmalloc(1, sizeof *l->lwp);
+	n = l->length;
+	l->lwp->field = nfield = xmalloc(l->lwp->size = n + slack > MAGIC ? n + slack : MAGIC, sizeof *l->lwp->field);
+	for (i = 0; i < n; ++i) {
+		v_set(nfield[i] = v_undef(), field[i]);
+	}
+	l->lwp->length = n;
+	l->lwp->refs = 1;
+	l->offset = 0;
+	decr(wp);
+}
+
+void li_decouple(struct list *l) {
+	decouple(l, 1);
+}
+
+void li_delete(struct list *l) {
+	decr(l->lwp);
+	xfree(l);
+}
+
+size_t (li_length)(const struct list *l) {
+	return li_length(l);
+}
+
+struct val *li_at(const struct list *l, size_t i) {
+	return i < l->length ? l->lwp->field[l->offset + i] : NULL;
+}
+
+int li_cmp(const struct list *k, const struct list *l) {
+	const size_t len_k = li_length(k), len_l = li_length(l);
+	struct val **const field_k = k->lwp->field + k->offset, **const field_l = l->lwp->field + l->offset;
+	size_t i;
+
+	for (i = 0; i < len_k && i < len_l; ++i) {
+		const int tmp = v_cmp_ls(field_k[i], field_l[i]);
+		if (tmp) {
+			return tmp;
+		}
+	}
+	if (i < len_k) {
+		return 1;
+	}
+	if (i < len_l) {
+		return -1;
+	}
+	return 0;
+}
+
+void (li_zero)(struct list *l) {
+	li_zero(l);
+}
+
+static void vpswap(struct val **p, struct val **q) {
+	struct val *tmp = *p;
+	*p = *q;
+	*q = tmp;
+}
+
+void li_push(struct list *l, struct val *v) {
+	struct li_whale *wp = l->lwp;
+	const size_t off = l->offset, len = l->length, wlen = wp->length, wsiz = wp->size;
+	struct val **field = wp->field;
+	assert(wp->refs > 0);
+	if (wp->refs > 1) {
+		decouple(l, 1);
+		wp = l->lwp;
+		field = wp->field;
+	} else if (off + len < wlen) {
+		v_delete(field[off + len]);
+	} else if (wlen < wsiz) {
+		/* */
+	} else if (off) {
+		size_t i;
+		for (i = 0; i < len; ++i) {
+			vpswap(&field[i], &field[off + i]);
+		}
+		l->offset = 0;
+		v_delete(field[len]);
+	} else {
+		wp->field = field = xrealloc(field, wp->size *= 2);
+	}
+	field[l->offset + l->length++] = v;
+	if (l->offset + l->length > wlen) {
+		wp->length = l->offset + l->length;
+	}
+}
+
+void li_push_cpy(struct list *l, const struct val *v) {
+	struct li_whale *wp = l->lwp;
+	const size_t off = l->offset, len = l->length, wlen = wp->length, wsiz = wp->size;
+	struct val **field = wp->field;
+	assert(wp->refs > 0);
+	if (wp->refs > 1) {
+		decouple(l, 1);
+		wp = l->lwp;
+		field = wp->field;
+		field[l->offset + len] = v_undef();
+	} else if (off + len < wlen) {
+		/* */
+	} else if (wlen < wsiz) {
+		field[off + len] = v_undef();
+	} else if (off) {
+		size_t i;
+		for (i = 0; i < len; ++i) {
+			vpswap(&field[i], &field[off + i]);
+		}
+		l->offset = 0;
+	} else {
+		wp->field = field = xrealloc(field, wp->size *= 2);
+		field[len] = v_undef();
+	}
+	v_set(field[l->offset + l->length++], v);
+	if (l->offset + l->length > wlen) {
+		wp->length = l->offset + l->length;
+	}
+}
+
+void li_append(struct list *k, const struct list *l) {
+	struct val **const field = l->lwp->field + l->offset;
+	const size_t len = li_length(l);
+	size_t i;
+
+	for (i = 0; i < len; ++i) {
+		li_push_cpy(k, field[i]);
+	}
+}
+
+void li_reverse(struct list *l) {
+	struct val **field;
+	size_t i, len;
+
+	len = li_length(l);
+	if (len < 2) {
+		return;
+	}
+
+	li_decouple(l);
+
+	field = l->lwp->field + l->offset;
+
+	for (i = 0; i < len / 2; ++i) {
+		vpswap(&field[i], &field[len - i - 1]);
+	}
+}
+
+void li_trunc(struct list *l, size_t n) {
+	if (l->length > n) {
+		if (!(l->length = n)) {
+			l->offset = 0;
+		}
+	}
+}
+
+void li_shift(struct list *l, size_t n) {
+	if (n >= li_length(l)) {
+		li_zero(l);
+		return;
+	}
+
+	l->offset += n;
+	l->length -= n;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/list.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+list.o: list.c list.h config.h val.h IO.h Str.h kork.h sub.h xmalloc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/list.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,42 @@
+#ifndef LIST_H_
+#define LIST_H_
+
+#include "config.h"
+
+#include <stddef.h>
+
+struct li_whale {
+	struct val **field;
+	size_t length, size;
+	size_t refs;
+};
+
+struct list {
+	struct li_whale *lwp;
+	size_t offset, length;
+};
+
+struct list *li_new(void);
+struct list *li_dup(const struct list *);
+void li_decouple(struct list *);
+void li_delete(struct list *);
+
+ATTR_PURE
+size_t li_length(const struct list *);
+ATTR_PURE
+struct val *li_at(const struct list *, size_t);
+ATTR_PURE
+int li_cmp(const struct list *, const struct list *);
+
+void li_zero(struct list *);
+void li_push(struct list *, struct val *);
+void li_push_cpy(struct list *, const struct val *);
+void li_append(struct list *, const struct list *);
+void li_reverse(struct list *);
+void li_trunc(struct list *, size_t);
+void li_shift(struct list *, size_t);
+
+#define li_length(l) ((l)->length + 0)
+#define li_zero(l) ((void)(l->offset = l->length = 0))
+
+#endif /* LIST_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/main.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,307 @@
+#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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/main.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,5 @@
+main.o: main.c IO.h config.h Str.h atechit.h compile.h text.h op.h expr.h \
+  re.h stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h \
+  deparse.h inc.h main.h main_io.h main_label.h mars.h venus.h hash.h \
+  main_opt.h main_var.h opt.h parse.h random.h run.h transmogrify.h \
+  version.h zz.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/main.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,6 @@
+#ifndef MAIN_H_
+#define MAIN_H_
+
+extern const char *Prog;
+
+#endif /* MAIN_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/main_io.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,8 @@
+#ifndef MAIN_IO_H_
+#define MAIN_IO_H_
+
+#include "IO.h"
+
+extern IO *In, *Out, *Err;
+
+#endif /* MAIN_IO_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/main_label.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,10 @@
+#ifndef MAIN_LABEL_H_
+#define MAIN_LABEL_H_
+
+#include "mars.h"
+#include "venus.h"
+
+extern struct mars Mars;
+extern struct venus Venus;
+
+#endif /* MAIN_LABEL_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/main_opt.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,16 @@
+#ifndef MAIN_OPT_H_
+#define MAIN_OPT_H_
+
+struct Options {
+	int deparse;
+	enum {
+		DBG_OPS   = 1,
+		DBG_HASH  = 2 * DBG_OPS,
+		DBG_REGEX = 2 * DBG_HASH
+	} debug;
+	int unoptimize;
+};
+
+extern struct Options Opt;
+
+#endif /* MAIN_OPT_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/main_var.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,8 @@
+#ifndef MAIN_VAR_H_
+#define MAIN_VAR_H_
+
+#include "variable.h"
+
+extern t_vr_container *Var_plain, *Var_hash;
+
+#endif /* MAIN_VAR_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/mars.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,85 @@
+#include "Str.h"
+#include "mars.h"
+#include "op.h"
+#include "xmalloc.h"
+
+enum {MAGIC = 128};
+
+static void null(struct mars **p, size_t n) {
+	for (; n; --n) {
+		p[n - 1] = NULL;
+	}
+}
+
+void ma_init(struct mars *m) {
+	m->data = NULL;
+	m->table = xmalloc(m->size = MAGIC, sizeof *m->table);
+	null(m->table, m->size);
+}
+
+void ma_end(struct mars *m) {
+	for (; m->size; --m->size) {
+		if (m->table[m->size - 1]) {
+			ma_end(m->table[m->size - 1]);
+			xfree(m->table[m->size - 1]);
+		}
+	}
+	xfree(m->table);
+}
+
+int ma_enter(struct mars *m, const String *key, struct op *value) {
+	size_t p;
+
+	for (p = 0; p < St_len(key); ++p) {
+		size_t tmp = ST_INDEX(key, p);
+		if (m->size <= tmp) {
+			m->table = xrealloc(m->table, m->size * 2);
+			null(m->table + m->size, m->size);
+			m->size *= 2;
+		}
+		if (!m->table[tmp]) {
+			m->table[tmp] = xmalloc(1, sizeof *m->table[tmp]);
+			ma_init(m->table[tmp]);
+		}
+		m = m->table[tmp];
+	}
+
+	if (m->data) {
+		return -1;
+	}
+	m->data = value;
+	return 0;
+}
+
+int ma_exists(const struct mars *m, const String *key) {
+	size_t p;
+	const struct op *ret = NULL;
+
+	for (p = 0; p < St_len(key); ++p) {
+		size_t tmp = ST_INDEX(key, p);
+		ret = m->data;
+		if (tmp >= m->size || !m->table[tmp]) {
+			return 0;
+		}
+		m = m->table[tmp];
+	}
+	return ret != NULL;
+}
+
+struct op *ma_find(const struct mars *m, String *key) {
+	size_t p;
+	struct op *ret = NULL;
+
+	for (p = 0; p < St_len(key); ++p) {
+		size_t tmp = ST_INDEX(key, p);
+		ret = m->data;
+		if (tmp >= m->size || !m->table[tmp]) {
+			break;
+		}
+		m = m->table[tmp];
+	}
+	if (ret) {
+		St_del(key, 0, p);
+	}
+	return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/mars.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,2 @@
+mars.o: mars.c Str.h config.h mars.h op.h IO.h expr.h re.h stack.h \
+  xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/mars.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,21 @@
+#ifndef MARS_H_
+#define MARS_H_
+
+#include "Str.h"
+#include "op.h"
+
+#include <stddef.h>
+
+struct mars {
+	size_t size, length;
+	struct mars **table;
+	struct op *data;
+};
+
+void ma_init(struct mars *);
+void ma_end(struct mars *);
+int ma_enter(struct mars *, const String *, struct op *);
+int ma_exists(const struct mars *, const String *);
+struct op *ma_find(const struct mars *, String *);
+
+#endif /* MARS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/match.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,147 @@
+#include "IO.h"
+#include "Str.h"
+#include "main_io.h"
+#include "main_opt.h"
+#include "match.h"
+#include "re.h"
+#include "run.h"
+#include "val.h"
+
+void do_match(struct val *v, t_regex *re) {
+	size_t mstart, mend;
+	size_t i;
+	size_t bra;
+
+	if (V_EXT_P(v)) {
+		if (Opt.debug & DBG_REGEX) {
+			V_STR(v);
+			io_write_s(Err, "match: <<");
+			io_write_m(Err, ko_ptr(v->ko), ko_length(v->ko));
+			io_write_m(Err, "\n", 1);
+		}
+
+		if (!(io_bufred(v->magic.ext) && re_iomatch(re, v->magic.ext, &mstart, &mend))) {
+			if (Opt.debug & DBG_REGEX) {
+				io_write_s(Err, "...failed\n");
+			}
+			while (Interp.match.length) {
+				--Interp.match.length;
+				v_end(&Interp.match.matches[Interp.match.length]);
+			}
+			v->type = V_UNDEF;
+			return;
+		}
+
+		if (Opt.debug & DBG_REGEX) {
+			io_write_s(Err, "...success!\n");
+		}
+		v_set_n(&Interp.result, mend);
+
+		bra = re_cabra(re);
+		if (Interp.match.size < bra) {
+			Interp.match.matches = xrealloc(Interp.match.matches, Interp.match.size = bra);
+		}
+		if (Interp.m_start.size < bra) {
+			Interp.m_start.index = xrealloc(Interp.m_start.index, Interp.m_start.size = bra);
+		}
+		if (Interp.m_end.size < bra) {
+			Interp.m_end.index = xrealloc(Interp.m_end.index, Interp.m_end.size = bra);
+		}
+
+		for (i = 0; i < bra; ++i) {
+			size_t ms, me;
+
+			if (re_backref(re, i, &ms, &me)) {
+				if (i < Interp.match.length) {
+					v_set_undef(&Interp.match.matches[i]);
+				} else {
+					v_init(&Interp.match.matches[Interp.match.length++]);
+				}
+				Interp.m_start.index[i] = -1;
+				Interp.m_end.index[i] = -1;
+			} else {
+				if (i >= Interp.match.length) {
+					v_init(&Interp.match.matches[Interp.match.length++]);
+				}
+				v_set_m(&Interp.match.matches[i], io_bufptr(v->magic.ext) + ms, me - ms);
+				Interp.m_start.index[i] = ms;
+				Interp.m_end.index[i] = me;
+			}
+		}
+
+		io_read(v->magic.ext, NULL, mend);
+		V_xxx_OFF(v);
+	} else {
+		String tmp;
+
+		V_STR(v);
+		V_xxx_OFF(v);
+
+		St_fake(&tmp, (char *)ko_ptr(v->ko), ko_length(v->ko));
+
+		if (Opt.debug & DBG_REGEX) {
+			io_write_s(Err, "match: \"");
+			io_write(Err, &tmp);
+			io_write_s(Err, "\"\n");
+		}
+
+		if (!re_match(re, &tmp, &mstart, &mend)) {
+			if (Opt.debug & DBG_REGEX) {
+				io_write_s(Err, "...failed\n");
+			}
+			while (Interp.match.length) {
+				--Interp.match.length;
+				v_end(&Interp.match.matches[Interp.match.length]);
+			}
+			v->type = V_UNDEF;
+			return;
+		}
+
+		if (Opt.debug & DBG_REGEX) {
+			io_write_s(Err, "...success!\n");
+		}
+		v_set_n(&Interp.result, mend);
+
+		bra = re_cabra(re);
+		if (Interp.match.size < bra) {
+			Interp.match.matches = xrealloc(Interp.match.matches, Interp.match.size = bra);
+		}
+		if (Interp.m_start.size < bra) {
+			Interp.m_start.index = xrealloc(Interp.m_start.index, Interp.m_start.size = bra);
+		}
+		if (Interp.m_end.size < bra) {
+			Interp.m_end.index = xrealloc(Interp.m_end.index, Interp.m_end.size = bra);
+		}
+
+		for (i = 0; i < bra; ++i) {
+			size_t ms, me;
+
+			if (re_backref(re, i, &ms, &me)) {
+				if (i < Interp.match.length) {
+					v_set_undef(&Interp.match.matches[i]);
+				} else {
+					v_init(&Interp.match.matches[Interp.match.length++]);
+				}
+				Interp.m_start.index[i] = -1;
+				Interp.m_end.index[i] = -1;
+			} else {
+				if (i >= Interp.match.length) {
+					v_init(&Interp.match.matches[Interp.match.length++]);
+				}
+				v_set_m(&Interp.match.matches[i], ko_ptr(v->ko) + ms, me - ms);
+				Interp.m_start.index[i] = ms;
+				Interp.m_end.index[i] = me;
+			}
+		}
+	}
+
+	if (!mstart) {
+		ko_cpy_m(v->ko, "0.", 2);
+		v->num = 0.0;
+		v->type = V_STR_K | V_NUM_K;
+		return;
+	}
+
+	v->num = mstart;
+	v->type = V_NUM_K;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/match.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+match.o: match.c IO.h config.h Str.h main_io.h main_opt.h match.h re.h \
+  val.h kork.h list.h sub.h run.h op.h expr.h stack.h xmalloc.h strhash.h \
+  variable.h text.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/match.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,9 @@
+#ifndef MATCH_H_
+#define MATCH_H_
+
+#include "re.h"
+#include "val.h"
+
+void do_match(struct val *, t_regex *);
+
+#endif /* MATCH_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/op.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,314 @@
+#include "IO.h"
+#include "Str.h"
+#include "expr.h"
+#include "main_label.h"
+#include "mars.h"
+#include "op.h"
+
+#include <ctype.h>
+#include <stddef.h>
+#include <string.h>
+
+void op_init(struct op *op) {
+	St_init(&op->txt);
+	op->type = OP_NOP;
+	op->arg = NULL;
+	op->arh.expr = NULL;
+	op->next = NULL;
+	op->line = 0;
+}
+
+void op_end(struct op *op) {
+	St_clear(&op->txt);
+	free_expr(op->arg);
+	switch (op->type) {
+		case OP_ASSIGN:
+		case OP_CALL_BACK:
+		case OP_MODIFY:
+		case OP_PRINT:
+		case OP_PUTC:
+		case OP_SET_VAL:
+		case OP_TEMP:
+			free_expr(op->arh.expr);
+			break;
+
+		default:
+			break;
+	}
+}
+
+void op_getop(struct op *p) {
+	p->next = NULL;
+
+	switch (ST_FIRSTCHAR(&p->txt)) {
+		case '#':
+			if (ST_INDEX(&p->txt, 1) == '!') {
+				p->type = OP_NOP;
+				break;
+			}
+			p->type = OP_SYSTEM;
+			St_shift(&p->txt);
+			p->arg = get_expr(p);
+			break;
+
+		case 'A':
+			switch (ST_INDEX(&p->txt, 1)) {
+				case 'B':
+					if (St_len(&p->txt) < 5 || St_ncmp_m(&p->txt, "ABRUF", 5)) {
+						goto defualt;
+					}
+					p->type = OP_CALL_BACK;
+					St_del(&p->txt, 0, 5);
+					p->arh.expr = get_iobj(p);
+					p->arg = get_expr(p);
+					break;
+
+				case 'N':
+					if (St_len(&p->txt) < 5 || St_ncmp_m(&p->txt, "ANRUF", 5)) {
+						goto defualt;
+					}
+					p->type = OP_CALL_DYN;
+					St_del(&p->txt, 0, 5);
+					p->arg = get_expr(p);
+					break;
+
+				default:
+					goto defualt;
+			}
+			break;
+
+		case 'C':
+			if (St_len(&p->txt) < 6 || St_ncmp_m(&p->txt, "CLAUDS", 6)) {
+				goto defualt;
+			}
+			p->type = OP_CLOSE;
+			St_del(&p->txt, 0, 6);
+			p->arg = get_expr(p);
+			break;
+
+		case 'E':
+			switch (ST_INDEX(&p->txt, 1)) {
+				case 'L':
+					if (St_len(&p->txt) < 4 || St_ncmp_m(&p->txt, "ELSE", 4)) {
+						goto defualt;
+					}
+					p->type = OP_ELSE;
+					St_del(&p->txt, 0, 4);
+					break;
+
+				case 'N':
+					if (St_len(&p->txt) >= 6 && St_ncmp_m(&p->txt, "END IF", 6) == 0) {
+						p->type = OP_FI;
+						break;
+					}
+					if (St_len(&p->txt) < 3 || St_ncmp_m(&p->txt, "END", 3)) {
+						goto defualt;
+					}
+					p->type = OP_EXIT;
+					St_del(&p->txt, 0, 3);
+					p->arg = get_expr(p);
+					break;
+
+				default:
+					goto defualt;
+			}
+			break;
+
+		case 'F':
+			switch (ST_INDEX(&p->txt, 1)) {
+				case 'I':
+					p->type = OP_FI;
+					break;
+
+				case 'L':
+					if (St_len(&p->txt) < 5 || St_ncmp_m(&p->txt, "FLUSH", 5)) {
+						goto defualt;
+					}
+					p->type = OP_FLUSH;
+					St_del(&p->txt, 0, 5);
+					p->arg = get_expr(p);
+					break;
+
+				default:
+					goto defualt;
+			}
+			break;
+
+		case 'G':
+			if (ST_INDEX(&p->txt, 1) != 'O') {
+				goto defualt;
+			}
+			switch (ST_INDEX(&p->txt, 2)) {
+				case 'F':
+					if (St_len(&p->txt) < 5 || St_ncmp_m(&p->txt, "GOFOR", 5)) {
+						goto defualt;
+					}
+					p->type = OP_GOBACK;
+					St_del(&p->txt, 0, 5);
+					p->arg = get_expr(p);
+					break;
+
+				case 'T':
+					if (St_len(&p->txt) < 4 || St_ncmp_m(&p->txt, "GOTO", 4)) {
+						goto defualt;
+					}
+					p->type = OP_GOTO;
+					St_del(&p->txt, 0, 4);
+					p->arg = get_expr(p);
+					break;
+
+				default:
+					goto defualt;
+			}
+			break;
+
+		case 'I':
+			switch (ST_INDEX(&p->txt, 1)) {
+				case 'A':
+					if (St_len(&p->txt) < 4 || St_ncmp_m(&p->txt, "IACS", 4)) {
+						goto defualt;
+					}
+					p->type = OP_THROW;
+					St_del(&p->txt, 0, 4);
+					p->arg = get_expr(p);
+					break;
+
+				case 'F':
+					p->type = OP_IF;
+					St_del(&p->txt, 0, 2);
+					p->arg = get_expr(p);
+					break;
+
+				default:
+					goto defualt;
+			}
+			break;
+
+		case 'K':
+			if (St_len(&p->txt) < 4 || St_ncmp_m(&p->txt, "KTHX", 4)) {
+				goto defualt;
+			}
+			p->type = OP_RETURN;
+			St_del(&p->txt, 0, 4);
+			p->arg = get_expr(p);
+			break;
+
+		case 'L':
+			if (ST_INDEX(&p->txt, 1) != 'E') {
+				goto defualt;
+			}
+			switch (ST_INDEX(&p->txt, 2)) {
+				case 'E':
+					if (ST_INDEX(&p->txt, 3) != 'T') {
+						goto defualt;
+					}
+					p->type = OP_TEMP;
+					St_del(&p->txt, 0, 4);
+					p->arh.expr = get_lval(p);
+					p->arg = get_expr(p);
+					break;
+
+				case 'T':
+					p->type = OP_ASSIGN;
+					St_del(&p->txt, 0, 3);
+					p->arh.expr = get_lval(p);
+					St_shiftws(&p->txt);
+					if (p->arh.expr && ST_INDEX(&p->txt, 1) == '=' && OPERATOR_P(ST_FIRSTCHAR(&p->txt))) {
+						p->arh.expr->op = expr_binop(ST_FIRSTCHAR(&p->txt));
+						St_del(&p->txt, 0, 2);
+						p->type = OP_MODIFY;
+					}
+					p->arg = get_expr(p);
+					break;
+
+				default:
+					goto defualt;
+			}
+			break;
+
+		case 'N':
+			if (St_len(&p->txt) < 4 || St_ncmp_m(&p->txt, "NEXT", 4)) {
+				goto defualt;
+			} else {
+				size_t tmp;
+				p->type = OP_NOP;
+				St_del(&p->txt, 0, 4);
+				tmp = St_shiftws(&p->txt);
+				if (!(p->next = ma_find(&Mars, &p->txt))) {
+					St_tac_m(&p->txt, "NEXT ", 4 + !!tmp);
+					p->arg = get_expr(p);
+					p->arh.expr = NULL;
+					p->type = OP_PRINT;
+				}
+			}
+			break;
+
+		case 'R':
+			if (ST_INDEX(&p->txt, 1) != 'E') {
+				goto defualt;
+			}
+			switch (ST_INDEX(&p->txt, 2)) {
+				case 'M':
+					p->type = OP_NOP;
+					break;
+
+				case 'S':
+					if (St_len(&p->txt) < 5 || St_ncmp_m(&p->txt, "RESET", 5)) {
+						goto defualt;
+					}
+					p->type = OP_RESET;
+					St_del(&p->txt, 0, 5);
+					p->arg = get_expr(p);
+					break;
+
+				default:
+					goto defualt;
+			}
+			break;
+
+		case 'S':
+			if (St_len(&p->txt) < 3 || St_ncmp_m(&p->txt, "SET", 3)) {
+				goto defualt;
+			}
+			p->type = OP_PUTC;
+			St_del(&p->txt, 0, 3);
+			p->arg = get_iobj(p);
+			p->arh.expr = NULL;
+			St_shiftws(&p->txt);
+			if (ST_FIRSTCHAR(&p->txt) == ',') {
+				St_shift(&p->txt);
+				p->arh.expr = p->arg;
+				p->arg = get_expr(p);
+			}
+			break;
+
+		case 'W':
+			if (St_len(&p->txt) < 4 || St_ncmp_m(&p->txt, "WUNT", 4)) {
+				goto defualt;
+			}
+			p->type = OP_PRINT;
+			St_del(&p->txt, 0, 4);
+			p->arg = get_iobj(p);
+			p->arh.expr = NULL;
+			St_shiftws(&p->txt);
+			if (ST_FIRSTCHAR(&p->txt) != EOF) {
+				p->arh.expr = p->arg;
+				p->arg = get_expr(p);
+			}
+			break;
+
+defualt:
+		default:
+			if ((p->arh.op = ma_find(&Mars, &p->txt))) {
+				p->type = OP_CALL;
+				p->arg = get_expr(p);
+			} else {
+				p->type = OP_PRINT;
+				p->arg = get_expr(p);
+				p->arh.expr = NULL;
+			}
+			break;
+	}
+
+	St_clear(&p->txt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/op.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+op.o: op.c IO.h config.h Str.h expr.h op.h re.h stack.h xmalloc.h \
+  strhash.h val.h kork.h list.h sub.h variable.h main_label.h mars.h \
+  venus.h hash.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/op.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,67 @@
+#ifndef OP_H_
+#define OP_H_
+
+#include "IO.h"
+#include "Str.h"
+#include "expr.h"
+
+#include <stddef.h>
+
+enum t_op {
+	OP_NOP,
+	OP_ASSIGN,
+	OP_CALL,
+	OP_CALL_BACK,
+	OP_CALL_DYN,
+	OP_CLOSE,
+	OP_ELSE,
+	OP_EXIT,
+	OP_FI,
+	OP_FLUSH,
+	OP_GOBACK,
+	OP_GOTO,
+	OP_HANG,
+	OP_IF,
+	OP_MODIFY,
+	OP_PRINT,
+	OP_PUTC,
+	OP_RESET,
+	OP_RETURN,
+	OP_SET_VAL,
+	OP_SYSTEM,
+	OP_TEMP,
+	OP_THROW
+};
+
+struct op {
+	enum t_op type;
+	struct expr *arg;
+	union {
+		struct expr *expr;
+		struct op *op;
+	} arh;
+	struct op *next;
+	String txt;
+	size_t line;
+};
+
+void op_init(struct op *);
+void op_end(struct op *);
+void op_getop(struct op *);
+
+#define OP_1ARG_P(op)                                             \
+(                                                                 \
+ (op) == OP_CALL   || (op) == OP_CALL_DYN || (op) == OP_CLOSE  || \
+ (op) == OP_EXIT   || (op) == OP_FLUSH    || (op) == OP_GOTO   || \
+ (op) == OP_IF     || (op) == OP_RESET    || (op) == OP_RETURN || \
+ (op) == OP_SYSTEM || (op) == OP_THROW                            \
+)
+
+#define OP_2ARG_P(op)                                               \
+(                                                                   \
+ (op) == OP_ASSIGN || (op) == OP_CALL_BACK || (op) == OP_MODIFY  || \
+ (op) == OP_PRINT  || (op) == OP_PUTC      || (op) == OP_SET_VAL || \
+ (op) == OP_TEMP                                                    \
+)
+
+#endif /* OP_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/opt.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,51 @@
+#include "opt.h"
+
+#include <string.h>
+
+int opt_ind;
+int opt_err;
+const char *opt_arg;
+static const char *nextchar;
+
+int opt_get(int argc, char *const *argv, const char *opts) {
+	const char *p;
+
+	if (!nextchar || !*nextchar) {
+		if (opt_ind < argc)
+			++opt_ind;
+		if (opt_ind >= argc || argv[opt_ind][0] != '-' || !argv[opt_ind][1]) {
+			return -1;
+		}
+		if (argv[opt_ind][1] == '-' && argv[opt_ind][2] == '\0') {
+			++opt_ind;
+			return -1;
+		}
+		nextchar = argv[opt_ind] + 1;
+	}
+
+	if ((p = strchr(opts, *nextchar)) && (*p != ':' || p == opts)) {
+		if (p[1] == ':') {
+			if (nextchar[1]) {
+				opt_arg = nextchar + 1;
+			} else if (opt_ind + 1 >= argc) {
+				opt_arg = NULL;
+			} else {
+				opt_arg = argv[++opt_ind];
+			}
+			nextchar = NULL;
+		} else {
+			++nextchar;
+		}
+		return p[0];
+	}
+
+	opt_err = *nextchar++;
+	return '\0';
+}
+
+#if 0
+void opt_reset(void) {
+	opt_ind = 0;
+	nextchar = NULL;
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/opt.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+opt.o: opt.c opt.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/opt.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,13 @@
+#ifndef OPT_H_
+#define OPT_H_
+
+extern int opt_ind;
+extern int opt_err;
+extern const char *opt_arg;
+
+int opt_get(int, char *const *, const char *);
+#if 0
+void opt_reset(void);
+#endif
+
+#endif /* OPT_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/parse.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,210 @@
+#include "IO.h"
+#include "inc.h"
+#include "main.h"
+#include "main_label.h"
+#include "mars.h"
+#include "op.h"
+#include "parse.h"
+#include "text.h"
+#include "venus.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void skipline(IO *f) {
+	int c;
+
+	for (
+			c = io_peek(f, 0);
+			c != EOF && c != '\n';
+			c = io_peek(f, 0)
+		) {
+		if (c == 'R' && io_cmppeek(f, 0, "REM", 3) == 0) {
+			io_read(f, NULL, 3);
+			skipline(f);
+		} else {
+			io_getc(f);
+		}
+	}
+	if (c == '\n') {
+		io_getc(f);
+	}
+}
+
+static IO *open_inc(const char *s, enum io_flags mode) {
+	size_t i;
+	String cur;
+
+	St_init(&cur);
+	for (i = 0; inc_ludes[i]; ++i) {
+		IO *tmp;
+
+		St_cpy_s(&cur, s);
+		St_tac_s(&cur, inc_ludes[i]);
+		if ((tmp = io_open(St_ptr(&cur), mode))) {
+			St_clear(&cur);
+			return tmp;
+		}
+	}
+	St_clear(&cur);
+
+	return NULL;
+}
+
+void parse(IO *f, struct text *text, size_t *line) {
+	int c;
+
+	while ((c = io_peek(f, 0)) != EOF) {
+		struct op node;
+		String label;
+		int is_static = 0;
+
+		for (
+				;
+				c != EOF && c != '\n' && isspace(c);
+				c = io_peek(f, 0)
+				) {
+			io_getc(f);
+		}
+		if (c == EOF) {
+			break;
+		}
+
+		if (io_cmppeek(f, 0, "INSERT", 6) == 0) {
+			size_t start;
+			size_t end;
+			String ifname;
+			IO *ifp;
+			int inc;
+
+			if (io_cmppeek(f, 6, " DA", 3) == 0) {
+				inc = 0;
+				start = 9;
+			} else {
+				inc = 1;
+				start = 6;
+			}
+
+			for (
+					;
+					(c = io_peek(f, start)) != '\n' && isspace(c);
+					++start
+				)
+				;
+			if (c == '\n' || c == EOF) {
+				goto no_insert;
+			}
+			for (
+					end = start + 1;
+					(c = io_peek(f, end)) != '\n' && c != EOF;
+					++end
+				)
+				;
+			for (
+					;
+					end > start && isspace(io_peek(f, end - 1));
+					--end
+				)
+				;
+			if (io_cmppeek(f, end - 4, "HERE", 4)) {
+				goto no_insert;
+			}
+			for (
+					end -= 4;
+					isspace(io_peek(f, end - 1));
+					--end
+				)
+				;
+			io_read(f, NULL, start);
+			St_init(&ifname);
+			if (end > start) {
+				io_read(f, &ifname, end - start);
+			}
+			if (!(ifp = (inc ? open_inc : io_open)(St_ptr(&ifname), IO_READ | IO_BUFFERED))) {
+				fprintf(stderr, "%s: %s: %s\n", Prog, St_ptr(&ifname), strerror(errno));
+				St_clear(&ifname);
+				exit(EXIT_FAILURE);
+			} else {
+				parse(ifp, text, line);
+				io_decr(ifp);
+			}
+			St_clear(&ifname);
+			io_getline(f, NULL);
+			continue;
+		}
+no_insert:
+
+		op_init(&node);
+		node.line = *line;
+
+		St_init(&label);
+		if (io_cmppeek(f, 0, "FOR", 3) == 0) {
+			size_t p;
+			for (p = 3; (c = io_peek(f, p)) != '\n' && isspace(c); ++p)
+				;
+			if (c != EOF && c != '\n') {
+				St_cat_c(&label, c);
+				for (++p; (c = io_peek(f, p)) != EOF && !isspace(c); ++p) {
+					St_cat_c(&label, c);
+				}
+				if (!ma_exists(&Mars, &label)) {
+					is_static = 1;
+					io_read(f, NULL, p);
+				} else {
+					St_zero(&label);
+				}
+			}
+		} else {
+			for (; (c = io_peek(f, 0)) == '0'; io_getc(f))
+				;
+			for (; isdigit(c); c = io_peek(f, 0)) {
+				St_cat_c(&label, c);
+				io_getc(f);
+			}
+		}
+
+		for (
+				c = io_peek(f, 0);
+				c != EOF && c != '\n' && isspace(c);
+				c = io_peek(f, 0)
+			) {
+			io_getc(f);
+		}
+
+		if (io_cmppeek(f, 0, "REM", 3) == 0) {
+			io_read(f, &node.txt, 3);
+			skipline(f);
+		} else {
+			size_t pos;
+
+			io_getline(f, &node.txt);
+			if ((pos = St_rstr_m(&node.txt, "?\?/\n", 4)) + 1u && pos + 4u == St_len(&node.txt)) {
+				String buf;
+				St_init(&buf);
+
+				do {
+					io_getline(f, &buf);
+					St_del(&node.txt, St_len(&node.txt) - 4u, 4);
+					St_cat(&node.txt, &buf);
+				} while ((pos = St_rstr_m(&node.txt, "?\?/\n", 4)) + 1u && pos + 4u == St_len(&node.txt));
+				St_clear(&buf);
+			}
+		}
+
+		{
+			struct op *tmp;
+			tmp = text_push(text, &node);
+			if (is_static) {
+				ma_enter(&Mars, &label, tmp);
+			} else {
+				ve_enter(&Venus, &label, tmp);
+				++*line;
+			}
+		}
+		St_clear(&label);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/parse.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+parse.o: parse.c IO.h config.h Str.h inc.h main.h main_label.h mars.h \
+  op.h expr.h re.h stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h \
+  variable.h venus.h hash.h parse.h text.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/parse.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,11 @@
+#ifndef PARSE_H_
+#define PARSE_H_
+
+#include "IO.h"
+#include "text.h"
+
+#include <stdio.h>
+
+void parse(IO *, struct text *, size_t *);
+
+#endif /* PARSE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/pp.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,957 @@
+#include "IO.h"
+#include "Str.h"
+#include "kork.h"
+#include "list.h"
+#include "main_io.h"
+#include "main_opt.h"
+#include "match.h"
+#include "pp.h"
+#include "re.h"
+#include "run.h"
+#include "val.h"
+#include "xmalloc.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void pp_abs(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, fabs(v->num));
+}
+
+void pp_acos(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, acos(v->num));
+}
+
+void pp_asin(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, asin(v->num));
+}
+
+void pp_atan(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, atan(v->num));
+}
+
+void pp_atan2(struct val *v) {
+	if (!V_LIST_P(v) || li_length(v->magic.list) != 2) {
+		v_set_undef(v);
+		#ifdef EINVAL
+			errno = EINVAL;
+		#endif
+		return;
+	} else {
+		struct val *const a = li_at(v->magic.list, 0);
+		struct val *const b = li_at(v->magic.list, 1);
+		V_NUM(a);
+		V_NUM(b);
+		v_set_n(v, atan2(a->num, b->num));
+	}
+}
+
+void pp_chr(struct val *v) {
+	V_NUM(v);
+	V_xxx_OFF(v);
+	ko_cpy_c(v->ko, (unsigned char)(v->num + .5));
+	v->type = V_STR_K;
+}
+
+void pp_cos(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, cos(v->num));
+}
+
+void pp_defined(struct val *v) {
+	if (v->type != V_UNDEF) {
+		v_set_n(v, 1.0);
+	}
+}
+
+void pp_eof(struct val *v) {
+	if (!V_EXT_P(v)) {
+		v_set_undef(v);
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+	if (io_eof(v->magic.ext)) {
+		v_set_n(v, 1.0);
+	} else {
+		v_set_m(v, "", 0);
+	}
+}
+
+void pp_error(struct val *v) {
+	if (!V_EXT_P(v)) {
+		v_set_undef(v);
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+	if (io_err(v->magic.ext)) {
+		v_set_n(v, 1.0);
+	} else {
+		v_set_m(v, "", 0);
+	}
+}
+
+void pp_escape(struct val *v) {
+	String tmp;
+	size_t i;
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	St_init(&tmp);
+	St_cpy(&tmp, ko_str(v->ko));
+	ko_zero(v->ko);
+	for (i = St_len(&tmp); i; ) {
+		--i;
+		if (!(ST_INDEX(&tmp, i) == '_' || isalnum(ST_INDEX(&tmp, i)))) {
+			ko_cat_c(v->ko, '!');
+		}
+		ko_cat_c(v->ko, ST_INDEX(&tmp, i));
+	}
+	St_clear(&tmp);
+	ko_reverse(v->ko);
+	v->type = V_STR_K;
+}
+
+void pp_getc(struct val *v) {
+	int c;
+
+	if (!V_EXT_P(v)) {
+		V_xxx_OFF(v);
+		v->type = V_UNDEF;
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+	c = io_getc(v->magic.ext);
+	#if EOF != -1
+		if (c == EOF) {
+			c = -1;
+		}
+	#endif
+	v_set_n(v, c);
+}
+
+void pp_getenv(struct val *v) {
+	const char *const tmp = getenv(v_sptr(v, NULL));
+	if (tmp) {
+		v_set_m(v, tmp, strlen(tmp));
+	} else {
+		V_xxx_OFF(v);
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_gets(struct val *v) {
+
+	if (!V_EXT_P(v)) {
+		V_xxx_OFF(v);
+		v->type = V_UNDEF;
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+
+	if (ko_getline(v->magic.ext, v->ko) + 1u) {
+		V_xxx_OFF(v);
+		v->type = V_STR_K;
+	} else {
+		V_xxx_OFF(v);
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_int(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, floor(v->num + .5));
+}
+
+void pp_io(struct val *v) {
+	if (V_EXT_P(v)) {
+		v_set_n(v, 1.0);
+	} else {
+		v_set_m(v, "", 0);
+	}
+}
+
+void pp_length(struct val *v) {
+	if (V_LIST_P(v)) {
+		v_set_n(v, li_length(v->magic.list));
+		return;
+	}
+	v_set_n(v, ko_length(v_kork(v)));
+}
+
+void pp_log(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, log(v->num));
+}
+
+void pp_log10(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, log10(v->num));
+}
+
+void pp_lower(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	ko_lower(v->ko);
+}
+
+void pp_moend(struct val *v) {
+	size_t n;
+
+	V_NUM(v);
+	n = RINT(v->num);
+	if (n < Interp.m_end.size) {
+		v_set_n(v, Interp.m_end.index[n]);
+	} else {
+		v_set_undef(v);
+	}
+}
+
+void pp_mostart(struct val *v) {
+	size_t n;
+
+	V_NUM(v);
+	n = RINT(v->num);
+	if (n < Interp.m_start.size) {
+		v_set_n(v, Interp.m_start.index[n]);
+	} else {
+		v_set_undef(v);
+	}
+}
+
+void pp_neg(struct val *v) {
+	V_NUM(v);
+	V_xxx_OFF(v);
+	v->num = -v->num;
+	v->type = V_NUM_K;
+}
+
+void pp_not(struct val *v) {
+	if (v_true(v)) {
+		v_set_m(v, "", 0);
+	} else {
+		v_set_n(v, 1.0);
+	}
+}
+
+void pp_num(struct val *v) {
+	V_NUM(v);
+	V_xxx_OFF(v);
+	v->type = V_NUM_K;
+}
+
+void pp_open(struct val *v) {
+	IO *fh;
+	struct val *name, *mode;
+	enum io_flags flags;
+
+	if (!V_LIST_P(v) || li_length(v->magic.list) != 2) {
+		v_set_undef(v);
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	name = li_at(v->magic.list, 0);
+	V_STR(name);
+	if (ko_chr(name->ko, '\0') + 1u) {
+		v_set_undef(v);
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	mode = li_at(v->magic.list, 1);
+	flags = 0;
+	V_STR(mode);
+
+	if (ko_chr(mode->ko, 'A') + 1u) {
+		flags |= IO_APPEND;
+		if (ko_chr(mode->ko, '+') + 1u) {
+			flags |= IO_READ;
+		}
+	} else if (ko_chr(mode->ko, 'W') + 1u) {
+		flags |= IO_WRITE | IO_TRUNCATE;
+		if (ko_chr(mode->ko, '+') + 1u) {
+			flags |= IO_READ;
+		}
+	} else {
+		flags |= IO_READ;
+		if (ko_chr(mode->ko, '+') + 1u) {
+			flags |= IO_WRITE;
+		} else if (ko_chr(mode->ko, 'Z') + 1u) {
+			flags |= IO_BUFFERED;
+		}
+	}
+
+	if (ko_chr(mode->ko, 'B') + 1u) {
+		flags |= IO_BINARY;
+	}
+	if ((flags & IO_WRITE || flags & IO_APPEND) && ko_chr(mode->ko, 'F') + 1u) {
+		flags |= IO_AUTOFLUSH;
+	}
+
+	if (!(fh = io_open(ko_szp(name->ko), flags))) {
+		v_set_undef(v);
+		return;
+	}
+
+	v_set_io(v, fh);
+}
+
+void pp_openr(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (ko_chr(v->ko, '\0') + 1u) {
+		v->type = V_UNDEF;
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+	} else if ((v->magic.ext = io_open(ko_szp(v->ko), IO_READ | IO_BINARY))) {
+		v->type = V_EXT_K;
+	} else {
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_openw(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (ko_chr(v->ko, '\0') + 1u) {
+		v->type = V_UNDEF;
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+	} else if ((v->magic.ext = io_open(ko_szp(v->ko), IO_WRITE | IO_TRUNCATE | IO_BINARY | IO_AUTOFLUSH))) {
+		v->type = V_EXT_K;
+	} else {
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_ord(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (ko_length(v->ko)) {
+		v->num = ko_at(v->ko, 0);
+		v->type = V_NUM_K;
+	} else {
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_quote(struct val *v) {
+	String tmp;
+	size_t i;
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	St_init(&tmp);
+	St_cpy_m(&tmp, ko_ptr(v->ko), ko_length(v->ko));
+	ko_zero(v->ko);
+	for (i = 0; i < St_len(&tmp); ++i) {
+		if (!(ST_INDEX(&tmp, i) == '_' || isalnum(ST_INDEX(&tmp, i)))) {
+			ko_cat_c(v->ko, '\\');
+		}
+		ko_cat_c(v->ko, ST_INDEX(&tmp, i));
+	}
+	St_clear(&tmp);
+	v->type = V_STR_K;
+}
+
+void pp_remove(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (ko_chr(v->ko, '\0') + 1u) {
+		v->type = V_UNDEF;
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	if (remove(ko_szp(v->ko))) {
+		v_set_m(v, "", 0);
+	} else {
+		v_set_n(v, 1.0);
+	}
+}
+
+void pp_rename(struct val *v) {
+	struct val *from, *to;
+
+	if (!V_LIST_P(v) || li_length(v->magic.list) != 2) {
+		v_set_undef(v);
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	from = li_at(v->magic.list, 0);
+	V_STR(from);
+	if (ko_chr(from->ko, '\0') + 1u) {
+		v_set_undef(v);
+		#ifdef ENOENT
+			errno = ENOENT;
+		#endif
+		return;
+	}
+
+	to = li_at(v->magic.list, 1);
+	V_STR(to);
+	if (ko_chr(to->ko, '\0') + 1u) {
+		v_set_undef(v);
+		#ifdef EINVAL
+			errno = EINVAL;
+		#endif
+		return;
+	}
+
+	if (rename(ko_szp(from->ko), ko_szp(to->ko))) {
+		v_set_m(v, "", 0);
+	} else {
+		v_set_n(v, 1.0);
+	}
+}
+
+void pp_reverse(struct val *v) {
+	if (V_LIST_P(v)) {
+		li_reverse(v->magic.list);
+		v->type = V_LIST_K;
+		return;
+	}
+	V_STR(v);
+	V_xxx_OFF(v);
+	ko_reverse(v->ko);
+	v->type = V_STR_K;
+}
+
+void pp_seek(struct val *v) {
+	struct val *fh, *tmp;
+	long off;
+	enum io_whence whence = IOS_START;
+
+	if (!V_LIST_P(v) || li_length(v->magic.list) < 2 || li_length(v->magic.list) > 3) {
+		v_set_undef(v);
+		#ifdef EINVAL
+			errno = EINVAL;
+		#endif
+		return;
+	}
+
+	fh = li_at(v->magic.list, 0);
+	if (!V_EXT_P(fh)) {
+		v_set_undef(v);
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+
+	tmp = li_at(v->magic.list, 1);
+	V_NUM(tmp);
+	off = RINT(tmp->num);
+
+	if ((tmp = li_at(v->magic.list, 2))) {
+		V_NUM(tmp);
+		switch ((long)RINT(tmp->num)) {
+			case 0: whence = IOS_START; break;
+			case 1: whence = IOS_CUR;   break;
+			case 2: whence = IOS_END;   break;
+			default:
+				v_set_undef(v);
+				#ifdef EINVAL
+					errno = EINVAL;
+				#endif
+				return;
+		}
+	}
+
+	v_set_n(v, io_seek(fh->magic.ext, off, whence));
+}
+
+void pp_sin(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, sin(v->num));
+}
+
+void pp_sqrt(struct val *v) {
+	V_NUM(v);
+	V_xxx_OFF(v);
+	v->num = sqrt(v->num);
+	v->type = V_NUM_K;
+}
+
+void pp_str(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	v->type |= V_STR_K;
+}
+
+void pp_tan(struct val *v) {
+	V_NUM(v);
+	v_set_n(v, tan(v->num));
+}
+
+void pp_tell(struct val *v) {
+	if (!V_EXT_P(v)) {
+		v_set_undef(v);
+		#ifdef EBADF
+			errno = EBADF;
+		#endif
+		return;
+	}
+	v_set_n(v, io_tell(v->magic.ext));
+}
+
+void pp_typeof(struct val *v) {
+	if (V_SUB_P(v)) {
+		v_set_m(v, "stuff", 5);
+	} else if (V_EXT_P(v)) {
+		v_set_m(v, "stream", 6);
+	} else if (V_LIST_P(v)) {
+		v_set_m(v, "list", 4);
+	} else if (V_NUM_P(v)) {
+		v_set_m(v, "number", 6);
+	} else if (V_STR_P(v)) {
+		v_set_m(v, "string", 6);
+	} else {
+		v_set_m(v, "nothing", 7);
+	}
+}
+
+void pp_upper(struct val *v) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	ko_upper(v->ko);
+}
+
+#define NUMB(expr)    \
+do {                  \
+	V_NUM(b);         \
+	V_NUM(v);         \
+	v_set_n(v, expr); \
+} while (0)
+
+#define ARITH(op) NUMB(v->num op b->num)
+
+void pp_add(struct val *v, struct val *b) {
+	ARITH(+);
+}
+
+#define LOGIC(init, cond)  \
+do {                       \
+	init;                  \
+	if (cond) {            \
+		v_set_n(v, 1.0);   \
+	} else {               \
+		v_set_m(v, "", 0); \
+	}                      \
+} while (0)
+
+void pp_and(struct val *v, struct val *b) {
+	LOGIC((void)0, v_true(v) && v_true(b));
+}
+
+void pp_comma(struct val *v, struct val *b) {
+	v_set(v, b);
+}
+
+void pp_concat(struct val *v, struct val *b) {
+	v_cat(v, b);
+}
+
+void pp_div(struct val *v, struct val *b) {
+	ARITH(/);
+}
+
+#define CMP_LS(op) LOGIC((void)0, v_cmp_ls(v, b) op 0)
+
+void pp_eq(struct val *v, struct val *b) {
+	CMP_LS(==);
+}
+
+#define CMP_N(op) LOGIC(V_NUM(v); V_NUM(b), v->num op b->num)
+
+void pp_eq_n(struct val *v, struct val *b) {
+	if (V_EXT_P(v)) {
+		if (V_EXT_P(b)) {
+			LOGIC((void)0, v->magic.ext == b->magic.ext);
+		} else if (V_SUB_P(b)) {
+			goto false_cmp;
+		} else {
+			goto default_cmp;
+		}
+	} else if (V_SUB_P(v)) {
+		if (V_SUB_P(b)) {
+			LOGIC((void)0, v->magic.sub == b->magic.sub);
+		} else if (V_EXT_P(b)) false_cmp: {
+			LOGIC((void)0, 0);
+		} else {
+			goto default_cmp;
+		}
+	} else default_cmp: {
+		CMP_N(==);
+	}
+}
+
+static const char xdigits[] = "0123456789" "abcdefghijklmnopqrstuvwxyz";
+
+void pp_frombase(struct val *v, struct val *b) {
+	double p;
+	size_t i;
+	int base, sign;
+	const char *tmp;
+
+	V_NUM(b);
+	V_STR(v);
+	V_xxx_OFF(v);
+	if (floor(b->num + .5) < 2. || floor(b->num + .5) > 36.) {
+		v->type = V_UNDEF;
+		return;
+	}
+	base = floor(b->num + .5);
+	ko_shiftws(v->ko);
+
+	sign = 0;
+	if (ko_at(v->ko, 0) == '-') {
+		sign = 1;
+		ko_shift(v->ko, 1);
+	} else if (ko_at(v->ko, 0) == '+') {
+		ko_shift(v->ko, 1);
+	}
+
+	p = 0.0;
+	for (i = 0; i < ko_length(v->ko); ++i) {
+		if ((tmp = strchr(xdigits, tolower(ko_at(v->ko, i)))) &&
+				tmp - xdigits < base) {
+			p *= base;
+			p += tmp - xdigits;
+		} else {
+			break;
+		}
+	}
+
+	if (i < ko_length(v->ko)) {
+		if (ko_at(v->ko, i) == '.') {
+			double shift = 1.0;
+
+			for (++i; i < ko_length(v->ko); ++i) {
+				if ((tmp = strchr(xdigits, tolower(ko_at(v->ko, i)))) &&
+						tmp - xdigits < base) {
+					shift *= base;
+					p *= base;
+					p += tmp - xdigits;
+				} else {
+					break;
+				}
+			}
+
+			p /= shift;
+		}
+	}
+
+	if (sign) {
+		p = -p;
+	}
+	v_set_n(v, p);
+}
+
+void pp_gt(struct val *v, struct val *b) {
+	CMP_LS(>);
+}
+
+void pp_gt_n(struct val *v, struct val *b) {
+	CMP_N(>);
+}
+
+void pp_lt(struct val *v, struct val *b) {
+	CMP_LS(<);
+}
+
+void pp_lt_n(struct val *v, struct val *b) {
+	CMP_N(<);
+}
+
+void pp_match(struct val *v, struct val *b) {
+	t_regex *re;
+
+	V_STR(b);
+	re = re_compile(ko_str(b->ko));
+	do_match(v, re);
+	re_free(re);
+}
+
+void pp_mod(struct val *v, struct val *b) {
+	NUMB(fmod(v->num, b->num));
+}
+
+void pp_mult(struct val *v, struct val *b) {
+	ARITH(*);
+}
+
+void pp_ne(struct val *v, struct val *b) {
+	CMP_LS(!=);
+}
+
+void pp_ne_n(struct val *v, struct val *b) {
+	if (V_EXT_P(v)) {
+		if (V_EXT_P(b)) {
+			LOGIC((void)0, v->magic.ext != b->magic.ext);
+		} else if (V_SUB_P(b)) {
+			goto true_cmp;
+		} else {
+			goto default_cmp;
+		}
+	} else if (V_SUB_P(v)) {
+		if (V_SUB_P(b)) {
+			LOGIC((void)0, v->magic.sub != b->magic.sub);
+		} else if (V_EXT_P(b)) true_cmp: {
+			LOGIC((void)0, 1);
+		} else {
+			goto default_cmp;
+		}
+	} else default_cmp: {
+		CMP_N(!=);
+	}
+}
+
+void pp_or(struct val *v, struct val *b) {
+	LOGIC((void)0, v_true(v) || v_true(b));
+}
+
+void pp_pop(struct val *v, struct val *b) {
+	long p;
+
+	V_NUM(b);
+	p = RINT(b->num);
+
+	if (V_LIST_P(v)) {
+		v->type = V_LIST_K;
+		if (p < 0) {
+			p += li_length(v->magic.list);
+		}
+		if (p < 0) {
+			p = 0;
+		}
+		if ((size_t)p >= li_length(v->magic.list)) {
+			return;
+		}
+		li_trunc(v->magic.list, p);
+		return;
+	}
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	v->type = V_STR_K;
+	if (p < 0) {
+		p += ko_length(v->ko);
+	}
+	if (p < 0) {
+		p = 0;
+	}
+	if ((size_t)p >= ko_length(v->ko)) {
+		return;
+	}
+	ko_trunc(v->ko, p);
+}
+
+void pp_pow(struct val *v, struct val *b) {
+	NUMB(pow(v->num, b->num));
+}
+
+void pp_read(struct val *v, struct val *b) {
+	long n;
+
+	if (V_SUB_P(v)) {
+		const size_t curdepth = depth_get();
+		stack_store(&Interp.arg, b);
+		eval_into(sub_expr(v->magic.sub), v);
+		depth_restore(curdepth);
+		return;
+	}
+
+	if (V_EXT_P(v)) {
+		V_NUM(b);
+		ko_read(v->magic.ext, v->ko, b->num < 0.0 ? (size_t)-1 : b->num + .5);
+		V_xxx_OFF(v);
+		v->type = V_STR_K;
+		return;
+	}
+
+	if (V_LIST_P(v)) {
+		struct val *ptr;
+		V_NUM(b);
+		n = RINT(b->num);
+		if (n < 0) {
+			n += li_length(v->magic.list);
+		}
+
+		if (n >= 0 && (ptr = li_at(v->magic.list, n))) {
+			struct list *tmp = li_dup(v->magic.list);
+			v_set(v, ptr);
+			li_delete(tmp);
+		} else {
+			v_set_undef(v);
+		}
+		return;
+	}
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	V_NUM(b);
+	n = RINT(b->num);
+	if (n < 0) {
+		n += ko_length(v->ko);
+	}
+	if (n >= 0 && (size_t)n < ko_length(v->ko)) {
+		ko_shift(v->ko, n);
+		ko_trunc(v->ko, 1);
+		v->type = V_STR_K;
+	} else {
+		v->type = V_UNDEF;
+	}
+}
+
+void pp_shift(struct val *v, struct val *b) {
+	long p;
+
+	V_NUM(b);
+	p = RINT(b->num);
+
+	if (V_LIST_P(v)) {
+		v->type = V_LIST_K;
+		if (p < 0) {
+			p += li_length(v->magic.list);
+		}
+		if (p <= 0) {
+			return;
+		}
+		if ((size_t)p >= li_length(v->magic.list)) {
+			li_zero(v->magic.list);
+		} else {
+			li_shift(v->magic.list, p);
+		}
+		return;
+	}
+
+	V_STR(v);
+	V_xxx_OFF(v);
+	v->type = V_STR_K;
+	if (p < 0) {
+		p += ko_length(v->ko);
+	}
+	if (p <= 0) {
+		return;
+	}
+	if ((size_t)p >= ko_length(v->ko)) {
+		ko_zero(v->ko);
+	} else {
+		ko_shift(v->ko, p);
+	}
+}
+
+void pp_sub(struct val *v, struct val *b) {
+	ARITH(-);
+}
+
+void pp_tobase(struct val *v, struct val *b) {
+	double p;
+	int base, sign;
+	size_t i;
+
+	V_NUM(b);
+	V_NUM(v);
+	V_xxx_OFF(v);
+
+	if (floor(b->num + .5) < 2. || floor(b->num + .5) > 36.) {
+		v->type = V_UNDEF;
+		return;
+	}
+	base = floor(b->num + .5);
+
+	sign = 0;
+	if (v->num < 0.) {
+		v->num = -v->num;
+		sign = 1;
+	}
+
+	p = v->num;
+	ko_zero(v->ko);
+
+	if (floor(p) < p) {
+		double foo, bar;
+		size_t rounds;
+
+		foo = 1 + floor(log(pow(10, DBL_DIG)) / log(base));
+		bar = p > 0. ? 1 + floor(log(p) / log(base)) : 0.;
+		if (bar >= foo) {
+			rounds = 0;
+		} else {
+			rounds = foo - bar;
+		}
+
+		for (i = 0; i < rounds; ++i) {
+			p *= base;
+			if (floor(p) >= p) {
+				++i;
+				break;
+			}
+		}
+
+		p = floor(p);
+		while (i--) {
+			int c;
+			c = fmod(p, base);
+			p = floor(p / base);
+			if (c >= 0) {
+				ko_cat_c(v->ko, xdigits[c]);
+			}
+		}
+	}
+
+	while (ko_at(v->ko, 0) == '0') {
+		ko_shift(v->ko, 1);
+	}
+
+	if (ko_length(v->ko)) {
+		ko_cat_c(v->ko, '.');
+	}
+
+	while (p > 0.) {
+		int c;
+		c = fmod(p, base);
+		p = floor(p / base);
+		if (c >= 0) {
+			ko_cat_c(v->ko, xdigits[c]);
+		}
+	}
+
+	if (!ko_length(v->ko) || ko_lastchar(v->ko) == '.') {
+		ko_cat_c(v->ko, '0');
+	}
+	if (sign) {
+		ko_cat_c(v->ko, '-');
+	}
+
+	v->type = V_STR_K;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/pp.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+pp.o: pp.c IO.h config.h Str.h kork.h list.h main_io.h main_opt.h match.h \
+  re.h val.h sub.h pp.h run.h op.h expr.h stack.h xmalloc.h strhash.h \
+  variable.h text.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/pp.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,81 @@
+#ifndef PP_H_
+#define PP_H_
+
+#include "val.h"
+
+#define DECL_PP1(name) void name(struct val *)
+
+DECL_PP1(pp_abs);
+DECL_PP1(pp_acos);
+DECL_PP1(pp_asin);
+DECL_PP1(pp_atan);
+DECL_PP1(pp_atan2);
+DECL_PP1(pp_chr);
+DECL_PP1(pp_cos);
+DECL_PP1(pp_defined);
+DECL_PP1(pp_eof);
+DECL_PP1(pp_error);
+DECL_PP1(pp_escape);
+DECL_PP1(pp_getc);
+DECL_PP1(pp_getenv);
+DECL_PP1(pp_gets);
+DECL_PP1(pp_int);
+DECL_PP1(pp_io);
+DECL_PP1(pp_length);
+DECL_PP1(pp_log);
+DECL_PP1(pp_log10);
+DECL_PP1(pp_lower);
+DECL_PP1(pp_moend);
+DECL_PP1(pp_mostart);
+DECL_PP1(pp_neg);
+DECL_PP1(pp_not);
+DECL_PP1(pp_num);
+DECL_PP1(pp_open);
+DECL_PP1(pp_openr);
+DECL_PP1(pp_openw);
+DECL_PP1(pp_ord);
+DECL_PP1(pp_quote);
+DECL_PP1(pp_remove);
+DECL_PP1(pp_rename);
+DECL_PP1(pp_reverse);
+DECL_PP1(pp_seek);
+DECL_PP1(pp_sin);
+DECL_PP1(pp_sqrt);
+DECL_PP1(pp_str);
+DECL_PP1(pp_tan);
+DECL_PP1(pp_tell);
+DECL_PP1(pp_typeof);
+DECL_PP1(pp_upper);
+
+#undef DECL_PP1
+
+#define DECL_PP2(name) void name(struct val *, struct val *)
+
+DECL_PP2(pp_add);
+DECL_PP2(pp_and);
+DECL_PP2(pp_comma);
+DECL_PP2(pp_concat);
+DECL_PP2(pp_div);
+DECL_PP2(pp_eq);
+DECL_PP2(pp_eq_n);
+DECL_PP2(pp_frombase);
+DECL_PP2(pp_gt);
+DECL_PP2(pp_gt_n);
+DECL_PP2(pp_lt);
+DECL_PP2(pp_lt_n);
+DECL_PP2(pp_match);
+DECL_PP2(pp_mod);
+DECL_PP2(pp_mult);
+DECL_PP2(pp_ne);
+DECL_PP2(pp_ne_n);
+DECL_PP2(pp_or);
+DECL_PP2(pp_pop);
+DECL_PP2(pp_pow);
+DECL_PP2(pp_read);
+DECL_PP2(pp_shift);
+DECL_PP2(pp_sub);
+DECL_PP2(pp_tobase);
+
+#undef DECL_PP2
+
+#endif /* PP_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/random.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,27 @@
+#include "config.h"
+#include "random.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+void randseed(void) {
+	unsigned seed;
+
+	seed = time(NULL);
+	#if HAVE_DEV_URANDOM_P
+	{
+		FILE *fp;
+
+		if ((fp = fopen("/dev/urandom", "rb"))) {
+			fread(&seed, sizeof seed, 1, fp);
+			fclose(fp);
+		}
+	}
+	#endif
+	srand(seed);
+}
+
+double randval(void) {
+	return rand() / (RAND_MAX + 1.0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/random.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+random.o: random.c config.h random.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/random.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,7 @@
+#ifndef RANDOM_H_
+#define RANDOM_H_
+
+void randseed(void);
+double randval(void);
+
+#endif /* RANDOM_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/re.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,2661 @@
+#include "config.h"
+#include "IO.h"
+#include "Str.h"
+#include "hash.h"
+#include "main_io.h"
+#include "main_opt.h"
+#include "re.h"
+#include "xmalloc.h"
+#include "zz.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+enum {CLASS_SIZE = UCHAR_MAX / CHAR_BIT + 1u};
+
+static unsigned char dc_word[CLASS_SIZE];
+static unsigned char dc_alpha[CLASS_SIZE];
+static unsigned char dc_cntrl[CLASS_SIZE];
+static unsigned char dc_digit[CLASS_SIZE];
+static unsigned char dc_lower[CLASS_SIZE];
+static unsigned char dc_print[CLASS_SIZE];
+static unsigned char dc_space[CLASS_SIZE];
+static unsigned char dc_upper[CLASS_SIZE];
+static unsigned char dc_xdigit[CLASS_SIZE];
+
+static void fill(unsigned char *v, int (*pred)(int)) {
+	unsigned char c;
+
+	for (c = UCHAR_MAX; c; --c) {
+		v[c / CHAR_BIT] |= !!pred(c) << c % CHAR_BIT;
+	}
+	v[0] |= !!pred(0);
+}
+
+static void nfill(unsigned char *v, int (*pred)(int)) {
+	unsigned char c;
+
+	for (c = UCHAR_MAX; c; --c) {
+		v[c / CHAR_BIT] |= !pred(c) << c % CHAR_BIT;
+	}
+	v[0] |= !pred(0);
+}
+
+ATTR_CONST
+static int siword(int c) {
+	return c == '_' || isalnum(c);
+}
+
+enum {MAGIC = 23};
+
+static Hash rcache;
+static String cached[MAGIC];
+static size_t cachfill, cachlast;
+
+static size_t hash(const void *s, size_t seed) {
+	return St_hash(s, seed);
+}
+
+static int compar(const void *a, const void *b) {
+	return St_cmp(a, b);
+}
+
+enum t_re_node {
+	RE_ALTER,
+	RE_ANYCH,
+	RE_AT_BOL,
+	RE_AT_BOS,
+	RE_AT_EOL,
+	RE_AT_EOS,
+	RE_AT_MATCH,
+	RE_AT_NOMATCH,
+	RE_AT_NWBOUND,
+	RE_AT_WBOUND,
+	RE_BACKCHECK,
+	RE_BACKREF,
+	RE_BEGIN_CAPTURE,
+	RE_CLASS,
+	RE_DEFCLASS,
+	RE_END_CAPTURE,
+	RE_INDEP,
+	RE_LITERAL,
+	RE_N_CLASS,
+	RE_N_DEFCLASS,
+	RE_PARTIAL,
+	RE_REP_FRUGAL,
+	RE_REP_GREEDY,
+	RE_SELECT,
+	RE_STUFF_FRUGAL,
+	RE_STUFF_GREEDY,
+	RE_XREP_FRUGAL,
+	RE_XREP_GREEDY
+};
+
+#define CMN_HDR  enum t_re_node type; union my_node *next
+
+typedef union my_node {
+	struct {
+		CMN_HDR;
+	} x;
+	struct {
+		CMN_HDR;
+		union my_node *arg;
+	} alter;
+	struct {
+		CMN_HDR;
+		size_t n;
+	} backref;
+	struct {
+		CMN_HDR;
+		size_t n;
+	} capture;
+	struct {
+		CMN_HDR;
+		unsigned char vec[CLASS_SIZE];
+	} class;
+	struct {
+		CMN_HDR;
+		unsigned char *vec;
+	} defclass;
+	struct {
+		CMN_HDR;
+		union my_node *arg;
+	} indep;
+	struct {
+		CMN_HDR;
+		unsigned char *buf;
+		size_t len;
+	} literal;
+	struct {
+		CMN_HDR;
+		union my_node *arg;
+		size_t min, max;
+		size_t n;
+	} rep;
+	struct {
+		CMN_HDR;
+		union my_node *arg;
+		union my_node *cond;
+	} select;
+} re_node;
+
+#include "re_block.c.h"
+
+enum re_flags {
+	FL_NONE = 0,
+	FL_ANCHOR_START = 1
+};
+
+struct cap_state {
+	size_t start, end, pending;
+};
+
+struct my_regex {
+	re_node *start;
+	size_t repets;
+	size_t *repbuf;
+	t_block alloc;
+	size_t captures;
+	struct cap_state *cap_ptr;
+	size_t refs;
+	enum re_flags flags;
+	size_t minlen;
+};
+
+static void delv(void *v) {
+	re_free(v);
+}
+
+void re_init(void) {
+	fill(dc_word, siword);
+	fill(dc_alpha, isalpha);
+	fill(dc_cntrl, iscntrl);
+	fill(dc_digit, isdigit);
+	fill(dc_lower, islower);
+	fill(dc_print, isprint);
+	fill(dc_space, isspace);
+	fill(dc_upper, isupper);
+	fill(dc_xdigit, isxdigit);
+
+	h_init(&rcache, hash, compar, NULL, delv);
+}
+
+void re_end(void) {
+	h_end(&rcache);
+	while (cachfill) {
+		--cachfill;
+		St_clear(&cached[cachfill]);
+	}
+	cachlast = 0;
+}
+
+
+enum   {ESCAPE     =        '!'};
+#define MEATCHARP(c) strchr("!|({<[]>})*+?:.'^$`", c)
+#define ENDBRANCHP(c) strchr("|({<[", c)
+
+struct parse_context {
+	t_regex *base;
+	t_block *block;
+	const String *s;
+	size_t *p;
+};
+
+static void skipspace(const String *const s, size_t *const p) {
+restart:
+	while (isspace(ST_INDEX(s, *p))) {
+		--*p;
+	}
+	if (ST_INDEX(s, *p) == ']' && ST_INDEX(s, *p - 1u) == '#') {
+		*p -= 2u;
+		while (ST_INDEX(s, *p) != EOF) {
+			if (ST_INDEX(s, *p) == '[') {
+				--*p;
+				goto restart;
+			}
+			if (ST_INDEX(s, *p) == ESCAPE && ST_INDEX(s, *p - 1u) != EOF) {
+				--*p;
+			}
+			--*p;
+		}
+	}
+}
+
+#define PRAZ(name) static re_node *name(const struct parse_context *con, re_node *next, re_node *curolan_zebeth)
+#define SNORK const String *const s = con->s; size_t *const p = con->p
+#define CALL(name, nextn, glob) name(con, nextn, glob)
+#define TAILC(name) return CALL(name, next, curolan_zebeth)
+
+PRAZ(quantifire);
+PRAZ(branch);
+PRAZ(alternation);
+
+PRAZ(atom) {
+	SNORK;
+	(void)curolan_zebeth;
+
+	skipspace(s, p);
+	switch (ST_INDEX(s, *p)) {
+		case '(':
+		case '<':
+		case '{':
+		case '[':
+		case EOF: {
+			re_node *cur = bl_node(con->block);
+			cur->literal.type = RE_LITERAL;
+			cur->literal.next = next;
+			cur->literal.buf = NULL;
+			cur->literal.len = 0;
+			return cur;
+		}
+
+		case ESCAPE: {
+			re_node *cur = bl_node(con->block);
+			--*p;
+			if (isdigit(ST_INDEX(s, *p))) {
+				do {
+					--*p;
+				} while (isdigit(ST_INDEX(s, *p)));
+				cur->backref.type = RE_BACKREF;
+				cur->backref.next = next;
+				cur->backref.n = strtoul(St_ptr(s) + *p + 1, NULL, 10);
+			} else if (isalpha(ST_INDEX(s, *p))) {
+				switch (ST_INDEX(s, *p)) {
+					case 'A':
+						cur->x.type = RE_AT_BOL;
+						cur->x.next = next;
+						--*p;
+						break;
+					case 'Z':
+						cur->x.type = RE_AT_EOL;
+						cur->x.next = next;
+						--*p;
+						break;
+
+					case 'b':
+						cur->x.type = RE_AT_WBOUND;
+						cur->x.next = next;
+						--*p;
+						break;
+					case 'B':
+						cur->x.type = RE_AT_NWBOUND;
+						cur->x.next = next;
+						--*p;
+						break;
+
+					case 'c':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_cntrl;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'C':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_cntrl;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					case 'd':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_digit;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'D':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_digit;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					case 'l':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_lower;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'L':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_lower;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					case 'p':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_print;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'P':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_print;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					case 'q':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_alpha;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'Q':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_alpha;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					case 's':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_space;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'S':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_space;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					case 'u':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_upper;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'U':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_upper;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					case 'w':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_word;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'W':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_word;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					case 'x':
+						cur->defclass.type = RE_DEFCLASS;
+						cur->defclass.vec = dc_xdigit;
+						cur->defclass.next = next;
+						--*p;
+						break;
+					case 'X':
+						cur->defclass.type = RE_N_DEFCLASS;
+						cur->defclass.vec = dc_xdigit;
+						cur->defclass.next = next;
+						--*p;
+						break;
+
+					default:
+						goto default_escape;
+				}
+
+			} else default_escape: {
+				cur->literal.type = RE_LITERAL;
+				cur->literal.next = next;
+				cur->literal.buf = bl_string(con->block, cur->literal.len = 1);
+				if (ST_INDEX(s, *p) == EOF) {
+					cur->literal.buf[0] = ESCAPE;
+				} else {
+					cur->literal.buf[0] = ST_INDEX(s, *p);
+					--*p;
+				}
+			}
+			return cur;
+		}
+
+		case ')': {
+			re_node *cur;
+			--*p;
+			cur = CALL(alternation, next, next);
+			skipspace(s, p);
+			if (ST_INDEX(s, *p) == '(') {
+				--*p;
+			}
+			return cur;
+		}
+
+		case ']': {
+			re_node *cur;
+			switch (ST_INDEX(s, *p - 1u)) {
+				case '&':
+					*p -= 2u;
+					cur = bl_node(con->block);
+					cur->indep.type = RE_AT_MATCH;
+					cur->indep.next = next;
+					cur->indep.arg = CALL(alternation, NULL, NULL);
+					break;
+
+				case '^':
+					*p -= 2u;
+					cur = bl_node(con->block);
+					cur->indep.type = RE_AT_NOMATCH;
+					cur->indep.next = next;
+					cur->indep.arg = CALL(alternation, NULL, NULL);
+					break;
+
+				case '?':
+					*p -= 2u;
+					cur = bl_node(con->block);
+					cur->select.type = RE_SELECT;
+					cur->select.cond = CALL(quantifire, NULL, NULL);
+					cur->select.arg = CALL(branch, next, next);
+					skipspace(s, p);
+					if (ST_INDEX(s, *p) == '|') {
+						--*p;
+						cur->select.next = CALL(alternation, next, next);
+					} else {
+						cur->select.next = next;
+					}
+					break;
+
+				case '0': case '1': case '2': case '3': case '4':
+				case '5': case '6': case '7': case '8': case '9': {
+					size_t i;
+
+					for (i = *p - 2u; isdigit(ST_INDEX(s, i)); --i)
+						;
+					if (ST_INDEX(s, i) != '[') {
+						goto std_atom;
+					}
+					cur = bl_node(con->block);
+					cur->backref.type = RE_BACKCHECK;
+					cur->backref.next = next;
+					cur->backref.n = strtoul(St_ptr(s) + i + 1u, NULL, 10);
+					*p = i - 1u;
+
+					return cur;
+				}
+
+				default:
+					goto std_atom;
+			}
+
+			skipspace(s, p);
+			if (ST_INDEX(s, *p) == '[') {
+				--*p;
+			}
+			return cur;
+		}
+
+		case '>': {
+			re_node *cur = bl_node(con->block);
+			--*p;
+			cur->indep.type = RE_INDEP;
+			cur->indep.next = next;
+			cur->indep.arg = CALL(alternation, NULL, NULL);
+			skipspace(s, p);
+			if (ST_INDEX(s, *p) == '<') {
+				--*p;
+			}
+			return cur;
+		}
+
+		case '}': {
+			re_node *begin = bl_node(con->block);
+			re_node *end = bl_node(con->block);
+			--*p;
+			begin->capture.n = end->capture.n = con->base->captures++;
+			end->capture.type = RE_END_CAPTURE;
+			end->capture.next = next;
+			begin->capture.type = RE_BEGIN_CAPTURE;
+			begin->capture.next = CALL(alternation, end, end);
+			skipspace(s, p);
+			if (ST_INDEX(s, *p) == '{') {
+				--*p;
+			}
+			return begin;
+		}
+
+		case '^': {
+			re_node *cur = bl_node(con->block);
+			cur->x.type = RE_AT_BOS;
+			cur->x.next = next;
+			--*p;
+			return cur;
+		}
+
+		case '$': {
+			re_node *cur = bl_node(con->block);
+			cur->x.type = RE_AT_EOS;
+			cur->x.next = next;
+			--*p;
+			return cur;
+		}
+
+		case '.': {
+			re_node *cur = bl_node(con->block);
+			cur->x.type = RE_ANYCH;
+			cur->x.next = next;
+			--*p;
+			return cur;
+		}
+
+		case '`': {
+			String tmp;
+			int c;
+			re_node *cur = bl_node(con->block);
+			cur->literal.type = RE_PARTIAL;
+			cur->literal.next = next;
+
+			--*p;
+			St_init(&tmp);
+			while (
+				(c = ST_INDEX(s, *p)) != EOF &&
+				c != '`'
+			) {
+				--*p;
+				if (c == ESCAPE && ST_INDEX(s, *p) != EOF) {
+					c = ST_INDEX(s, *p);
+					--*p;
+				}
+				St_cat_c(&tmp, c);
+			}
+			if (c == '`') {
+				--*p;
+			}
+			St_reverse(&tmp);
+
+			cur->literal.buf = bl_string(con->block, cur->literal.len = St_len(&tmp));
+			memcpy(cur->literal.buf, St_ptr(&tmp), St_len(&tmp));
+			St_clear(&tmp);
+
+			return cur;
+		}
+
+		case '\'': {
+			int c;
+			re_node *cur = bl_node(con->block);
+			cur->class.type = RE_CLASS;
+			cur->class.next = next;
+			memset(cur->class.vec, '\0', sizeof cur->class.vec);
+			for (--*p; (c = ST_INDEX(s, *p)) != EOF && c != '\''; --*p) {
+				if (c == ESCAPE) {
+					switch (c = ST_INDEX(s, *p - 1)) {
+						case EOF:
+							cur->class.vec[ESCAPE / CHAR_BIT] |= 1u << ESCAPE % CHAR_BIT;
+							break;
+
+						case 'c':
+							fill(cur->class.vec, iscntrl);
+							--*p;
+							break;
+						case 'C':
+							nfill(cur->class.vec, iscntrl);
+							--*p;
+							break;
+
+						case 'd':
+							fill(cur->class.vec, isdigit);
+							--*p;
+							break;
+						case 'D':
+							nfill(cur->class.vec, isdigit);
+							--*p;
+							break;
+
+						case 'l':
+							fill(cur->class.vec, islower);
+							--*p;
+							break;
+						case 'L':
+							nfill(cur->class.vec, islower);
+							--*p;
+							break;
+
+						case 'p':
+							fill(cur->class.vec, isprint);
+							--*p;
+							break;
+						case 'P':
+							nfill(cur->class.vec, isprint);
+							--*p;
+							break;
+
+						case 'q':
+							fill(cur->class.vec, isalpha);
+							--*p;
+							break;
+						case 'Q':
+							nfill(cur->class.vec, isalpha);
+							--*p;
+							break;
+
+						case 's':
+							fill(cur->class.vec, isspace);
+							--*p;
+							break;
+						case 'S':
+							nfill(cur->class.vec, isspace);
+							--*p;
+							break;
+
+						case 'u':
+							fill(cur->class.vec, isupper);
+							--*p;
+							break;
+						case 'U':
+							nfill(cur->class.vec, isupper);
+							--*p;
+							break;
+
+						case 'w':
+							fill(cur->class.vec, siword);
+							--*p;
+							break;
+						case 'W':
+							nfill(cur->class.vec, siword);
+							--*p;
+							break;
+
+						case 'x':
+							fill(cur->class.vec, isxdigit);
+							--*p;
+							break;
+						case 'X':
+							nfill(cur->class.vec, isxdigit);
+							--*p;
+							break;
+
+						default:
+							cur->class.vec[c / CHAR_BIT] |= 1u << c % CHAR_BIT;
+							--*p;
+							break;
+					}
+				} else {
+					int b;
+					if (ST_INDEX(s, *p - 1) == '-' && (b = ST_INDEX(s, *p - 2)) != EOF) {
+						if (b == ESCAPE && (b = ST_INDEX(s, *p - 3)) == EOF) {
+							b = ESCAPE;
+						}
+						if (b > c) {
+							goto normal_char;
+						}
+						for (; b < c; ++b) {
+							cur->class.vec[b / CHAR_BIT] |= 1u << b % CHAR_BIT;
+						}
+						cur->class.vec[b / CHAR_BIT] |= 1u << b % CHAR_BIT;
+						*p -= 2;
+						if (ST_INDEX(s, *p) == ESCAPE && ST_INDEX(s, *p - 1) != EOF) {
+							--*p;
+						}
+					} else if (ST_INDEX(s, *p) == ']' && *p >= 8 && ST_INDEX(s, *p - 1) == ':') {
+
+#define CXCLASS_CHECK(n, t, f, o, b) if (1) { \
+	if (memcmp(St_ptr(s) + *p - (n), (t), (n) - 1) != 0) { \
+		goto normal_char; \
+	} \
+	(b)(cur->class.vec, (f)); \
+	*p -= (o); \
+	break; \
+} else (void)0
+
+#define CCLASS_CHECK(t, f, o) CXCLASS_CHECK(sizeof (t), t, f, o, fill)
+#define CNCLASS_CHECK(t, f, o) CXCLASS_CHECK(sizeof (t), t, f, o, nfill)
+
+						if (ST_INDEX(s, *p - 8) == '[' && ST_INDEX(s, *p - 7) == ':') {
+							switch (ST_INDEX(s, *p - 6)) {
+								case 'a':
+									if (ST_INDEX(s, *p - 5) != 'l') {
+										goto normal_char;
+									}
+									switch (ST_INDEX(s, *p - 4)) {
+										case 'n': CCLASS_CHECK("um", isalnum, 8);
+										case 'p': CCLASS_CHECK("ha", isalpha, 8);
+										default: goto normal_char;
+									}
+									break;
+
+								case 'c': CCLASS_CHECK("ntrl", iscntrl, 8);
+								case 'd': CCLASS_CHECK("igit", isdigit, 8);
+								case 'g': CCLASS_CHECK("raph", isgraph, 8);
+								case 'l': CCLASS_CHECK("ower", islower, 8);
+
+								case 'p':
+									switch (ST_INDEX(s, *p - 5)) {
+										case 'r': CCLASS_CHECK("int", isprint, 8);
+										case 'u': CCLASS_CHECK("nct", ispunct, 8);
+										default: goto normal_char;
+									}
+									break;
+
+								case 's': CCLASS_CHECK("pace", isspace, 8);
+								case 'u': CCLASS_CHECK("pper", isupper, 8);
+								default: goto normal_char;
+							}
+						} else if (*p >= 9 && ST_INDEX(s, *p - 9) == '[' && ST_INDEX(s, *p - 8) == ':') {
+							switch (ST_INDEX(s, *p - 7)) {
+								case 'x':
+									CCLASS_CHECK("digit", isxdigit, 9);
+
+								case '^':
+									switch (ST_INDEX(s, *p - 6)) {
+										case 'a':
+											if (ST_INDEX(s, *p - 5) != 'l') {
+												goto normal_char;
+											}
+											switch (ST_INDEX(s, *p - 4)) {
+												case 'n': CNCLASS_CHECK("um", isalnum, 9);
+												case 'p': CNCLASS_CHECK("ha", isalpha, 9);
+												default: goto normal_char;
+											}
+											break;
+
+										case 'c': CNCLASS_CHECK("ntrl", iscntrl, 9);
+										case 'd': CNCLASS_CHECK("igit", isdigit, 9);
+										case 'g': CNCLASS_CHECK("raph", isgraph, 9);
+										case 'l': CNCLASS_CHECK("ower", islower, 9);
+
+										case 'p':
+											switch (ST_INDEX(s, *p - 5)) {
+												case 'r': CNCLASS_CHECK("int", isprint, 9);
+												case 'u': CNCLASS_CHECK("nct", ispunct, 9);
+												default: goto normal_char;
+											}
+											break;
+
+										case 's': CNCLASS_CHECK("pace", isspace, 9);
+										case 'u': CNCLASS_CHECK("pper", isupper, 9);
+										default: goto normal_char;
+									}
+									break;
+
+								default: goto normal_char;
+							}
+						} else if (*p >= 10 && memcmp(St_ptr(s) + *p - 10, "[:^xdigit", 9) == 0) {
+							nfill(cur->class.vec, isxdigit);
+							*p -= 10;
+						} else {
+							goto normal_char;
+						}
+					} else if (ST_INDEX(s, *p) == '^' && ((b = ST_INDEX(s, *p - 1) == '\'') || b == EOF)) {
+						cur->class.type = RE_N_CLASS;
+					} else normal_char: {
+						cur->class.vec[c / CHAR_BIT] |= 1u << c % CHAR_BIT;
+					}
+				}
+			}
+			if (c == '\'') {
+				--*p;
+			}
+
+			if (cur->class.type == RE_CLASS) {
+				size_t i;
+				for (i = 0; i < CLASS_SIZE; ++i) {
+					if (cur->class.vec[i] != (unsigned char)~0u) {
+						return cur;
+					}
+				}
+				cur->x.type = RE_ANYCH;
+			} else {
+				size_t i;
+				assert(cur->class.type == RE_N_CLASS);
+				for (i = 0; i < CLASS_SIZE; ++i) {
+					if (cur->class.vec[i] != 0) {
+						return cur;
+					}
+				}
+				cur->x.type = RE_ANYCH;
+			}
+
+			return cur;
+		}
+
+std_atom:
+		default: {
+			re_node *cur = bl_node(con->block);
+			cur->literal.type = RE_LITERAL;
+
+			/*XXX?*/
+			if (next && next->x.type == RE_LITERAL) {
+				if (next->literal.len) {
+					cur->literal.buf = bl_string(con->block, cur->literal.len = 1u + next->literal.len);
+					memcpy(cur->literal.buf + 1, next->literal.buf, next->literal.len);
+				}
+				cur->literal.next = next->literal.next;
+			} else {
+				cur->literal.next = next;
+				cur->literal.buf = bl_string(con->block, cur->literal.len = 1);
+			}
+
+			cur->literal.buf[0] = ST_INDEX(s, *p);
+			--*p;
+			return cur;
+		}
+	}
+}
+
+PRAZ(literal) {
+	SNORK;
+	int c;
+	String tmp;
+	re_node *cur;
+
+	skipspace(s, p);
+	if ((ST_INDEX(s, *p) != ESCAPE || isalnum(ST_INDEX(s, *p - 1))) && MEATCHARP(ST_INDEX(s, *p))) {
+		TAILC(atom);
+	}
+
+	cur = bl_node(con->block);
+	cur->literal.type = RE_LITERAL;
+	cur->literal.next = next;
+
+	St_init(&tmp);
+	while (
+			(c = ST_INDEX(s, *p)) != EOF &&
+			(
+			 !MEATCHARP(c) ||
+			 (c == ESCAPE && !isalnum(ST_INDEX(s, *p - 1)))
+			)
+		  ) {
+		--*p;
+		if (c == ESCAPE && ST_INDEX(s, *p) != EOF) {
+			c = ST_INDEX(s, *p);
+			--*p;
+		}
+		St_cat_c(&tmp, c);
+		skipspace(s, p);
+	}
+	St_reverse(&tmp);
+
+	if (next && next->x.type == RE_LITERAL) {
+		if (next->literal.len) {
+			St_cat_m(&tmp, next->literal.buf, next->literal.len);
+		}
+		cur->literal.next = next->literal.next;
+	}
+
+	cur->literal.buf = bl_string(con->block, cur->literal.len = St_len(&tmp));
+	memcpy(cur->literal.buf, St_ptr(&tmp), St_len(&tmp));
+	St_clear(&tmp);
+
+	return cur;
+}
+
+#define RETURN_REP(lo, hi, grdy) \
+do { \
+	re_node *cur = bl_node(con->block); \
+	cur->rep.type = (grdy); \
+	cur->rep.next = next; \
+	cur->rep.min = (lo); \
+	cur->rep.max = (hi); \
+	cur->rep.n = con->base->repets++; \
+	cur->rep.arg = CALL(atom, cur, NULL); \
+	return cur; \
+} while (0)
+
+#define STD_REP_CASE(lo, hi, grdy) \
+do { \
+	--*p; \
+	RETURN_REP(lo, hi, grdy); \
+} while (0)
+
+#define CUSTOM_REP_CASE(off, grdy, mismatch) \
+do { \
+	size_t from, to; \
+	to = *p - 1 - (off); \
+	while (isdigit(ST_INDEX(s, to))) { \
+		--to; \
+	} \
+	if (ST_INDEX(s, to) == ':') { \
+		*p = to - 1; \
+		to = ST_INDEX(s, to + 1) == ':' ? (size_t)-1 : strtoul(St_ptr(s) + to + 1, NULL, 10); \
+		from = to; \
+	} else { \
+		if (ST_INDEX(s, to) != ',') { \
+			mismatch; \
+		} \
+		from = to - 1; \
+		if (!isdigit(ST_INDEX(s, from))) { \
+			mismatch; \
+		} \
+		do { \
+			--from; \
+		} while (isdigit(ST_INDEX(s, from))); \
+		if (ST_INDEX(s, from) != ':') { \
+			mismatch; \
+		} \
+		*p = from - 1; \
+		from = strtoul(St_ptr(s) + from + 1, NULL, 10); \
+		to = ST_INDEX(s, to + 1) == ':' ? (size_t)-1 : strtoul(St_ptr(s) + to + 1, NULL, 10); \
+	} \
+	if (to == 1) { \
+		if (from == 1) { \
+			TAILC(atom); \
+		} \
+		if (from == 0) { \
+			re_node *cur = bl_node(con->block); \
+			cur->alter.type = RE_ALTER; \
+			if ((grdy) == RE_REP_GREEDY) { \
+				cur->alter.next = next; \
+				cur->alter.arg = CALL(atom, next, NULL); \
+			} else { \
+				cur->alter.arg = next; \
+				cur->alter.next = CALL(atom, next, NULL); \
+			} \
+			return cur; \
+		} \
+	} \
+	RETURN_REP(from, to, grdy); \
+} while (0)
+
+PRAZ(quantifire) {
+	SNORK;
+
+	skipspace(s, p);
+	switch (ST_INDEX(s, *p)) {
+		case EOF: return next;
+
+		case '?':
+			switch (ST_INDEX(s, *p - 1)) {
+				case ':': CUSTOM_REP_CASE(1, RE_REP_FRUGAL, goto hook);
+				case '*': --*p; STD_REP_CASE(0, -1, RE_REP_FRUGAL);
+				case '+': --*p; STD_REP_CASE(1, -1, RE_REP_FRUGAL);
+
+				case '?': {
+					re_node *cur = bl_node(con->block);
+					*p -= 2;
+					cur->alter.type = RE_ALTER;
+					cur->alter.next = CALL(atom, next, NULL);
+					cur->alter.arg = next;
+					return cur;
+				}
+
+hook:
+				default: {
+					re_node *cur = bl_node(con->block);
+					--*p;
+					cur->alter.type = RE_ALTER;
+					cur->alter.next = next;
+					cur->alter.arg = CALL(atom, next, NULL);
+					return cur;
+				}
+			}
+
+		case ':': CUSTOM_REP_CASE(0, RE_REP_GREEDY, goto noquanti);
+		case '*': STD_REP_CASE(0, -1, RE_REP_GREEDY);
+		case '+': STD_REP_CASE(1, -1, RE_REP_GREEDY);
+
+noquanti:
+		default: TAILC(literal);
+	}
+}
+
+PRAZ(branch) {
+	SNORK;
+	skipspace(s, p);
+	while (ST_INDEX(s, *p) != EOF && !ENDBRANCHP(ST_INDEX(s, *p))) {
+		next = CALL(quantifire, next, curolan_zebeth);
+		skipspace(s, p);
+	}
+	return next;
+}
+
+PRAZ(alternation) {
+	SNORK;
+	re_node *cur;
+
+	skipspace(s, p);
+	if (ST_INDEX(s, *p) == EOF) {
+		return next;
+	}
+
+	next = CALL(branch, next, curolan_zebeth);
+	skipspace(s, p);
+	if (ST_INDEX(s, *p) != '|') {
+		return next;
+	}
+
+	--*p;
+	cur = bl_node(con->block);
+	cur->alter.type = RE_ALTER;
+	cur->alter.next = next;
+	cur->alter.arg = CALL(alternation, curolan_zebeth, curolan_zebeth);
+	return cur;
+}
+
+ATTR_CONST
+static size_t minimum(const size_t a, const size_t b) {
+	return a < b ? a : b;
+}
+
+ATTR_CONST
+static size_t maximum(const size_t a, const size_t b) {
+	return a > b ? a : b;
+}
+
+ATTR_PURE
+static size_t subminlen(const re_node *node, const re_node *end, size_t start) {
+	if (!node || node == end) {
+		return start;
+	}
+
+	switch (node->x.type) {
+		case RE_ALTER:
+			return minimum(
+					subminlen(node->alter.arg, end, start),
+					subminlen(node->alter.next, end, start)
+					);
+
+		case RE_ANYCH:
+		case RE_CLASS:
+		case RE_DEFCLASS:
+		case RE_N_CLASS:
+		case RE_N_DEFCLASS:
+			return subminlen(node->x.next, end, start + 1u);
+
+		case RE_AT_BOL:
+		case RE_AT_BOS:
+		case RE_AT_EOL:
+		case RE_AT_EOS:
+		case RE_AT_NWBOUND:
+		case RE_AT_WBOUND:
+		case RE_BACKCHECK:
+		case RE_BACKREF:
+		case RE_BEGIN_CAPTURE:
+		case RE_END_CAPTURE:
+		case RE_PARTIAL:
+		case RE_STUFF_FRUGAL:
+		case RE_STUFF_GREEDY:
+			return subminlen(node->x.next, end, start);
+
+		case RE_AT_MATCH:
+			return maximum(
+					subminlen(node->indep.arg, end, start),
+					subminlen(node->indep.next, end, start)
+					);
+
+		case RE_AT_NOMATCH:
+			return subminlen(node->indep.next, end, start);
+
+		case RE_INDEP:
+			return subminlen(node->indep.next, end, start + subminlen(node->indep.arg, NULL, 0));
+
+		case RE_LITERAL:
+			return subminlen(node->literal.next, end, start + node->literal.len);
+
+		case RE_SELECT:
+			return minimum(
+					subminlen(node->select.cond, end, start) + subminlen(node->select.arg, end, start),
+					subminlen(node->select.next, end, start)
+					);
+
+		case RE_REP_FRUGAL:
+		case RE_REP_GREEDY:
+			if (node->rep.min == 0) {
+				return subminlen(node->rep.next, end, start);
+			}
+			return subminlen(node->rep.next, end, start + node->rep.min * subminlen(node->rep.arg, node, 0));
+
+		case RE_XREP_FRUGAL:
+		case RE_XREP_GREEDY:
+			if (node->rep.min == 0) {
+				return subminlen(node->rep.next, end, start);
+			}
+			return subminlen(node->rep.next, end, start + node->rep.min * subminlen(node->rep.arg, NULL, 0));
+	}
+
+	NOTREACHED;
+}
+
+ATTR_PURE
+static size_t minlen(const re_node *node) {
+	return subminlen(node, NULL, 0);
+}
+
+static void reinit(t_regex *re) {
+	const struct cap_state null = { -1, -1, -1 };
+	size_t i;
+	for (i = 0; i < re->captures; ++i) {
+		re->cap_ptr[i] = null;
+	}
+}
+
+static re_node **notcomplex(re_node **node, const re_node *end) {
+	assert(*node != NULL);
+
+	if (*node == end) {
+		return node;
+	}
+
+	switch ((*node)->x.type) {
+		case RE_ALTER:
+		case RE_AT_MATCH:
+		case RE_AT_NOMATCH:
+		case RE_BACKCHECK:
+		case RE_BACKREF:
+		case RE_BEGIN_CAPTURE:
+		case RE_END_CAPTURE:
+		case RE_INDEP:
+		case RE_PARTIAL:
+		case RE_REP_FRUGAL:
+		case RE_REP_GREEDY:
+		case RE_SELECT:
+		case RE_STUFF_FRUGAL:
+		case RE_STUFF_GREEDY:
+			return NULL;
+
+		case RE_XREP_FRUGAL:
+		case RE_XREP_GREEDY:
+			if ((*node)->rep.min == (*node)->rep.max) {
+				return notcomplex(&(*node)->rep.next, end);
+			}
+			return NULL;
+
+		case RE_ANYCH:
+		case RE_AT_BOL:
+		case RE_AT_BOS:
+		case RE_AT_EOL:
+		case RE_AT_EOS:
+		case RE_AT_NWBOUND:
+		case RE_AT_WBOUND:
+		case RE_CLASS:
+		case RE_DEFCLASS:
+		case RE_LITERAL:
+		case RE_N_CLASS:
+		case RE_N_DEFCLASS:
+			return notcomplex(&(*node)->x.next, end);
+	}
+
+	NOTREACHED;
+}
+
+static void trysimpl(re_node *node) {
+	assert(node->x.type == RE_REP_FRUGAL || node->x.type == RE_REP_GREEDY);
+
+	{
+		re_node **const tmp = notcomplex(&node->rep.arg, node);
+		if (!tmp) {
+			return;
+		}
+		*tmp = NULL;
+	}
+
+	switch (node->rep.type) {
+		case RE_REP_FRUGAL:
+			if (
+					node->rep.arg->x.type == RE_ANYCH &&
+					node->rep.arg->x.next == NULL &&
+					node->rep.max == (size_t)-1
+			   ) {
+				if (node->rep.min == 0) {
+					node->x.type = RE_STUFF_FRUGAL;
+					break;
+				}
+				if (node->rep.min == 1) {
+					node->rep.arg->x.next = node->x.next;
+					node->rep.arg->x.type = RE_STUFF_FRUGAL;
+					node->x.next = node->rep.arg;
+					node->x.type = RE_ANYCH;
+					break;
+				}
+			}
+
+			node->rep.n = minlen(node->rep.arg);
+			node->rep.type = RE_XREP_FRUGAL;
+			break;
+
+		case RE_REP_GREEDY:
+			if (
+					node->rep.arg->x.type == RE_ANYCH &&
+					node->rep.arg->x.next == NULL &&
+					node->rep.max == (size_t)-1
+			   ) {
+				if (node->rep.min == 0) {
+					node->x.type = RE_STUFF_GREEDY;
+					break;
+				}
+				if (node->rep.min == 1) {
+					node->rep.arg->x.next = node->x.next;
+					node->rep.arg->x.type = RE_STUFF_GREEDY;
+					node->x.next = node->rep.arg;
+					node->x.type = RE_ANYCH;
+					break;
+				}
+			}
+
+			node->rep.n = minlen(node->rep.arg);
+			node->rep.type = RE_XREP_GREEDY;
+			break;
+
+		default:
+			NOTREACHED;
+	}
+
+}
+
+static void simplerep(re_node *node, const re_node *const end) {
+	if (!node || node == end) {
+		return;
+	}
+
+	switch (node->x.type) {
+		case RE_ALTER:
+			simplerep(node->alter.arg, end);
+			simplerep(node->alter.next, end);
+			return;
+
+		case RE_AT_MATCH:
+		case RE_AT_NOMATCH:
+		case RE_INDEP:
+			simplerep(node->indep.arg, end);
+			simplerep(node->indep.next, end);
+			return;
+
+		case RE_ANYCH:
+		case RE_AT_BOL:
+		case RE_AT_BOS:
+		case RE_AT_EOL:
+		case RE_AT_EOS:
+		case RE_AT_NWBOUND:
+		case RE_AT_WBOUND:
+		case RE_BACKCHECK:
+		case RE_BACKREF:
+		case RE_BEGIN_CAPTURE:
+		case RE_CLASS:
+		case RE_DEFCLASS:
+		case RE_END_CAPTURE:
+		case RE_LITERAL:
+		case RE_N_CLASS:
+		case RE_N_DEFCLASS:
+		case RE_PARTIAL:
+			simplerep(node->x.next, end);
+			return;
+
+		case RE_REP_FRUGAL:
+		case RE_REP_GREEDY:
+			simplerep(node->rep.next, end);
+			simplerep(node->rep.arg, node);
+			trysimpl(node);
+			return;
+
+		case RE_SELECT:
+			simplerep(node->select.cond, end);
+			simplerep(node->select.arg, end);
+			simplerep(node->select.next, end);
+			return;
+
+		case RE_STUFF_FRUGAL:
+		case RE_STUFF_GREEDY:
+		case RE_XREP_FRUGAL:
+		case RE_XREP_GREEDY:
+			return;
+	}
+
+	NOTREACHED;
+}
+
+ATTR_PURE
+static int anchorbegp(const re_node *node) {
+	return
+		node &&
+		(
+		 node->x.type == RE_AT_BOS ||
+		 node->x.type == RE_STUFF_FRUGAL ||
+		 node->x.type == RE_STUFF_GREEDY ||
+		 (
+		  node->x.type == RE_ALTER &&
+		  anchorbegp(node->alter.arg) &&
+		  anchorbegp(node->alter.next)
+		 ) ||
+		 (
+		  node->x.type == RE_INDEP &&
+		  anchorbegp(node->indep.arg)
+		 ) ||
+		 (
+		  node->x.type == RE_AT_MATCH &&
+		  (
+		   anchorbegp(node->indep.arg) ||
+		   anchorbegp(node->indep.next)
+		  )
+		 ) ||
+		 (
+		  (
+		   node->x.type == RE_AT_NOMATCH ||
+		   node->x.type == RE_AT_BOL ||
+		   node->x.type == RE_AT_EOL ||
+		   node->x.type == RE_AT_EOS ||
+		   node->x.type == RE_AT_NWBOUND ||
+		   node->x.type == RE_AT_WBOUND ||
+		   node->x.type == RE_BEGIN_CAPTURE ||
+		   node->x.type == RE_BACKCHECK
+		  ) &&
+		  anchorbegp(node->x.next)
+		 )
+		)
+		;
+}
+
+static void dostuff(t_regex *re) {
+	simplerep(re->start, NULL);
+
+	if (!re->start || anchorbegp(re->start)) {
+		re->flags |= FL_ANCHOR_START;
+	}
+}
+
+t_regex *re_compile(const String *s) {
+	size_t p;
+	struct parse_context dcon;
+	t_regex *re;
+	void *pre;
+
+	if (h_get(&rcache, s, &pre) == H_OK) {
+		re = pre;
+		++re->refs;
+		return re;
+	}
+
+	re = xmalloc(1, sizeof *re);
+	re->flags = FL_NONE;
+	re->refs = 0;
+	re->repets = 0;
+	bl_init(&re->alloc);
+	re->cap_ptr = NULL;
+	re->captures = 0;
+	dcon.base = re;
+	dcon.block = &re->alloc;
+	dcon.s = s;
+	p = St_len(s) - 1;
+	dcon.p = &p;
+
+	re->start = alternation(&dcon, NULL, NULL);
+
+	if (re->captures) {
+		re->cap_ptr = xmalloc(re->captures, sizeof *re->cap_ptr);
+		reinit(re);
+	}
+
+	dostuff(re);
+	re->minlen = minlen(re->start);
+	re->repbuf = xmalloc(re->repets, sizeof *re->repbuf);
+
+	if (cachfill < sizeof cached / sizeof cached[0]) {
+		St_init(&cached[cachfill]);
+		St_cpy(&cached[cachfill], s);
+		++re->refs;
+		h_put(&rcache, &cached[cachfill], re, 1);
+		++cachfill;
+	} else {
+		h_del(&rcache, &cached[cachlast]);
+		St_cpy(&cached[cachlast], s);
+		++re->refs;
+		h_put(&rcache, &cached[cachlast], re, 1);
+		cachlast = (cachlast + 1u) % (sizeof cached / sizeof cached[0]);
+	}
+
+	if (Opt.debug & DBG_REGEX) {
+		io_write_s(Err, "Compiled ");
+		re_dump(re, io_fp(Err));
+	}
+
+	return re;
+}
+
+t_regex *re_dup(t_regex *re) {
+	++re->refs;
+	return re;
+}
+
+void re_free(t_regex *re) {
+	if (re->refs) {
+		--re->refs;
+		return;
+	}
+	bl_free(&re->alloc);
+	xfree(re->repbuf);
+	xfree(re->cap_ptr);
+	xfree(re);
+}
+
+ATTR_PURE
+static int match_class(unsigned char c, const unsigned char *vec) {
+	return vec[c / CHAR_BIT] & 1u << c % CHAR_BIT;
+}
+
+static void *mem_dup(const void *p, size_t n, size_t m) {
+	void *q = xmalloc(n, m);
+	memcpy(q, p, n * m);
+	return q;
+}
+
+#define NO_MATCH ((size_t)-1)
+
+#define Mmatch0(Xmatch, Xindex) \
+	size_t tmp; \
+ \
+	if (!node) { \
+		return o; \
+	} \
+ \
+	switch (node->x.type) { \
+		case RE_ALTER: \
+			if ((tmp = Xmatch(base, node->alter.arg, s, o)) == NO_MATCH) { \
+				return Xmatch(base, node->alter.next, s, o); \
+			} \
+			return tmp; \
+ \
+		case RE_ANYCH: \
+			if (Xindex(s, o) != EOF) { \
+				return Xmatch(base, node->x.next, s, o + 1u); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_AT_BOL: \
+			if (o == 0 || Xindex(s, o - 1u) == '\n') { \
+				return Xmatch(base, node->x.next, s, o); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_AT_BOS: \
+			if (o == 0) { \
+				return Xmatch(base, node->x.next, s, o); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_AT_EOL: \
+			if ( \
+					Xindex(s, o) == '\n' || \
+					( \
+					 o && Xindex(s, o) == EOF && Xindex(s, o - 1u) != '\n' \
+					) \
+			   ) { \
+				return Xmatch(base, node->x.next, s, o); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_AT_EOS: \
+			if (Xindex(s, o) == EOF) { \
+				return Xmatch(base, node->x.next, s, o); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_AT_MATCH: { \
+			struct cap_state *prev = mem_dup(base->cap_ptr, base->captures, sizeof *base->cap_ptr); \
+			tmp = Xmatch(base, node->indep.arg, s, o); \
+			if (tmp != NO_MATCH) { \
+				tmp = Xmatch(base, node->indep.next, s, o); \
+				if (tmp == NO_MATCH) { \
+					memcpy(base->cap_ptr, prev, base->captures * sizeof *base->cap_ptr); \
+				} \
+			} \
+			xfree(prev); \
+			return tmp; \
+		} \
+ \
+		case RE_AT_NOMATCH: { \
+			struct cap_state *prev = mem_dup(base->cap_ptr, base->captures, sizeof *base->cap_ptr); \
+			tmp = Xmatch(base, node->indep.arg, s, o); \
+			if (tmp == NO_MATCH) { \
+				tmp = Xmatch(base, node->indep.next, s, o); \
+			} else { \
+				memcpy(base->cap_ptr, prev, base->captures * sizeof *base->cap_ptr); \
+				tmp = NO_MATCH; \
+			} \
+			xfree(prev); \
+			return tmp; \
+		} \
+ \
+		case RE_AT_NWBOUND: \
+			if (siword(Xindex(s, o - 1u)) == siword(Xindex(s, o))) { \
+				return Xmatch(base, node->x.next, s, o); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_AT_WBOUND: \
+			if (siword(Xindex(s, o - 1u)) != siword(Xindex(s, o))) { \
+				return Xmatch(base, node->x.next, s, o); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_BACKCHECK: \
+			if ( \
+					node->backref.n < base->captures && \
+					base->cap_ptr[node->backref.n].start + 1u && \
+					base->cap_ptr[node->backref.n].end + 1u && \
+					base->cap_ptr[node->backref.n].end >= base->cap_ptr[node->backref.n].start \
+			   ) { \
+				return Xmatch(base, node->backref.next, s, o); \
+			} \
+			return NO_MATCH
+
+
+#define Mmatch1(Xmatch, Xindex) \
+		case RE_BEGIN_CAPTURE: { \
+			const size_t prev = base->cap_ptr[node->capture.n].pending; \
+			base->cap_ptr[node->capture.n].pending = o; \
+			if ((tmp = Xmatch(base, node->capture.next, s, o)) == NO_MATCH) { \
+				base->cap_ptr[node->capture.n].pending = prev; \
+			} \
+			return tmp; \
+		} \
+ \
+		case RE_CLASS: \
+			if ( \
+				Xindex(s, o) != EOF && \
+				match_class(Xindex(s, o), node->class.vec) \
+			) { \
+				return Xmatch(base, node->class.next, s, o + 1u); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_DEFCLASS: \
+			if ( \
+				Xindex(s, o) != EOF && \
+				match_class(Xindex(s, o), node->defclass.vec) \
+			) { \
+				return Xmatch(base, node->defclass.next, s, o + 1u); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_END_CAPTURE: { \
+			const size_t prevbeg = base->cap_ptr[node->capture.n].start; \
+			const size_t prevend = base->cap_ptr[node->capture.n].end; \
+			if (base->cap_ptr[node->capture.n].pending + 1u == 0u) { \
+				return NO_MATCH; \
+			} \
+ \
+			base->cap_ptr[node->capture.n].end = o; \
+			base->cap_ptr[node->capture.n].start = base->cap_ptr[node->capture.n].pending; \
+			if ((tmp = Xmatch(base, node->capture.next, s, o)) == NO_MATCH) { \
+				base->cap_ptr[node->capture.n].end = prevend; \
+				base->cap_ptr[node->capture.n].start = prevbeg; \
+			} \
+			return tmp; \
+		} \
+ \
+		case RE_INDEP: { \
+			struct cap_state *prev = mem_dup(base->cap_ptr, base->captures, sizeof *base->cap_ptr); \
+			if ((tmp = Xmatch(base, node->indep.arg, s, o)) != NO_MATCH) { \
+				tmp = Xmatch(base, node->indep.next, s, tmp); \
+				if (tmp == NO_MATCH) { \
+					memcpy(base->cap_ptr, prev, base->captures * sizeof *base->cap_ptr); \
+				} \
+			} \
+			xfree(prev); \
+			return tmp; \
+		} \
+ \
+		case RE_N_CLASS: \
+			if ( \
+				Xindex(s, o) != EOF && \
+				!match_class(Xindex(s, o), node->class.vec) \
+			) { \
+				return Xmatch(base, node->class.next, s, o + 1u); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_N_DEFCLASS: \
+			if ( \
+				Xindex(s, o) != EOF && \
+				!match_class(Xindex(s, o), node->defclass.vec) \
+			) { \
+				return Xmatch(base, node->defclass.next, s, o + 1u); \
+			} \
+			return NO_MATCH; \
+ \
+		case RE_PARTIAL: { \
+			size_t n; \
+			for (n = 0; n < node->literal.len; ++n) { \
+				if (node->literal.buf[n] != Xindex(s, o + n)) { \
+					break; \
+				} \
+			} \
+			for (; n; --n) { \
+				if ((tmp = Xmatch(base, node->literal.next, s, o + n)) != NO_MATCH) { \
+					return tmp; \
+				} \
+			} \
+			return Xmatch(base, node->literal.next, s, o); \
+		} \
+ \
+		case RE_SELECT: { \
+			struct cap_state *prev = mem_dup(base->cap_ptr, base->captures, sizeof *base->cap_ptr); \
+			if ((tmp = Xmatch(base, node->select.cond, s, o)) != NO_MATCH) { \
+				tmp = Xmatch(base, node->select.arg, s, tmp); \
+				if (tmp == NO_MATCH) { \
+					memcpy(base->cap_ptr, prev, base->captures * sizeof *base->cap_ptr); \
+				} \
+			} else { \
+				tmp = Xmatch(base, node->select.next, s, o); \
+			} \
+			xfree(prev); \
+			return tmp; \
+		} \
+ \
+		case RE_REP_FRUGAL: \
+			if (base->repbuf[node->rep.n] >= node->rep.max) { \
+				const size_t n = base->repbuf[node->rep.n]; \
+ \
+				base->repbuf[node->rep.n] = 0; \
+				tmp = Xmatch(base, node->rep.next, s, o); \
+				base->repbuf[node->rep.n] = n; \
+				return tmp; \
+			} else if (base->repbuf[node->rep.n] >= node->rep.min) { \
+				const size_t n = base->repbuf[node->rep.n]; \
+ \
+				base->repbuf[node->rep.n] = 0; \
+				if ((tmp = Xmatch(base, node->rep.next, s, o)) != NO_MATCH) { \
+					base->repbuf[node->rep.n] = n; \
+					return tmp; \
+				} \
+				base->repbuf[node->rep.n] = n + 1u; \
+				tmp = Xmatch(base, node->rep.arg, s, o); \
+				--base->repbuf[node->rep.n]; \
+				return tmp; \
+			} else { \
+				++base->repbuf[node->rep.n]; \
+				tmp = Xmatch(base, node->rep.arg, s, o); \
+				--base->repbuf[node->rep.n]; \
+				return tmp; \
+			} \
+ \
+		case RE_REP_GREEDY: \
+			if (base->repbuf[node->rep.n] >= node->rep.max) { \
+				const size_t n = base->repbuf[node->rep.n]; \
+ \
+				base->repbuf[node->rep.n] = 0; \
+				tmp = Xmatch(base, node->rep.next, s, o); \
+				base->repbuf[node->rep.n] = n; \
+				return tmp; \
+			} else if (base->repbuf[node->rep.n] >= node->rep.min) { \
+				const size_t n = base->repbuf[node->rep.n]; \
+ \
+				++base->repbuf[node->rep.n]; \
+				if ((tmp = Xmatch(base, node->rep.arg, s, o)) != NO_MATCH) { \
+					--base->repbuf[node->rep.n]; \
+					return tmp; \
+				} \
+				base->repbuf[node->rep.n] = 0; \
+				tmp = Xmatch(base, node->rep.next, s, o); \
+				base->repbuf[node->rep.n] = n; \
+				return tmp; \
+			} else { \
+				++base->repbuf[node->rep.n]; \
+				tmp = Xmatch(base, node->rep.arg, s, o); \
+				--base->repbuf[node->rep.n]; \
+				return tmp; \
+			} \
+ \
+		case RE_XREP_FRUGAL: { \
+			size_t n; \
+ \
+			for (n = 0; n < node->rep.min; ++n) { \
+				if ((tmp = Xmatch(base, node->rep.arg, s, o)) == NO_MATCH) { \
+					return NO_MATCH; \
+				} \
+				o = tmp; \
+			} \
+ \
+			for (; n < node->rep.max; ++n) { \
+				if ((tmp = Xmatch(base, node->rep.next, s, o)) != NO_MATCH) { \
+					return tmp; \
+				} \
+				if ((tmp = Xmatch(base, node->rep.arg, s, o)) == NO_MATCH) { \
+					return NO_MATCH; \
+				} \
+				o = tmp; \
+			} \
+			return Xmatch(base, node->rep.next, s, o); \
+		} \
+ \
+		case RE_XREP_GREEDY: { \
+			size_t n; \
+ \
+			for (n = 0; n < node->rep.min; ++n) { \
+				if ((tmp = Xmatch(base, node->rep.arg, s, o)) == NO_MATCH) { \
+					return NO_MATCH; \
+				} \
+				o = tmp; \
+			} \
+ \
+			for (; n < node->rep.max; ++n) { \
+				if ((tmp = Xmatch(base, node->rep.arg, s, o)) == NO_MATCH) { \
+					break; \
+				} \
+				o = tmp; \
+			} \
+ \
+			for (; n > node->rep.min; --n) { \
+				if ((tmp = Xmatch(base, node->rep.next, s, o)) != NO_MATCH) { \
+					return tmp; \
+				} \
+				o -= node->rep.n; \
+			} \
+ \
+			return Xmatch(base, node->rep.next, s, o); \
+		} \
+	} \
+ \
+	NOTREACHED
+
+
+static size_t match(const t_regex *base, const re_node *node, const String *s, size_t o) {
+
+	Mmatch0(match, ST_INDEX);
+
+		case RE_BACKREF:
+			if (
+					node->backref.n < base->captures &&
+					base->cap_ptr[node->backref.n].start + 1u &&
+					base->cap_ptr[node->backref.n].end + 1u &&
+					base->cap_ptr[node->backref.n].end >= base->cap_ptr[node->backref.n].start &&
+					(
+					 base->cap_ptr[node->backref.n].end - base->cap_ptr[node->backref.n].start <= St_len(s) - o &&
+					 memcmp(
+						 St_ptr(s) + base->cap_ptr[node->backref.n].start,
+						 St_ptr(s) + o,
+						 base->cap_ptr[node->backref.n].end - base->cap_ptr[node->backref.n].start
+						 ) == 0
+					)
+			   ) {
+				return match(base, node->backref.next, s, o + base->cap_ptr[node->backref.n].end - base->cap_ptr[node->backref.n].start);
+			}
+			return NO_MATCH;
+
+
+		case RE_LITERAL:
+			if (
+					node->literal.len == 0 ||
+					(
+					 node->literal.len <= St_len(s) - o &&
+					 memcmp(node->literal.buf, St_ptr(s) + o, node->literal.len) == 0
+					)
+			   ) {
+				return match(base, node->literal.next, s, o + node->literal.len);
+			}
+			return NO_MATCH;
+
+		case RE_STUFF_FRUGAL:
+			if (!node->x.next) {
+				return o;
+			}
+
+			if (node->x.next->x.type == RE_LITERAL && node->x.next->literal.len) {
+				for (
+						o = St_stro_m(s, o, node->x.next->literal.buf, node->x.next->literal.len);
+						o + 1u;
+						o = St_stro_m(s, o + 1u, node->x.next->literal.buf, node->x.next->literal.len)
+						) {
+					if ((tmp = match(base, node->x.next->literal.next, s, o + node->x.next->literal.len)) != NO_MATCH) {
+						return tmp;
+					}
+				}
+				return NO_MATCH;
+			}
+
+			for (; o <= St_len(s); ++o) {
+				if ((tmp = match(base, node->x.next, s, o)) != NO_MATCH) {
+					return tmp;
+				}
+			}
+			return NO_MATCH;
+
+		case RE_STUFF_GREEDY: {
+			size_t n;
+
+			if (!node->x.next) {
+				return St_len(s);
+			}
+
+			if (node->x.next->x.type == RE_LITERAL && node->x.next->literal.len) {
+				for (
+						n = St_rstr_m(s, node->x.next->literal.buf, node->x.next->literal.len);
+						n > o && n + 1u;
+						n = St_rstro_m(s, n - 1u, node->x.next->literal.buf, node->x.next->literal.len)
+						) {
+					if ((tmp = match(base, node->x.next->literal.next, s, n + node->x.next->literal.len)) != NO_MATCH) {
+						return tmp;
+					}
+					if (!n) {
+						break;
+					}
+				}
+				if (n == o) {
+					return match(base, node->x.next->literal.next, s, n + node->x.next->literal.len);
+				}
+				return NO_MATCH;
+			}
+
+			for (n = St_len(s); n > o; --n) {
+				if ((tmp = match(base, node->x.next, s, n)) != NO_MATCH) {
+					return tmp;
+				}
+			}
+			return match(base, node->x.next, s, o);
+		}
+
+	Mmatch1(match, ST_INDEX);
+}
+
+static size_t iomatch(const t_regex *base, const re_node *node, IO *s, size_t o) {
+
+	Mmatch0(iomatch, io_peek);
+
+		case RE_BACKREF:
+			if (
+					node->backref.n < base->captures &&
+					base->cap_ptr[node->backref.n].start + 1u &&
+					base->cap_ptr[node->backref.n].end + 1u &&
+					base->cap_ptr[node->backref.n].end >= base->cap_ptr[node->backref.n].start &&
+					(
+					 io_xcmp(
+						 s,
+						 base->cap_ptr[node->backref.n].start,
+						 o,
+						 base->cap_ptr[node->backref.n].end - base->cap_ptr[node->backref.n].start
+						 ) == 0
+					)
+			   ) {
+				return iomatch(base, node->backref.next, s, o + base->cap_ptr[node->backref.n].end - base->cap_ptr[node->backref.n].start);
+			}
+			return NO_MATCH;
+
+
+		case RE_LITERAL:
+			if (
+					node->literal.len == 0 ||
+					(
+					 io_cmppeek(s, o, node->literal.buf, node->literal.len) == 0
+					)
+			   ) {
+				return iomatch(base, node->literal.next, s, o + node->literal.len);
+			}
+			return NO_MATCH;
+
+		case RE_STUFF_FRUGAL:
+			if (!node->x.next) {
+				return o;
+			}
+
+			for (; io_peek(s, o) != EOF; ++o) {
+				if ((tmp = iomatch(base, node->x.next, s, o)) != NO_MATCH) {
+					return tmp;
+				}
+			}
+			return iomatch(base, node->x.next, s, o);
+
+		case RE_STUFF_GREEDY: {
+			size_t n;
+
+			for (n = o; io_peek(s, n) != EOF; ++n)
+				;
+
+			if (!node->x.next) {
+				return n;
+			}
+
+			for (; n > o; --n) {
+				if ((tmp = iomatch(base, node->x.next, s, n)) != NO_MATCH) {
+					return tmp;
+				}
+			}
+			return iomatch(base, node->x.next, s, o);
+		}
+
+	Mmatch1(iomatch, io_peek);
+}
+
+#define DEFRBUF ((void)0)
+#define MRETURN(x) return (x)
+#define INITRBUF(re) memset((re)->repbuf, '\0', (re)->repets * sizeof *(re)->repbuf)
+
+int re_iomatch(t_regex *re, IO *io, size_t *ms, size_t *me) {
+	size_t i;
+	DEFRBUF;
+	INITRBUF(re);
+
+	assert(re != NULL);
+	assert(io != NULL);
+	assert(io_bufred(io));
+	assert(ms != NULL);
+	assert(me != NULL);
+
+	reinit(re);
+
+	if (re->flags & FL_ANCHOR_START) {
+		if ((*me = iomatch(re, re->start, io, 0)) == NO_MATCH) {
+			MRETURN(0);
+		}
+		*ms = 0;
+		MRETURN(1);
+	}
+
+	for (i = 0; io_peek(io, i) != EOF; ++i) {
+		if ((*me = iomatch(re, re->start, io, i)) != NO_MATCH) {
+			*ms = i;
+			MRETURN(1);
+		}
+	}
+
+	if ((*me = iomatch(re, re->start, io, i)) != NO_MATCH) {
+		*ms = i;
+		MRETURN(1);
+	}
+	MRETURN(0);
+}
+
+int re_match(t_regex *re, const String *s, size_t *ms, size_t *me) {
+	size_t i;
+	DEFRBUF;
+	INITRBUF(re);
+
+	assert(re != NULL);
+	assert(s != NULL);
+	assert(ms != NULL);
+	assert(me != NULL);
+
+	if (re->minlen > St_len(s)) {
+		MRETURN(0);
+	}
+
+	reinit(re);
+
+	if (re->flags & FL_ANCHOR_START) {
+		if ((*me = match(re, re->start, s, 0)) == NO_MATCH) {
+			MRETURN(0);
+		}
+		*ms = 0;
+		MRETURN(1);
+	}
+
+	if (re->start->x.type == RE_LITERAL && re->start->literal.len) {
+		for (
+				i = St_str_m(s, re->start->literal.buf, re->start->literal.len);
+				i + 1u;
+				i = St_stro_m(s, i + 1u, re->start->literal.buf, re->start->literal.len)
+			) {
+			if ((*me = match(re, re->start->literal.next, s, i + re->start->literal.len)) != NO_MATCH) {
+				*ms = i;
+				MRETURN(1);
+			}
+		}
+		MRETURN(0);
+	}
+
+	for (i = 0; i <= St_len(s) - re->minlen; ++i) {
+		if ((*me = match(re, re->start, s, i)) != NO_MATCH) {
+			*ms = i;
+			MRETURN(1);
+		}
+	}
+
+	MRETURN(0);
+}
+
+size_t re_cabra(const t_regex *re) {
+	return re->captures;
+}
+
+int re_backref(const t_regex *re, size_t i, size_t *a, size_t *z) {
+	if (i >= re->captures) {
+		return -1;
+	}
+	if (!(re->cap_ptr[i].start + 1u && re->cap_ptr[i].end + 1u)) {
+		return 1;
+	}
+	*a = re->cap_ptr[i].start;
+	*z = re->cap_ptr[i].end;
+	return 0;
+}
+
+ATTR_PURE
+static const re_node *branchend(const re_node *a, const re_node *b) {
+	for (; a; a = a->x.next) {
+		const re_node *q = b;
+		for (; q; q = q->x.next) {
+			if (a == q) {
+				return q;
+			}
+		}
+	}
+	return NULL;
+}
+
+static void decom_class(String *s, const unsigned char vec[HAVE_C99(static CLASS_SIZE)]) {
+	unsigned char i;
+	int flag = 0;
+	char big_enough[100];
+
+	for (i = 0; i < UCHAR_MAX; ++i) {
+		if (vec[i / CHAR_BIT] & 1u << i % CHAR_BIT) {
+			if (vec[(i + 1u) / CHAR_BIT] & 1u << (i + 1u) % CHAR_BIT) {
+				if (!flag) {
+					flag = 1;
+					goto mess;
+				}
+				flag = 1;
+			} else {
+				if (flag) {
+					if (i > 1u && vec[(i - 2u) / CHAR_BIT] & 1u << (i - 2u) % CHAR_BIT) {
+						St_cat_c(s, '-');
+					}
+					flag = 0;
+				}
+mess:
+				if (!isprint(i)) {
+					St_cat_c(s, '\\');
+					switch (i) {
+						case '\a': St_cat_c(s, 'a'); break;
+						case '\b': St_cat_c(s, 'b'); break;
+						case '\f': St_cat_c(s, 'f'); break;
+						case '\n': St_cat_c(s, 'n'); break;
+						case '\r': St_cat_c(s, 'r'); break;
+						case '\t': St_cat_c(s, 't'); break;
+						case '\v': St_cat_c(s, 'v'); break;
+						default:
+								   sprintf(big_enough, "%03o", (unsigned)i);
+								   St_cat_s(s, big_enough);
+								   break;
+					}
+				} else {
+					if (strchr("\\\"", (char)i)) {
+						St_cat_c(s, '\\');
+					}
+					St_cat_c(s, i);
+					if (strchr("!'-", (char)i)) {
+						St_cat_c(s, ESCAPE);
+					}
+				}
+			}
+		}
+	}
+	if (vec[i / CHAR_BIT] & 1u << i % CHAR_BIT) {
+		if (flag) {
+			if (vec[(i - 2u) / CHAR_BIT] & 1u << (i - 2u) % CHAR_BIT) {
+				St_cat_c(s, '-');
+			}
+			flag = 0;
+		}
+		if (!isprint(i)) {
+			St_cat_c(s, '\\');
+			switch (i) {
+				case '\a': St_cat_c(s, 'a'); break;
+				case '\b': St_cat_c(s, 'b'); break;
+				case '\f': St_cat_c(s, 'f'); break;
+				case '\n': St_cat_c(s, 'n'); break;
+				case '\r': St_cat_c(s, 'r'); break;
+				case '\t': St_cat_c(s, 't'); break;
+				case '\v': St_cat_c(s, 'v'); break;
+				default:
+						   sprintf(big_enough, "%03o", (unsigned)i);
+						   St_cat_s(s, big_enough);
+						   break;
+			}
+		} else {
+			if (strchr("\\\"", (char)i)) {
+				St_cat_c(s, '\\');
+			}
+			St_cat_c(s, i);
+			if (strchr("!'-", (char)i)) {
+				St_cat_c(s, ESCAPE);
+			}
+		}
+	}
+}
+
+static void dumpclass(FILE *fp, const unsigned char vec[HAVE_C99(static CLASS_SIZE)]) {
+	String tmp;
+	St_init(&tmp);
+	decom_class(&tmp, vec);
+	ST_WRITE(&tmp, fp);
+	St_clear(&tmp);
+}
+
+ATTR_CONST
+static int cdefclass(const unsigned char *ptr) {
+	if (ptr == dc_word)   { return 'w'; }
+	if (ptr == dc_alpha)  { return 'q'; }
+	if (ptr == dc_cntrl)  { return 'c'; }
+	if (ptr == dc_digit)  { return 'd'; }
+	if (ptr == dc_lower)  { return 'l'; }
+	if (ptr == dc_print)  { return 'p'; }
+	if (ptr == dc_space)  { return 's'; }
+	if (ptr == dc_upper)  { return 'u'; }
+	if (ptr == dc_xdigit) { return 'x'; }
+	NOTREACHED;
+}
+
+static void decom_liter(String *s, const re_node *node) {
+	size_t i;
+	assert(node->x.type == RE_LITERAL || node->x.type == RE_PARTIAL);
+	for (i = 0; i < node->literal.len; ++i) {
+		if (!isprint((unsigned char)node->literal.buf[i])) {
+			St_cat_c(s, '\\');
+			switch (node->literal.buf[i]) {
+				case '\a': St_cat_c(s, 'a'); break;
+				case '\b': St_cat_c(s, 'b'); break;
+				case '\f': St_cat_c(s, 'f'); break;
+				case '\n': St_cat_c(s, 'n'); break;
+				case '\r': St_cat_c(s, 'r'); break;
+				case '\t': St_cat_c(s, 't'); break;
+				case '\v': St_cat_c(s, 'v'); break;
+				default: {
+					char big_enough[100];
+					sprintf(big_enough, "%03o", (unsigned)(unsigned char)node->literal.buf[i]);
+					St_cat_s(s, big_enough);
+					break;
+				}
+			}
+		} else {
+			if (node->x.type == RE_LITERAL && strchr("\"\\", node->literal.buf[i])) {
+				St_cat_c(s, '\\');
+			}
+			St_cat_c(s, node->literal.buf[i]);
+			if (MEATCHARP(node->literal.buf[i])) {
+				St_cat_c(s, ESCAPE);
+			}
+		}
+	}
+}
+
+static void dumpliter(FILE *fp, const re_node *node) {
+	String tmp;
+	St_init(&tmp);
+	decom_liter(&tmp, node);
+	ST_WRITE(&tmp, fp);
+	St_clear(&tmp);
+}
+
+static void do_indent(FILE *fp, size_t n) {
+	for (; n; --n) {
+		fputs("  ", fp);
+	}
+}
+
+static void dumpnode(FILE *fp, const re_node *node, const re_node *end, const size_t indent) {
+	if (node == end) {
+		return;
+	}
+
+	assert(node != NULL);
+	fprintf(fp, "[%p] ", (const void *)node);
+	do_indent(fp, indent);
+
+	switch (node->x.type) {
+		case RE_ALTER: {
+			const re_node *const be = branchend(node->alter.arg, node->alter.next);
+			fputs("branch {\n", fp);
+			dumpnode(fp, node->alter.arg, be, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} or {\n", fp);
+			dumpnode(fp, node->alter.next, be, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /branch\n", fp);
+			dumpnode(fp, be, end, indent);
+			return;
+		}
+
+		case RE_ANYCH:
+			fputs("anychar\n", fp);
+			break;
+
+		case RE_AT_BOL:
+			fputs("at beginning of line\n", fp);
+			break;
+
+		case RE_AT_BOS:
+			fputs("at beginning of string\n", fp);
+			break;
+
+		case RE_AT_EOL:
+			fputs("at end of line\n", fp);
+			break;
+
+		case RE_AT_EOS:
+			fputs("at end of string\n", fp);
+			break;
+
+		case RE_AT_MATCH:
+			fputs("at match {\n", fp);
+			dumpnode(fp, node->indep.arg, NULL, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /at match\n", fp);
+			break;
+
+		case RE_AT_NOMATCH:
+			fputs("not at match {\n", fp);
+			dumpnode(fp, node->indep.arg, NULL, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /not at match\n", fp);
+			break;
+
+		case RE_AT_NWBOUND:
+			fputs("not at word boundary\n", fp);
+			break;
+
+		case RE_AT_WBOUND:
+			fputs("at word boundary\n", fp);
+			break;
+
+		case RE_BACKCHECK:
+			fprintf(fp, "check capture (%lu)\n", (unsigned long)node->backref.n);
+			break;
+
+		case RE_BACKREF:
+			fprintf(fp, "backref (%lu)\n", (unsigned long)node->backref.n);
+			break;
+
+		case RE_BEGIN_CAPTURE:
+			fprintf(fp, "begin capture (%lu)\n", (unsigned long)node->backref.n);
+			break;
+
+		case RE_CLASS:
+			fputs("character class '", fp);
+			dumpclass(fp, node->class.vec);
+			fputs("'\n", fp);
+			break;
+
+		case RE_DEFCLASS:
+			fprintf(fp, "character class %c!\n", cdefclass(node->defclass.vec));
+			break;
+
+		case RE_END_CAPTURE:
+			fprintf(fp, "end capture (%lu)\n", (unsigned long)node->backref.n);
+			break;
+
+		case RE_INDEP:
+			fputs("independent submatch {\n", fp);
+			dumpnode(fp, node->indep.arg, NULL, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /independent submatch\n", fp);
+			break;
+
+		case RE_LITERAL:
+			fputs("literal \"", fp);
+			dumpliter(fp, node);
+			fputs("\"\n", fp);
+			break;
+
+		case RE_N_CLASS:
+			fputs("negated character class '", fp);
+			dumpclass(fp, node->class.vec);
+			fputs("'\n", fp);
+			break;
+
+		case RE_N_DEFCLASS:
+			fprintf(fp, "character class %c!\n", toupper(cdefclass(node->defclass.vec)));
+			break;
+
+		case RE_PARTIAL:
+			fputs("abbreviation `", fp);
+			dumpliter(fp, node);
+			fputs("`\n", fp);
+			break;
+
+		case RE_REP_FRUGAL:
+			fputs("repetition ", fp);
+			if (node->rep.min == node->rep.max) {
+				fprintf(fp, ":%lu:?", (unsigned long)node->rep.min);
+			} else if (node->rep.max != (size_t)-1) {
+				fprintf(fp, ":%lu,%lu:?", (unsigned long)node->rep.min, (unsigned long)node->rep.max);
+			} else {
+				switch (node->rep.min) {
+					case 0: fputs("*?", fp); break;
+					case 1: fputs("+?", fp); break;
+					default: fprintf(fp, ":%lu,:?", (unsigned long)node->rep.min); break;
+				}
+			}
+			fputs(" {\n", fp);
+			dumpnode(fp, node->rep.arg, node, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /repetition\n", fp);
+			break;
+
+		case RE_REP_GREEDY:
+			fputs("repetition ", fp);
+			if (node->rep.min == node->rep.max) {
+				fprintf(fp, ":%lu:", (unsigned long)node->rep.min);
+			} else if (node->rep.max != (size_t)-1) {
+				fprintf(fp, ":%lu,%lu:", (unsigned long)node->rep.min, (unsigned long)node->rep.max);
+			} else {
+				switch (node->rep.min) {
+					case 0: fputs("*", fp); break;
+					case 1: fputs("+", fp); break;
+					default: fprintf(fp, ":%lu,:", (unsigned long)node->rep.min); break;
+				}
+			}
+			fputs(" {\n", fp);
+			dumpnode(fp, node->rep.arg, node, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /repetition\n", fp);
+			break;
+
+		case RE_SELECT: {
+			const re_node *const be = branchend(node->select.arg, node->select.next);
+			fputs("if {\n", fp);
+			dumpnode(fp, node->select.cond, NULL, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} then {\n", fp);
+			dumpnode(fp, node->select.arg, be, indent + 1);
+			if (node->select.next != be) {
+				fprintf(fp, "[%p] ", (const void *)node);
+				do_indent(fp, indent);
+				fputs("} else {\n", fp);
+				dumpnode(fp, node->select.next, be, indent + 1);
+			}
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /if\n", fp);
+			dumpnode(fp, be, end, indent);
+			return;
+		}
+
+		case RE_STUFF_FRUGAL:
+			fputs(".*?\n", fp);
+			break;
+
+		case RE_STUFF_GREEDY:
+			fputs(".*\n", fp);
+			break;
+
+		case RE_XREP_FRUGAL:
+			fputs("simple repetition ", fp);
+			if (node->rep.min == node->rep.max) {
+				fprintf(fp, ":%lu:?", (unsigned long)node->rep.min);
+			} else if (node->rep.max != (size_t)-1) {
+				fprintf(fp, ":%lu,%lu:?", (unsigned long)node->rep.min, (unsigned long)node->rep.max);
+			} else {
+				switch (node->rep.min) {
+					case 0: fputs("*?", fp); break;
+					case 1: fputs("+?", fp); break;
+					default: fprintf(fp, ":%lu,:?", (unsigned long)node->rep.min); break;
+				}
+			}
+			fputs(" {\n", fp);
+			dumpnode(fp, node->rep.arg, NULL, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /simple repetition\n", fp);
+			break;
+
+		case RE_XREP_GREEDY:
+			fputs("simple repetition ", fp);
+			if (node->rep.min == node->rep.max) {
+				fprintf(fp, ":%lu:", (unsigned long)node->rep.min);
+			} else if (node->rep.max != (size_t)-1) {
+				fprintf(fp, ":%lu,%lu:", (unsigned long)node->rep.min, (unsigned long)node->rep.max);
+			} else {
+				switch (node->rep.min) {
+					case 0: fputs("*", fp); break;
+					case 1: fputs("+", fp); break;
+					default: fprintf(fp, ":%lu,:", (unsigned long)node->rep.min); break;
+				}
+			}
+			fputs(" {\n", fp);
+			dumpnode(fp, node->rep.arg, NULL, indent + 1);
+			fprintf(fp, "[%p] ", (const void *)node);
+			do_indent(fp, indent);
+			fputs("} /simple repetition\n", fp);
+			break;
+	}
+	dumpnode(fp, node->x.next, end, indent);
+}
+
+void re_dump(const t_regex *re, FILE *fp) {
+	fprintf(fp, "RE: minlen=%lu, anchor=%s\n", (unsigned long)re->minlen, re->flags & FL_ANCHOR_START ? "start" : "none");
+	dumpnode(fp, re->start, NULL, 0);
+}
+
+static void cat_n(String *s, unsigned long n) {
+	char big_enough[100];
+	sprintf(big_enough, "%lu", n);
+	St_cat_s(s, big_enough);
+}
+
+static void decom_repeat(String *s, const re_node *node) {
+	assert(
+		node->x.type == RE_REP_FRUGAL ||
+		node->x.type == RE_REP_GREEDY ||
+		node->x.type == RE_XREP_FRUGAL ||
+		node->x.type == RE_XREP_GREEDY
+	);
+
+	if (node->rep.min == 0 && node->rep.max == (size_t)-1) {
+		St_cat_c(s, '*');
+	} else if (node->rep.min == 0 && node->rep.max == 1) {
+		St_cat_c(s, '?');
+	} else if (node->rep.min == 1 && node->rep.max == (size_t)-1) {
+		St_cat_c(s, '+');
+	} else if (node->rep.min == node->rep.max) {
+		St_cat_c(s, ':');
+		cat_n(s, node->rep.min);
+		St_cat_c(s, ':');
+	} else if (node->rep.max == (size_t)-1) {
+		St_cat_c(s, ':');
+		cat_n(s, node->rep.min);
+		St_cat_m(s, ",:", 2);
+	} else {
+		St_cat_c(s, ':');
+		cat_n(s, node->rep.min);
+		St_cat_c(s, ',');
+		cat_n(s, node->rep.max);
+		St_cat_c(s, ':');
+	}
+
+	if (node->rep.type == RE_REP_FRUGAL || node->rep.type == RE_XREP_FRUGAL) {
+		St_cat_c(s, '?');
+	}
+}
+
+static void decom_node(String *s, const re_node *node, const re_node *end) {
+	if (node == end) {
+		return;
+	}
+
+	assert(node != NULL);
+	switch (node->x.type) {
+		case RE_ALTER: {
+			const re_node *const be = branchend(node->alter.arg, node->alter.next);
+			St_cat_c(s, '(');
+			decom_node(s, node->alter.arg, be);
+			St_cat_c(s, '|');
+			decom_node(s, node->alter.next, be);
+			St_cat_c(s, ')');
+			decom_node(s, be, end);
+			break;
+		}
+
+		case RE_ANYCH:
+			St_cat_c(s, '.');
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_AT_BOL:
+			St_cat_m(s, "A!", 2);
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_AT_BOS:
+			St_cat_c(s, '^');
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_AT_EOL:
+			St_cat_m(s, "Z!", 2);
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_AT_EOS:
+			St_cat_c(s, '$');
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_AT_MATCH:
+			St_cat_c(s, '[');
+			decom_node(s, node->indep.arg, NULL);
+			St_cat_m(s, "&]", 2);
+			decom_node(s, node->indep.next, end);
+			break;
+
+		case RE_AT_NOMATCH:
+			St_cat_c(s, '[');
+			decom_node(s, node->indep.arg, NULL);
+			St_cat_m(s, "^]", 2);
+			decom_node(s, node->indep.next, end);
+			break;
+
+		case RE_AT_NWBOUND:
+			St_cat_m(s, "B!", 2);
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_AT_WBOUND:
+			St_cat_m(s, "b!", 2);
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_BACKREF:
+		case RE_BACKCHECK: {
+			String tmp;
+			if (node->x.type == RE_BACKCHECK) {
+				St_cat_c(s, '[');
+			}
+			St_init(&tmp);
+#if HAVE_VSNPRINTF_P
+			St_xprintf(&tmp, "%lu", (unsigned long)node->backref.n);
+#else
+			St_num(&tmp, node->backref.n);
+#endif
+			St_cat(s, &tmp);
+			St_clear(&tmp);
+			if (node->x.type == RE_BACKCHECK) {
+				St_cat_c(s, ']');
+			} else {
+				St_cat_c(s, ESCAPE);
+			}
+			decom_node(s, node->backref.next, end);
+			break;
+		}
+
+		case RE_BEGIN_CAPTURE:
+			St_cat_c(s, '{');
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_CLASS:
+			St_cat_c(s, '\'');
+			decom_class(s, node->class.vec);
+			St_cat_c(s, '\'');
+			decom_node(s, node->class.next, end);
+			break;
+
+		case RE_DEFCLASS:
+			St_cat_c(s, cdefclass(node->defclass.vec));
+			St_cat_c(s, ESCAPE);
+			decom_node(s, node->defclass.next, end);
+			break;
+
+		case RE_END_CAPTURE:
+			St_cat_c(s, '}');
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_INDEP:
+			St_cat_c(s, '<');
+			decom_node(s, node->indep.arg, NULL);
+			St_cat_c(s, '>');
+			decom_node(s, node->indep.next, end);
+			break;
+
+		case RE_LITERAL:
+			decom_liter(s, node);
+			decom_node(s, node->literal.next, end);
+			break;
+
+		case RE_N_CLASS:
+			St_cat_m(s, "'^", 2);
+			decom_class(s, node->class.vec);
+			St_cat_c(s, '\'');
+			decom_node(s, node->class.next, end);
+			break;
+
+		case RE_N_DEFCLASS:
+			St_cat_c(s, toupper(cdefclass(node->defclass.vec)));
+			St_cat_c(s, ESCAPE);
+			decom_node(s, node->defclass.next, end);
+			break;
+
+		case RE_PARTIAL:
+			St_cat_c(s, '`');
+			decom_liter(s, node);
+			St_cat_c(s, '`');
+			break;
+
+		case RE_REP_FRUGAL:
+		case RE_REP_GREEDY:
+			St_cat_c(s, '(');
+			decom_node(s, node->rep.arg, node);
+			St_cat_c(s, ')');
+			decom_repeat(s, node);
+			decom_node(s, node->rep.next, end);
+			break;
+
+		case RE_SELECT: {
+			const re_node *const be = branchend(node->select.arg, node->select.next);
+			St_cat_c(s, '[');
+			if (be != node->select.next) {
+				decom_node(s, node->select.next, be);
+				St_cat_c(s, '|');
+			}
+			decom_node(s, node->select.arg, be);
+			St_cat_c(s, '(');
+			decom_node(s, node->select.cond, NULL);
+			St_cat_c(s, ')');
+			St_cat_m(s, "?]", 2);
+			decom_node(s, be, end);
+			break;
+		}
+
+		case RE_STUFF_FRUGAL:
+			St_cat_m(s, ".*?", 3);
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_STUFF_GREEDY:
+			St_cat_m(s, ".*", 2);
+			decom_node(s, node->x.next, end);
+			break;
+
+		case RE_XREP_FRUGAL:
+		case RE_XREP_GREEDY:
+			St_cat_c(s, '(');
+			decom_node(s, node->rep.arg, NULL);
+			St_cat_c(s, ')');
+			decom_repeat(s, node);
+			decom_node(s, node->rep.next, end);
+			break;
+	}
+}
+
+void re_decompile(const t_regex *re, String *s) {
+	St_zero(s);
+	decom_node(s, re->start, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/re.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,2 @@
+re.o: re.c config.h IO.h Str.h hash.h main_io.h main_opt.h re.h xmalloc.h \
+ zz.h re_block.c.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/re.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,31 @@
+#ifndef RE_H_
+#define RE_H_
+
+#include "config.h"
+#include "IO.h"
+#include "Str.h"
+
+#include <stddef.h>
+#include <stdio.h>
+
+typedef struct my_regex t_regex;
+
+void re_init(void);
+void re_end(void);
+
+t_regex *re_compile(const String *);
+void re_free(t_regex *);
+t_regex *re_dup(t_regex *);
+
+ATTR_PURE
+int re_match(t_regex *, const String *, size_t *, size_t *);
+int re_iomatch(t_regex *, IO *, size_t *, size_t *);
+
+ATTR_PURE
+size_t re_cabra(const t_regex *);
+int re_backref(const t_regex *, size_t, size_t *, size_t *);
+
+void re_dump(const t_regex *, FILE *);
+void re_decompile(const t_regex *, String *);
+
+#endif /* RE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/re_block.c.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,60 @@
+/* vi: set ft=c: */
+enum {BLOCK_MAGIC = 32};
+
+struct block_node {
+	struct block_node *next;
+	size_t used;
+	re_node nodes[BLOCK_MAGIC];
+};
+
+struct txt_node {
+	struct txt_node *next;
+	unsigned char *buf;
+};
+
+typedef struct {
+	struct block_node *root;
+	struct txt_node *txt_root;
+} t_block;
+
+static void bl_init(t_block *b) {
+	b->root = xmalloc(1, sizeof *b->root);
+	b->root->next = NULL;
+	b->root->used = 0;
+	b->txt_root = NULL;
+}
+
+static void bl_free(t_block *b) {
+	while (b->root) {
+		struct block_node *tmp = b->root->next;
+		xfree(b->root);
+		b->root = tmp;
+	}
+
+	while (b->txt_root) {
+		struct txt_node *tmp = b->txt_root->next;
+		xfree(b->txt_root->buf);
+		xfree(b->txt_root);
+		b->txt_root = tmp;
+	}
+}
+
+static re_node *bl_node(t_block *b) {
+	if (b->root->used >= sizeof b->root->nodes / sizeof b->root->nodes[0]) {
+		struct block_node *tmp = xmalloc(1, sizeof *tmp);
+		tmp->used = 0;
+		tmp->next = b->root;
+		b->root = tmp;
+	}
+
+	return b->root->nodes + b->root->used++;
+}
+
+static unsigned char *bl_string(t_block *b, size_t len) {
+	struct txt_node *tmp = xmalloc(1, sizeof *tmp);
+	tmp->buf = xmalloc(len, sizeof *tmp->buf);
+	tmp->next = b->txt_root;
+	b->txt_root = tmp;
+
+	return b->txt_root->buf;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/run.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,458 @@
+#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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/run.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,4 @@
+run.o: run.c config.h IO.h Str.h atechit.h expr.h op.h re.h stack.h \
+  xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h hang.h main.h \
+  main_io.h main_label.h mars.h venus.h hash.h main_opt.h run.h text.h \
+  zz.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/run.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,51 @@
+#ifndef RUN_H_
+#define RUN_H_
+
+#include "config.h"
+#include "op.h"
+#include "text.h"
+#include "val.h"
+#include "stack.h"
+
+#include <stddef.h>
+
+struct val *execute(const struct op *, struct val *);
+ATTR_NORETURN
+void run(const struct text *, size_t, char **);
+
+struct Interp {
+	struct val arg;
+	struct val result;
+
+	struct {
+		struct val *argv;
+		size_t argc;
+	} a;
+	struct {
+		struct val *matches;
+		size_t length, size;
+	} match;
+	struct {
+		size_t *index;
+		size_t size;
+	} m_start;
+	struct {
+		size_t *index;
+		size_t size;
+	} m_end;
+};
+extern struct Interp Interp;
+
+typedef struct {
+	struct val *target;
+	struct val content;
+} save_pair;
+
+stack_declare(save_pair, extern)
+extern stack(save_pair) Saved;
+
+void stack_store(struct val *, const struct val *);
+size_t depth_get(void);
+void depth_restore(size_t);
+
+#endif /* RUN_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/stack.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,103 @@
+#ifndef STACK_H_
+#define STACK_H_
+
+#include "config.h"
+#include "xmalloc.h"
+
+#include <stddef.h>
+
+#define stack(T) struct T##STACK
+#define stack_func(T, f) T##STACK##f
+
+#define stack_declare(T, X) \
+ \
+stack(T) { \
+	T *arr; \
+	size_t size, length; \
+}; \
+ \
+X void stack_func(T, init)(stack(T) *); \
+X void stack_func(T, end)(stack(T) *); \
+X void stack_func(T, clear)(stack(T) *, size_t); \
+ \
+X void stack_func(T, push)(stack(T) *, const T *); \
+X void stack_func(T, pushnull)(stack(T) *); \
+X int stack_func(T, pop)(stack(T) *, T *); \
+/* X int stack_func(T, peek)(const stack(T) *, T *); */ \
+ATTR_PURE \
+X T *stack_func(T, at)(const stack(T) *, size_t); \
+ATTR_PURE \
+X size_t stack_func(T, size)(const stack(T) *); \
+/* end of declaration */
+
+#define stack_define(T, X, prepare, clean, copy) \
+X void stack_func(T, init)(stack(T) *s) { \
+	s->arr = xmalloc(s->size = 7, sizeof *s->arr); \
+	s->length = 0; \
+} \
+ \
+X void stack_func(T, end)(stack(T) *s) { \
+	while (s->length) { \
+		--s->length; \
+		(clean)(&s->arr[s->length]); \
+	} \
+	xfree(s->arr); \
+	s->arr = 0; \
+} \
+ \
+X void stack_func(T, clear)(stack(T) *s, size_t n) { \
+	while (n && s->length) { \
+		--n; \
+		--s->length; \
+		(clean)(&s->arr[s->length]); \
+	} \
+} \
+ \
+X void stack_func(T, push)(stack(T) *s, const T *x) { \
+	if (s->length >= s->size) { \
+		s->arr = xrealloc(s->arr, s->size *= 2); \
+	} \
+	(prepare)(&s->arr[s->length]); \
+	(copy)(&s->arr[s->length], x); \
+	++s->length; \
+} \
+ \
+X void stack_func(T, pushnull)(stack(T) *s) { \
+	if (s->length >= s->size) { \
+		s->arr = xrealloc(s->arr, s->size *= 2); \
+	} \
+	(prepare)(&s->arr[s->length]); \
+	++s->length; \
+} \
+ \
+X int stack_func(T, pop)(stack(T) *s, T *x) { \
+	if (s->length) { \
+		--s->length; \
+		(copy)(x, &s->arr[s->length]); \
+		(clean)(&s->arr[s->length]); \
+		return 0; \
+	} \
+	return 1; \
+} \
+ \
+/* X int stack_func(T, peek)(const stack(T) *s, T *x) { \
+	if (s->length) { \
+		(copy)(x, &s->arr[s->length - 1]); \
+		return 0; \
+	} \
+	return 1; \
+} */ \
+ \
+X size_t stack_func(T, size)(const stack(T) *s) { \
+	return s->length; \
+} \
+ \
+X T *stack_func(T, at)(const stack(T) *s, size_t n) { \
+	if (n < s->length) { \
+		return &s->arr[s->length - n - 1u]; \
+	} \
+	return NULL; \
+} \
+/* end of definition */
+
+#endif /* STACK_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/strhash.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,65 @@
+#include "hash.h"
+#include "strhash.h"
+#include "strutil.h"
+#include "xmalloc.h"
+
+#include <string.h>
+
+struct strx {
+	char *ptr;
+	size_t length;
+};
+
+struct strx_const {
+	const char *ptr;
+	size_t length;
+};
+
+struct strhash {
+	Hash hash;
+};
+
+static size_t hash(const void *p, size_t h) {
+	const struct strx *const s = p;
+	return u_hash(s->ptr, s->length, h);
+}
+
+static int compar(const void *ap, const void *bp) {
+	const struct strx *const a = ap, *const b = bp;
+	return u_cmp(a->ptr, a->length, b->ptr, b->length);
+}
+
+static void delk(void *p) {
+	struct strx *const s = p;
+	xfree(s->ptr);
+	xfree(s);
+}
+
+t_strhash *sh_new(void (*delv)(void *)) {
+	t_strhash *sh = xmalloc(1, sizeof *sh);
+	h_init(&sh->hash, hash, compar, delk, delv);
+	return sh;
+}
+
+void sh_delete(t_strhash *sh) {
+	h_end(&sh->hash);
+	xfree(sh);
+}
+
+void sh_put(t_strhash *sh, const char *key, size_t keylen, void *val) {
+	struct strx *dup = xmalloc(1, sizeof *dup);
+	dup->ptr = xmalloc(dup->length = keylen, sizeof *dup->ptr);
+	memcpy(dup->ptr, key, keylen);
+	h_put(&sh->hash, dup, val, 1);
+}
+
+void *sh_get(t_strhash *sh, const char *key, size_t keylen) {
+	void *p;
+	struct strx_const tmp;
+	tmp.ptr = key;
+	tmp.length = keylen;
+	if (h_get(&sh->hash, &tmp, &p) == H_OK) {
+		return p;
+	}
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/strhash.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+strhash.o: strhash.c hash.h strhash.h config.h strutil.h xmalloc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/strhash.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,16 @@
+#ifndef STRHASH_H_
+#define STRHASH_H_
+
+#include "config.h"
+
+#include <stddef.h>
+
+typedef struct strhash t_strhash;
+
+t_strhash *sh_new(void (*)(void *));
+void sh_delete(t_strhash *);
+
+void sh_put(t_strhash *, const char *, size_t, void *);
+void *sh_get(t_strhash *, const char *, size_t);
+
+#endif /* STRHASH_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/strutil.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,34 @@
+#include "strutil.h"
+
+#include <string.h>
+
+/* FYI: This is the "One-at-a-Time" algorithm by Bob Jenkins
+ * from requirements by Colin Plumb.
+ * (http://burtleburtle.net/bob/hash/doobs.html) */
+size_t u_hash(const char *s, size_t l, size_t h) {
+	size_t i;
+
+	for (i = 0; i < l; ++i) {
+		h += (unsigned char)s[i];
+		h += h << 10;
+		h ^= h >> 6;
+	}
+	h += h << 3;
+	h ^= h >> 11;
+	h += h << 15;
+
+	return h;
+}
+
+int u_cmp(const char *as, size_t al, const char *bs, size_t bl) {
+	int tmp;
+	if (as == bs && al == bl)
+		return 0;
+	if ((tmp = memcmp(as, bs, al < bl ? al : bl)))
+		return tmp;
+	if (al < bl)
+		return -1;
+	if (al > bl)
+		return 1;
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/strutil.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+strutil.o: strutil.c strutil.h config.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/strutil.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,13 @@
+#ifndef STRUTIL_H_
+#define STRUTIL_H_
+
+#include "config.h"
+
+#include <stddef.h>
+
+ATTR_PURE
+size_t u_hash(const char *, size_t, size_t);
+ATTR_PURE
+int u_cmp(const char *, size_t, const char *, size_t);
+
+#endif /* STRUTIL_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/sub.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,134 @@
+#include "expr.h"
+#include "main_opt.h"
+#include "sub.h"
+#include "transmogrify.h"
+#include "xmalloc.h"
+
+#include <stddef.h>
+
+struct sub {
+	struct expr *expr;
+	size_t refs;
+	size_t id;
+};
+
+static struct expr *dup(const struct expr *e) {
+	struct expr *p;
+
+	if (!e) {
+		return NULL;
+	}
+
+	p = xmalloc(1, sizeof *p);
+	*p = *e;
+
+	switch (e->type) {
+		case literE:
+			p->v.val = v_undef();
+			v_set(p->v.val, e->v.val);
+			break;
+
+		case varE:
+			break;
+
+		case varhashE:
+			p->right = dup(e->right);
+			break;
+
+		case symbolE:
+			switch (e->op) {
+				case S_ARGV:
+					p->right = dup(e->right);
+					break;
+			}
+			break;
+
+		case unopE:
+			p->right = dup(e->right);
+			if (e->op == F_MATCH) {
+				p->left.rx = re_dup(e->left.rx);
+			}
+			break;
+
+		case binopE:
+		case listE:
+			p->right = dup(e->right);
+			p->left.expr = dup(e->left.expr);
+			break;
+	}
+
+	return p;
+}
+
+static void solid(struct expr *e) {
+	if (!e) {
+		return;
+	}
+
+	switch (e->type) {
+		case literE:
+			break;
+
+		case varE:
+			e->v.val = eval_expr(e);
+			e->type = literE;
+			break;
+
+		case varhashE:
+			break;
+
+		case symbolE:
+			switch (e->op) {
+				case S_ARGV:
+					solid(e->right);
+					break;
+			}
+			break;
+
+		case unopE:
+			solid(e->right);
+			break;
+
+		case binopE:
+		case listE:
+			solid(e->left.expr);
+			solid(e->right);
+			break;
+	}
+}
+
+struct sub *sub_new(const struct expr *e) {
+	static size_t idcount;
+	struct sub *p;
+	p = xmalloc(1, sizeof *p);
+	p->expr = dup(e);
+	solid(p->expr);
+	if (!Opt.unoptimize) {
+		trans_fold(&p->expr);
+	}
+	p->refs = 0;
+	p->id = idcount++;
+	return p;
+}
+
+struct sub *sub_incr(struct sub *p) {
+	++p->refs;
+	return p;
+}
+
+void sub_decr(struct sub *p) {
+	if (!p->refs) {
+		free_expr(p->expr);
+		xfree(p);
+		return;
+	}
+	--p->refs;
+}
+
+struct expr *sub_expr(const struct sub *p) {
+	return p->expr;
+}
+
+size_t sub_id(const struct sub *p) {
+	return p->id;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/sub.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+sub.o: sub.c expr.h config.h Str.h op.h IO.h re.h stack.h xmalloc.h \
+  strhash.h val.h kork.h list.h sub.h variable.h main_opt.h \
+  transmogrify.h text.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/sub.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,18 @@
+#ifndef SUB_H_
+#define SUB_H_
+
+#include "config.h"
+
+#include <stddef.h>
+
+struct expr;
+struct sub;
+
+struct sub *sub_new(const struct expr *);
+struct sub *sub_incr(struct sub *);
+void sub_decr(struct sub *);
+ATTR_PURE
+struct expr *sub_expr(const struct sub *);
+size_t sub_id(const struct sub *);
+
+#endif /* SUB_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/syntax/ploki.vim	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,74 @@
+" Vim syntax file
+" Language:	ploki
+" Maintainer:	Lukas Mai
+" Last Change:	2010-02-28
+" Version:	0.15
+
+if version < 600
+	sy clear
+elseif exists('b:current_syntax')
+	finish
+endif
+
+syn match plokiStuff   /.\+/ contains=plokiList,plokiNumber,plokiSpcErr,plokiSpecial,plokiFunc,plokiString contained
+
+syn match plokiList    /#<\|#>/
+syn match plokiNumber  /\.\d\+\%([Ee][-+]\=\d\+\)\=/
+syn match plokiNumber  /\d\+\%(\.\d*\)\=\%([Ee][-+]\=\d\+\)\=/
+syn match plokiSpcErr  /\\/
+syn match plokiSpecial
+	\     /\\\%([!?@_]\|\d\+\|ARG\|AUSG\|EING\|E\|FEHL\|PI\)\=/
+syn match plokiFunc /\\\%(ARG:\|ENV\|[LQRU]\)/
+syn match plokiFunc
+	\     /@\%([-+]\|ABS\|ACOS\|APERS\|ASIN\|ATAN2\=\|CHR\|COS\|DEF-P\|EDD-P\|ENV\|ERR-P\|EVAL\|GET\|INT\|IO-P\|LAPERS\|LEGS\|LENGTH\|L[GN]\|NEG\|NOT\|NUM\|OMFG\|ORD\|RE\%(MOVE\|NAEM\|VERSE\)\|SAG\|SAPERS\|SIN\|SQRT\|STR\|SUCH\|TAN\|TYPE OF\)/
+syn match plokiEscErr  /\\/ contained
+syn match plokiEscape  /\\\%([abfnrtv"\\V]\|c.\|x\x\x\=\|\o\{1,3}\)/ contained
+syn match plokiString  '"\%(\%(??/\n\)\@![^"\\]\)*\%(\\c\=.\%(\%(??/\n\)\@![^"\\]\)*\)*"\='
+	\     contains=plokiEscape,plokiEscErr nextgroup=plokiCont
+syn match plokiGarbage '\%(\%(??/\n\)\@!.\)\+' contained nextgroup=plokiCont
+syn match plokiCmd
+	\     /#\|A[BN]RUF\|CLAUDS\|END\|FLUSH\|GO\%(FOR\|TO\)\|IACS\|IF\|KTHX\|LEE\=T\|RESET\|SET\|WUNT/
+	\     contained
+syn match plokiCmd     /#!\|ELSE\|END IF\|FI/ contained nextgroup=plokiGarbage
+syn region plokiComment matchgroup=plokiCmd start=/REM/ end=/$/
+	\     contained contains=plokiComment
+syn match plokiWord    /\S\+/        contained nextgroup=plokiGarbage
+syn match plokiCmd     /NEXT\s*/     contained nextgroup=plokiWord
+syn match plokiLabel   /^\s*\%(FOR\s*\S\+\|\d*\)\s*/
+	\     nextgroup=plokiCmd,plokiComment,plokiSpcErr,plokiSpecial,plokiFunc,plokiString
+"	                         ^... around a bug(?)
+
+syn match plokiCont    '??/\n' nextgroup=plokiStuff
+
+syn match plokiInsert  /^\s*INSERT\%(\%( DA\)\=\)\@>\s*\S\+\%(\s\+\S\+\)*\s*HERE\s*$/ contains=plokiInsFile
+syn match plokiInsFile /\%(^\s*INSERT\%(\%( DA\)\=\)\@>\s*\)\@<=\S\+\%(\s\+\S\+\)*\%(\s*HERE\s*$\)\@=/ contained
+
+if version >= 508 || !exists('did_ploki_syn_inits')
+	if version < 508
+		let did_ploki_syn_inits = 1
+		command -nargs=+ HiLink hi link <args>
+	else
+		command -nargs=+ HiLink hi def link <args>
+	endif
+
+	HiLink plokiinsFile String
+	HiLink plokiInsert  Preproc
+	HiLink plokiCont    Preproc
+	HiLink plokiLabel   Label
+	HiLink plokiGarbage Comment
+	HiLink plokiComment Comment
+	HiLink plokiCmd     Type
+	HiLink plokiWord    Label
+	HiLink plokiString  String
+	HiLink plokiEscape  SpecialChar
+	HiLink plokiEscErr  Error
+	HiLink plokiFunc    Function
+	HiLink plokiSpecial Special
+	HiLink plokiSpcErr  Error
+	HiLink plokiNumber  Number
+	HiLink plokiList    Preproc
+
+	delc HiLink
+endif
+
+let b:current_syntax = 'ploki'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/t/00-compile.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,6 @@
+#!ploki
+"1..4
+"ok 1
+"ok 2\n"
+WUNT "ok 3
+WUNT \AUSG "ok 4\n"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/t/10-regress.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,79 @@
+#!ploki
+"1..8
+(#<"ok 1"#> . 0 _ "
+
+IF @NOT ("X" ~ "[{.}^]|{.}"
+	"not "
+ELSE
+	IF \0 ; "X" | (\1 ; "")
+		"not "
+	FI
+FI
+"ok 2
+
+IF @NOT ("Y" ~ "<{.}>Z|{.}"
+	"not "
+ELSE
+	IF \0 ; "Y" | (\1 ; "")
+		"not "
+	FI
+FI
+"ok 3
+
+IF @NOT ("Z" ~ "[{.}&]X|{.}"
+	"not "
+ELSE
+	IF \0 ; "Z" | (\1 ; "")
+		"not "
+	FI
+FI
+"ok 4
+
+(#<"not " "ok "#> [ 1 _ #<5#> _ "
+
+LET f @OMFG @throw \@
+LET e "E"
+LET x @EVAL #<"A" (f . e)#>
+IF x ; "" | (\_ ; e)
+	"not "
+FI
+"ok 6
+
+LET fh @APERS #<"/ this file does not exist!" "RF"#>
+IF fh
+	CLAUDS fh
+	"not "
+FI
+"ok 7
+
+LET n "tmp.txt"
+LET fh @APERS #<n "W+"#>
+IF @NOT fh
+	WUNT \FEHL "open: " _ n _ ": " _ \! _ "
+	END 1
+END IF
+LET txt "zomg
+WUNT fh txt
+IF @SUCH #<fh 0#>
+	WUNT \FEHL "seek: " _ n _ ": " _ \! _ "
+	END 1
+FI
+LET s fh . @NEG 1
+IF @ERR-P fh
+	WUNT \FEHL "read: " _ n _ ": " _ \! _ "
+	END 1
+FI
+CLAUDS fh
+IF \_
+	WUNT \FEHL "close: " _ n _ ": " _ \! _ "
+	END 1
+END IF
+LET () @REMOVE n
+IF s ; txt
+	"not "
+FI
+"ok 8
+
+END
+
+FOR throw IACS \@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/tags	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1109 @@
+!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;" to lines/
+!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/
+!_TAG_PROGRAM_AUTHOR	Darren Hiebert	/dhiebert@users.sourceforge.net/
+!_TAG_PROGRAM_NAME	Exuberant Ctags	//
+!_TAG_PROGRAM_URL	http://ctags.sourceforge.net	/official site/
+!_TAG_PROGRAM_VERSION	5.7	//
+ARITH	pp.c	555;"	d	file:
+ATECHIT_H_	atechit.h	2;"	d
+ATTR	config.h	74;"	d
+ATTR	config.h	76;"	d
+ATTR	config.h	87;"	d
+ATTR_CONST	config.h	95;"	d
+ATTR_MALLOC	config.h	80;"	d
+ATTR_MALLOC	config.h	83;"	d
+ATTR_MALLOC	config.h	88;"	d
+ATTR_NORETURN	config.h	93;"	d
+ATTR_PURE	config.h	81;"	d
+ATTR_PURE	config.h	84;"	d
+ATTR_PURE	config.h	89;"	d
+ATTR_SPRINTF	config.h	94;"	d
+ATTR_UNUSED	config.h	92;"	d
+Afunc	atechit.c	/^static void (*Afunc[MAGIC])(void);$/;"	v	file:
+Aused	atechit.c	/^static size_t Aused;$/;"	v	file:
+BADF	IO.c	246;"	d	file:
+BADF	IO.c	248;"	d	file:
+BLOCK_MAGIC	re_block.c.h	/^enum {BLOCK_MAGIC = 32};$/;"	e	enum:__anon33
+B_AMPERSAND	expr.h	/^	B_AMPERSAND,$/;"	e	enum:t_binop
+B_ANGLE	expr.h	/^	B_ANGLE,$/;"	e	enum:t_binop
+B_BACKSPARK	expr.h	/^	B_BACKSPARK,$/;"	e	enum:t_binop
+B_BRACELET	expr.h	/^	B_BRACELET,$/;"	e	enum:t_binop
+B_DOUBLE_OH_SEVEN	expr.h	/^	B_DOUBLE_OH_SEVEN,$/;"	e	enum:t_binop
+B_EMBRACE	expr.h	/^	B_EMBRACE,$/;"	e	enum:t_binop
+B_FLATWORM	expr.h	/^	B_FLATWORM,$/;"	e	enum:t_binop
+B_HALF_MESH	expr.h	/^	B_HALF_MESH,$/;"	e	enum:t_binop
+B_HYBRID	expr.h	/^	B_HYBRID,$/;"	e	enum:t_binop
+B_INTERSECTION	expr.h	/^	B_INTERSECTION,$/;"	e	enum:t_binop
+B_RIGHT_ANGLE	expr.h	/^	B_RIGHT_ANGLE,$/;"	e	enum:t_binop
+B_SHARK_FIN	expr.h	/^	B_SHARK_FIN,$/;"	e	enum:t_binop
+B_SLAT	expr.h	/^	B_SLAT,$/;"	e	enum:t_binop
+B_SPARK	expr.h	/^	B_SPARK,$/;"	e	enum:t_binop
+B_SPARK_SPOT	expr.h	/^	B_SPARK_SPOT,$/;"	e	enum:t_binop
+B_SPIKE	expr.h	/^	B_SPIKE,$/;"	e	enum:t_binop
+B_SPLAT	expr.h	/^	B_SPLAT,$/;"	e	enum:t_binop
+B_SPOT	expr.h	/^	B_SPOT,$/;"	e	enum:t_binop
+B_SQIGGLE	expr.h	/^	B_SQIGGLE,$/;"	e	enum:t_binop
+B_TAIL	expr.h	/^	B_TAIL,$/;"	e	enum:t_binop
+B_TWO_SPOT	expr.h	/^	B_TWO_SPOT,$/;"	e	enum:t_binop
+B_U_TURN	expr.h	/^	B_U_TURN,$/;"	e	enum:t_binop
+B_U_TURN_BACK	expr.h	/^	B_U_TURN_BACK,$/;"	e	enum:t_binop
+B_WORM	expr.h	/^	B_WORM,$/;"	e	enum:t_binop
+B_XMATCH	expr.h	/^	B_XMATCH$/;"	e	enum:t_binop
+CALL	re.c	231;"	d	file:
+CASE	expr.c	580;"	d	file:
+CASE	expr.c	731;"	d	file:
+CASE_DO_STUFF	expr.c	572;"	d	file:
+CASE_DO_STUFF	expr.c	730;"	d	file:
+CCLASS_CHECK	re.c	712;"	d	file:
+CLASS_SIZE	re.c	/^enum {CLASS_SIZE = UCHAR_MAX \/ CHAR_BIT + 1u};$/;"	e	enum:__anon9	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon11	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon12	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon13	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon14	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon15	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon16	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon17	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon18	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon19	file:
+CMN_HDR	re.c	/^		CMN_HDR;$/;"	m	struct:my_node::__anon20	file:
+CMN_HDR	re.c	99;"	d	file:
+CMP_LS	pp.c	587;"	d	file:
+CMP_N	pp.c	593;"	d	file:
+CNCLASS_CHECK	re.c	713;"	d	file:
+COMPILE_H_	compile.h	2;"	d
+CONFIG_H_	config.h	2;"	d
+CPY	Str.c	399;"	d	file:
+CUSTOM_REP_CASE	re.c	912;"	d	file:
+CXCLASS_CHECK	re.c	703;"	d	file:
+DBG_HASH	main_opt.h	/^		DBG_HASH  = 2 * DBG_OPS,$/;"	e	enum:Options::__anon31
+DBG_OPS	main_opt.h	/^		DBG_OPS   = 1,$/;"	e	enum:Options::__anon31
+DBG_REGEX	main_opt.h	/^		DBG_REGEX = 2 * DBG_HASH$/;"	e	enum:Options::__anon31
+DEBUG	config.h	5;"	d
+DEBUG	config.h	8;"	d
+DEBUG_P	config.h	6;"	d
+DEBUG_P	config.h	9;"	d
+DECL_PP1	pp.h	50;"	d
+DECL_PP1	pp.h	6;"	d
+DECL_PP2	pp.h	52;"	d
+DECL_PP2	pp.h	79;"	d
+DEFRBUF	re.c	1918;"	d	file:
+DELKV	hash.c	52;"	d	file:
+DEPARSE_H_	deparse.h	2;"	d
+DIR_END	config.h	14;"	d
+DIR_END	config.h	16;"	d
+DIR_END	config.h	19;"	d
+DI_NONE	IO.c	/^		DI_NONE,$/;"	e	enum:IO::__anon1	file:
+DI_RD	IO.c	/^		DI_RD,$/;"	e	enum:IO::__anon1	file:
+DI_WR	IO.c	/^		DI_WR$/;"	e	enum:IO::__anon1	file:
+DO_SLEEP	config.h	25;"	d
+DO_SLEEP	config.h	29;"	d
+DO_SLEEP	config.h	32;"	d
+ENDBRANCHP	re.c	200;"	d	file:
+ESCAPE	re.c	/^enum   {ESCAPE     =        '!'};$/;"	e	enum:__anon21	file:
+EXPR_H_	expr.h	2;"	d
+Err	main.c	/^IO *In, *Out, *Err;$/;"	v
+FL_ANCHOR_START	re.c	/^	FL_ANCHOR_START = 1$/;"	e	enum:re_flags	file:
+FL_NONE	re.c	/^	FL_NONE = 0,$/;"	e	enum:re_flags	file:
+F_ABS	expr.h	/^	F_ABS,$/;"	e	enum:t_func
+F_ACOS	expr.h	/^	F_ACOS,$/;"	e	enum:t_func
+F_ASIN	expr.h	/^	F_ASIN,$/;"	e	enum:t_func
+F_ATAN	expr.h	/^	F_ATAN,$/;"	e	enum:t_func
+F_ATAN2	expr.h	/^	F_ATAN2,$/;"	e	enum:t_func
+F_CALL	expr.h	/^	F_CALL = -1,$/;"	e	enum:t_func
+F_CATCH	expr.h	/^	F_CATCH,$/;"	e	enum:t_func
+F_CHR	expr.h	/^	F_CHR,$/;"	e	enum:t_func
+F_COS	expr.h	/^	F_COS,$/;"	e	enum:t_func
+F_DEFINED	expr.h	/^	F_DEFINED,$/;"	e	enum:t_func
+F_EOF	expr.h	/^	F_EOF,$/;"	e	enum:t_func
+F_ERROR	expr.h	/^	F_ERROR,$/;"	e	enum:t_func
+F_EXP	expr.h	/^	F_EXP,$/;"	e	enum:t_func
+F_FREEZE	expr.h	/^	F_FREEZE,$/;"	e	enum:t_func
+F_GETC	expr.h	/^	F_GETC,$/;"	e	enum:t_func
+F_GETENV	expr.h	/^	F_GETENV,$/;"	e	enum:t_func
+F_GETS	expr.h	/^	F_GETS,$/;"	e	enum:t_func
+F_HANG	expr.h	/^	F_HANG,$/;"	e	enum:t_func
+F_INT	expr.h	/^	F_INT,$/;"	e	enum:t_func
+F_IO	expr.h	/^	F_IO,$/;"	e	enum:t_func
+F_LENGTH	expr.h	/^	F_LENGTH,$/;"	e	enum:t_func
+F_LOG	expr.h	/^	F_LOG,$/;"	e	enum:t_func
+F_LOG10	expr.h	/^	F_LOG10,$/;"	e	enum:t_func
+F_LOWER	expr.h	/^	F_LOWER,$/;"	e	enum:t_func
+F_MATCH	expr.h	/^	F_MATCH,$/;"	e	enum:t_func
+F_MOEND	expr.h	/^	F_MOEND,$/;"	e	enum:t_func
+F_MOSTART	expr.h	/^	F_MOSTART,$/;"	e	enum:t_func
+F_NEG	expr.h	/^	F_NEG,$/;"	e	enum:t_func
+F_NOT	expr.h	/^	F_NOT,$/;"	e	enum:t_func
+F_NUL	expr.h	/^	F_NUL,$/;"	e	enum:t_func
+F_NUM	expr.h	/^	F_NUM,$/;"	e	enum:t_func
+F_OPEN	expr.h	/^	F_OPEN,$/;"	e	enum:t_func
+F_OPENR	expr.h	/^	F_OPENR,$/;"	e	enum:t_func
+F_OPENW	expr.h	/^	F_OPENW,$/;"	e	enum:t_func
+F_ORD	expr.h	/^	F_ORD,$/;"	e	enum:t_func
+F_QUOTE	expr.h	/^	F_QUOTE,$/;"	e	enum:t_func
+F_REMOVE	expr.h	/^	F_REMOVE,$/;"	e	enum:t_func
+F_RENAME	expr.h	/^	F_RENAME,$/;"	e	enum:t_func
+F_REVERSE	expr.h	/^	F_REVERSE,$/;"	e	enum:t_func
+F_RE_ESC	expr.h	/^	F_RE_ESC,$/;"	e	enum:t_func
+F_SEEK	expr.h	/^	F_SEEK,$/;"	e	enum:t_func
+F_SIN	expr.h	/^	F_SIN,$/;"	e	enum:t_func
+F_SQRT	expr.h	/^	F_SQRT,$/;"	e	enum:t_func
+F_STR	expr.h	/^	F_STR,$/;"	e	enum:t_func
+F_TAN	expr.h	/^	F_TAN,$/;"	e	enum:t_func
+F_TELL	expr.h	/^	F_TELL,$/;"	e	enum:t_func
+F_TYPEOF	expr.h	/^	F_TYPEOF,$/;"	e	enum:t_func
+F_UPPER	expr.h	/^	F_UPPER$/;"	e	enum:t_func
+HANG_H_	hang.h	2;"	d
+HASH_H_	hash.h	2;"	d
+HAVE_C99	config.h	58;"	d
+HAVE_C99	config.h	61;"	d
+HAVE_C99_P	config.h	57;"	d
+HAVE_C99_P	config.h	60;"	d
+HAVE_DEV_URANDOM_P	config.h	36;"	d
+HAVE_DEV_URANDOM_P	config.h	38;"	d
+HAVE_GCC	config.h	66;"	d
+HAVE_GCC	config.h	69;"	d
+HAVE_GCC_P	config.h	65;"	d
+HAVE_GCC_P	config.h	68;"	d
+HAVE_SLEEP_P	config.h	23;"	d
+HAVE_SLEEP_P	config.h	27;"	d
+HAVE_SLEEP_P	config.h	31;"	d
+HAVE_VSNPRINTF	config.h	103;"	d
+HAVE_VSNPRINTF	config.h	106;"	d
+HAVE_VSNPRINTF	config.h	110;"	d
+HAVE_VSNPRINTF	config.h	98;"	d
+HAVE_VSNPRINTF_P	config.h	104;"	d
+HAVE_VSNPRINTF_P	config.h	107;"	d
+HAVE_VSNPRINTF_P	config.h	111;"	d
+HAVE_VSNPRINTF_P	config.h	99;"	d
+H_EXIST	hash.h	/^	H_EXIST,$/;"	e	enum:__anon30
+H_NOENT	hash.h	/^	H_NOENT$/;"	e	enum:__anon30
+H_OK	hash.h	/^	H_OK,$/;"	e	enum:__anon30
+Hash	hash.h	/^} Hash;$/;"	t	typeref:struct:__anon29
+IF_DB	xmalloc.c	12;"	d	file:
+IF_DB	xmalloc.c	14;"	d	file:
+INC_H_	inc.h	2;"	d
+INC_PREFIX	config.h	43;"	d
+INC_PREFIX	config.h	45;"	d
+INC_PREFIX_LIST	config.h	49;"	d
+INITRBUF	re.c	1920;"	d	file:
+IO	IO.c	/^struct IO {$/;"	s	file:
+IO	IO.h	/^typedef struct IO IO;$/;"	t	typeref:struct:IO
+IOS_CUR	IO.h	/^	IOS_CUR   = SEEK_CUR,$/;"	e	enum:io_whence
+IOS_END	IO.h	/^	IOS_END   = SEEK_END$/;"	e	enum:io_whence
+IOS_START	IO.h	/^	IOS_START = SEEK_SET,$/;"	e	enum:io_whence
+IO_APPEND	IO.h	/^	IO_APPEND    = 2 * IO_WRITE,$/;"	e	enum:io_flags
+IO_AUTOFLUSH	IO.h	/^	IO_AUTOFLUSH = 2 * IO_BUFFERED$/;"	e	enum:io_flags
+IO_BINARY	IO.h	/^	IO_BINARY    = 1,$/;"	e	enum:io_flags
+IO_BUFFERED	IO.h	/^	IO_BUFFERED  = 2 * IO_TRUNCATE,$/;"	e	enum:io_flags
+IO_H_	IO.h	2;"	d
+IO_READ	IO.h	/^	IO_READ      = 2 * IO_BINARY,$/;"	e	enum:io_flags
+IO_TRUNCATE	IO.h	/^	IO_TRUNCATE  = 2 * IO_APPEND,$/;"	e	enum:io_flags
+IO_WRITE	IO.h	/^	IO_WRITE     = 2 * IO_READ,$/;"	e	enum:io_flags
+In	main.c	/^IO *In, *Out, *Err;$/;"	v
+Interp	run.c	/^struct Interp Interp;$/;"	v	typeref:struct:Interp
+Interp	run.h	/^struct Interp {$/;"	s
+KORK_H_	kork.h	2;"	d
+LIST_H_	list.h	2;"	d
+LOGIC	pp.c	561;"	d	file:
+MAGIC	Str.c	/^enum {MAGIC = 4};$/;"	e	enum:__anon2	file:
+MAGIC	atechit.c	/^enum {MAGIC = 23};$/;"	e	enum:__anon3	file:
+MAGIC	deparse.c	/^enum {MAGIC = 23};$/;"	e	enum:__anon5	file:
+MAGIC	hash.c	/^enum {MAGIC = 112};$/;"	e	enum:__anon6	file:
+MAGIC	list.c	/^enum {MAGIC = 7};$/;"	e	enum:__anon7	file:
+MAGIC	mars.c	/^enum {MAGIC = 128};$/;"	e	enum:__anon8	file:
+MAGIC	re.c	/^enum {MAGIC = 23};$/;"	e	enum:__anon10	file:
+MAGIC	text.c	/^enum {MAGIC = 42};$/;"	e	enum:__anon22	file:
+MAGIC	variable.c	/^enum {MAGIC = 23};$/;"	e	enum:__anon23	file:
+MAGIC	venus.c	/^enum {MAGIC = 23};$/;"	e	enum:__anon24	file:
+MAIN_H_	main.h	2;"	d
+MAIN_IO_H_	main_io.h	2;"	d
+MAIN_LABEL_H_	main_label.h	2;"	d
+MAIN_OPT_H_	main_opt.h	2;"	d
+MAIN_VAR_H_	main_var.h	2;"	d
+MAKE_LABEL	deparse.c	18;"	d	file:
+MAKE_LABEL	deparse.c	25;"	d	file:
+MARS_H_	mars.h	2;"	d
+MATCH_H_	match.h	2;"	d
+MEATCHARP	re.c	199;"	d	file:
+MIN	Str.c	735;"	d	file:
+MRETURN	re.c	1919;"	d	file:
+MY_E	expr.c	37;"	d	file:
+MY_E	expr.c	39;"	d	file:
+MY_PI	expr.c	31;"	d	file:
+MY_PI	expr.c	33;"	d	file:
+Mars	main.c	/^struct mars Mars;$/;"	v	typeref:struct:mars
+Mmatch0	re.c	1443;"	d	file:
+Mmatch1	re.c	1542;"	d	file:
+NAME	main.c	34;"	d	file:
+NCMP	Str.c	191;"	d	file:
+NDEBUG	config.h	10;"	d
+NOTREACHED	zz.h	9;"	d
+NO_MATCH	re.c	1441;"	d	file:
+NUMB	pp.c	548;"	d	file:
+OFFSET_OFF	Str.c	17;"	d	file:
+OPERATORS	expr.h	164;"	d
+OPERATOR_P	expr.h	165;"	d
+OPT_H_	opt.h	2;"	d
+OP_1ARG_P	op.h	52;"	d
+OP_2ARG_P	op.h	60;"	d
+OP_ASSIGN	op.h	/^	OP_ASSIGN,$/;"	e	enum:t_op
+OP_CALL	op.h	/^	OP_CALL,$/;"	e	enum:t_op
+OP_CALL_BACK	op.h	/^	OP_CALL_BACK,$/;"	e	enum:t_op
+OP_CALL_DYN	op.h	/^	OP_CALL_DYN,$/;"	e	enum:t_op
+OP_CLOSE	op.h	/^	OP_CLOSE,$/;"	e	enum:t_op
+OP_ELSE	op.h	/^	OP_ELSE,$/;"	e	enum:t_op
+OP_EXIT	op.h	/^	OP_EXIT,$/;"	e	enum:t_op
+OP_FI	op.h	/^	OP_FI,$/;"	e	enum:t_op
+OP_FLUSH	op.h	/^	OP_FLUSH,$/;"	e	enum:t_op
+OP_GOBACK	op.h	/^	OP_GOBACK,$/;"	e	enum:t_op
+OP_GOTO	op.h	/^	OP_GOTO,$/;"	e	enum:t_op
+OP_HANG	op.h	/^	OP_HANG,$/;"	e	enum:t_op
+OP_H_	op.h	2;"	d
+OP_IF	op.h	/^	OP_IF,$/;"	e	enum:t_op
+OP_MODIFY	op.h	/^	OP_MODIFY,$/;"	e	enum:t_op
+OP_NOP	op.h	/^	OP_NOP,$/;"	e	enum:t_op
+OP_PRINT	op.h	/^	OP_PRINT,$/;"	e	enum:t_op
+OP_PUTC	op.h	/^	OP_PUTC,$/;"	e	enum:t_op
+OP_RESET	op.h	/^	OP_RESET,$/;"	e	enum:t_op
+OP_RETURN	op.h	/^	OP_RETURN,$/;"	e	enum:t_op
+OP_SET_VAL	op.h	/^	OP_SET_VAL,$/;"	e	enum:t_op
+OP_SYSTEM	op.h	/^	OP_SYSTEM,$/;"	e	enum:t_op
+OP_TEMP	op.h	/^	OP_TEMP,$/;"	e	enum:t_op
+OP_THROW	op.h	/^	OP_THROW$/;"	e	enum:t_op
+Opt	main.c	/^struct Options Opt;$/;"	v	typeref:struct:Options
+Options	main_opt.h	/^struct Options {$/;"	s
+Out	main.c	/^IO *In, *Out, *Err;$/;"	v
+PARSE_H_	parse.h	2;"	d
+PMODE	IO.c	260;"	d	file:
+PP_H_	pp.h	2;"	d
+PRAZ	re.c	/^PRAZ(alternation) {$/;"	f
+PRAZ	re.c	/^PRAZ(atom) {$/;"	f
+PRAZ	re.c	/^PRAZ(branch) {$/;"	f
+PRAZ	re.c	/^PRAZ(literal) {$/;"	f
+PRAZ	re.c	/^PRAZ(quantifire) {$/;"	f
+PRAZ	re.c	229;"	d	file:
+PUSH_SEEN	deparse.c	517;"	d	file:
+Prog	main.c	/^const char *Prog = NAME;$/;"	v
+RANDOM_H_	random.h	2;"	d
+RETURN_REP	re.c	894;"	d	file:
+RE_ALTER	re.c	/^	RE_ALTER,$/;"	e	enum:t_re_node	file:
+RE_ANYCH	re.c	/^	RE_ANYCH,$/;"	e	enum:t_re_node	file:
+RE_AT_BOL	re.c	/^	RE_AT_BOL,$/;"	e	enum:t_re_node	file:
+RE_AT_BOS	re.c	/^	RE_AT_BOS,$/;"	e	enum:t_re_node	file:
+RE_AT_EOL	re.c	/^	RE_AT_EOL,$/;"	e	enum:t_re_node	file:
+RE_AT_EOS	re.c	/^	RE_AT_EOS,$/;"	e	enum:t_re_node	file:
+RE_AT_MATCH	re.c	/^	RE_AT_MATCH,$/;"	e	enum:t_re_node	file:
+RE_AT_NOMATCH	re.c	/^	RE_AT_NOMATCH,$/;"	e	enum:t_re_node	file:
+RE_AT_NWBOUND	re.c	/^	RE_AT_NWBOUND,$/;"	e	enum:t_re_node	file:
+RE_AT_WBOUND	re.c	/^	RE_AT_WBOUND,$/;"	e	enum:t_re_node	file:
+RE_BACKCHECK	re.c	/^	RE_BACKCHECK,$/;"	e	enum:t_re_node	file:
+RE_BACKREF	re.c	/^	RE_BACKREF,$/;"	e	enum:t_re_node	file:
+RE_BEGIN_CAPTURE	re.c	/^	RE_BEGIN_CAPTURE,$/;"	e	enum:t_re_node	file:
+RE_CLASS	re.c	/^	RE_CLASS,$/;"	e	enum:t_re_node	file:
+RE_DEFCLASS	re.c	/^	RE_DEFCLASS,$/;"	e	enum:t_re_node	file:
+RE_END_CAPTURE	re.c	/^	RE_END_CAPTURE,$/;"	e	enum:t_re_node	file:
+RE_H_	re.h	2;"	d
+RE_INDEP	re.c	/^	RE_INDEP,$/;"	e	enum:t_re_node	file:
+RE_LITERAL	re.c	/^	RE_LITERAL,$/;"	e	enum:t_re_node	file:
+RE_N_CLASS	re.c	/^	RE_N_CLASS,$/;"	e	enum:t_re_node	file:
+RE_N_DEFCLASS	re.c	/^	RE_N_DEFCLASS,$/;"	e	enum:t_re_node	file:
+RE_PARTIAL	re.c	/^	RE_PARTIAL,$/;"	e	enum:t_re_node	file:
+RE_REP_FRUGAL	re.c	/^	RE_REP_FRUGAL,$/;"	e	enum:t_re_node	file:
+RE_REP_GREEDY	re.c	/^	RE_REP_GREEDY,$/;"	e	enum:t_re_node	file:
+RE_SELECT	re.c	/^	RE_SELECT,$/;"	e	enum:t_re_node	file:
+RE_STUFF_FRUGAL	re.c	/^	RE_STUFF_FRUGAL,$/;"	e	enum:t_re_node	file:
+RE_STUFF_GREEDY	re.c	/^	RE_STUFF_GREEDY,$/;"	e	enum:t_re_node	file:
+RE_XREP_FRUGAL	re.c	/^	RE_XREP_FRUGAL,$/;"	e	enum:t_re_node	file:
+RE_XREP_GREEDY	re.c	/^	RE_XREP_GREEDY$/;"	e	enum:t_re_node	file:
+RINT	val.h	78;"	d
+RMODE	IO.c	261;"	d	file:
+RSTR	Str.c	317;"	d	file:
+RUN_H_	run.h	2;"	d
+Root	IO.c	/^static IO *Root;$/;"	v	file:
+Root	xmalloc.c	/^} *Root;$/;"	v	typeref:struct:node	file:
+SLEEP_HEADER	config.h	24;"	d
+SLEEP_HEADER	config.h	28;"	d
+SNAP	transmogrify.c	288;"	d	file:
+SNORK	re.c	230;"	d	file:
+STACK_H_	stack.h	2;"	d
+STD_REP_CASE	re.c	906;"	d	file:
+STR	Str.c	284;"	d	file:
+STRHASH_H_	strhash.h	2;"	d
+STRUTIL_H_	strutil.h	2;"	d
+STR_H_	Str.h	2;"	d
+ST_FIRSTCHAR	Str.h	140;"	d
+ST_INDEX	Str.h	141;"	d
+ST_LASTCHAR	Str.h	139;"	d
+ST_WRITE	Str.h	142;"	d
+SUB_H_	sub.h	2;"	d
+S_ARG	expr.h	/^	S_ARG,$/;"	e	enum:t_symbol
+S_ARGC	expr.h	/^	S_ARGC,$/;"	e	enum:t_symbol
+S_ARGV	expr.h	/^	S_ARGV,$/;"	e	enum:t_symbol
+S_ERR	expr.h	/^	S_ERR,$/;"	e	enum:t_symbol
+S_EULER	expr.h	/^	S_EULER,$/;"	e	enum:t_symbol
+S_LUDOLF	expr.h	/^	S_LUDOLF,$/;"	e	enum:t_symbol
+S_MATCH	expr.h	/^	S_MATCH,$/;"	e	enum:t_symbol
+S_NUL	expr.h	/^	S_NUL,$/;"	e	enum:t_symbol
+S_RAND	expr.h	/^	S_RAND,$/;"	e	enum:t_symbol
+S_RESULT	expr.h	/^	S_RESULT,$/;"	e	enum:t_symbol
+S_STDERR	expr.h	/^	S_STDERR$/;"	e	enum:t_symbol
+S_STDIN	expr.h	/^	S_STDIN,$/;"	e	enum:t_symbol
+S_STDOUT	expr.h	/^	S_STDOUT,$/;"	e	enum:t_symbol
+St_cat	Str.c	/^St_cat(String *s, const String *t) {$/;"	f
+St_cat_c	Str.c	/^St_cat_c(String *s, int c) {$/;"	f
+St_cat_m	Str.c	/^St_cat_m(String *s, const void *m, size_t n) {$/;"	f
+St_cat_s	Str.c	/^St_cat_s(String *s, const char *tz) {$/;"	f
+St_chr	Str.c	/^St_chr(const String *s, int c) {$/;"	f
+St_clear	Str.c	/^St_clear(String *s) {$/;"	f
+St_cmp	Str.c	/^St_cmp(const String *s, const String *t) {$/;"	f
+St_cmp_m	Str.c	/^St_cmp_m(const String *s, const void *m, size_t n) {$/;"	f
+St_cpy	Str.c	/^St_cpy(String *s, const String *t) {$/;"	f
+St_cpy_c	Str.c	/^St_cpy_c(String *s, int c) {$/;"	f
+St_cpy_m	Str.c	/^St_cpy_m(String *s, const void *m, size_t n) {$/;"	f
+St_cpy_s	Str.c	/^St_cpy_s(String *s, const char *tz) {$/;"	f
+St_del	Str.c	/^St_del(String *s, size_t p, size_t n) {$/;"	f
+St_fake	Str.c	/^St_fake(String *s, char *p, size_t n) {$/;"	f
+St_grep	Str.c	/^St_grep(String *s, int (*good)(int)) {$/;"	f
+St_grow	Str.c	/^St_grow(String *s, size_t n) {$/;"	f	file:
+St_hash	Str.c	/^St_hash(const String *s, size_t h) {$/;"	f
+St_init	Str.c	/^St_init(String *s) {$/;"	f
+St_len	Str.h	137;"	d
+St_lower	Str.c	/^St_lower(String *s) {$/;"	f
+St_ncmp_m	Str.c	/^St_ncmp_m(const String *s, const void *m, size_t n) {$/;"	f
+St_num	Str.c	/^St_num(String *s, double d) {$/;"	f
+St_ptr	Str.h	136;"	d
+St_read	Str.c	/^St_read(String *s, FILE *fp, size_t n) {$/;"	f
+St_reverse	Str.c	/^St_reverse(String *s) {$/;"	f
+St_rstr_m	Str.c	/^St_rstr_m(const String *s, const void *m, size_t n) {$/;"	f
+St_rstr_s	Str.c	/^St_rstr_s(const String *s, const char *tz) {$/;"	f
+St_rstro_m	Str.c	/^St_rstro_m(const String *s, size_t off, const void *m, size_t n) {$/;"	f
+St_shift	Str.c	/^St_shift(String *s) {$/;"	f
+St_shiftws	Str.c	/^St_shiftws(String *s) {$/;"	f
+St_str	Str.c	/^St_str(const String *s, const String *t) {$/;"	f
+St_str_m	Str.c	/^St_str_m(const String *s, const void *m, size_t n) {$/;"	f
+St_stro_m	Str.c	/^St_stro_m(const String *s, size_t off, const void *m, size_t n) {$/;"	f
+St_substr	Str.c	/^St_substr(String *l, String *s, size_t p, size_t n, const String *r) {$/;"	f
+St_tac_c	Str.c	/^St_tac_c(String *s, int c) {$/;"	f
+St_tac_m	Str.c	/^St_tac_m(String *s, const void *m, size_t n) {$/;"	f
+St_tac_s	Str.c	/^St_tac_s(String *s, const char *tz) {$/;"	f
+St_trunc	Str.c	/^St_trunc(String *s, size_t n) {$/;"	f
+St_upper	Str.c	/^St_upper(String *s) {$/;"	f
+St_xprintf	Str.c	/^St_xprintf(String *s, const char *fmt, ...) {$/;"	f
+St_zero	Str.h	138;"	d
+String	Str.h	/^} String;$/;"	t	typeref:struct:__anon25
+TAC	Str.c	470;"	d	file:
+TAILC	re.c	232;"	d	file:
+TEXT_H_	text.h	2;"	d
+TOLABEL	expr.h	167;"	d
+TOS	expr.c	785;"	d	file:
+TRANSMOGRIFY_H_	transmogrify.h	2;"	d
+VAL_H_	val.h	2;"	d
+VARIABLE_H_	variable.h	2;"	d
+VENUS_H_	venus.h	2;"	d
+VERSION_H_	version.h	2;"	d
+VR_NO_COOKIE	variable.h	10;"	d
+V_EXT_K	val.h	/^	V_EXT_K = V_NUM_K << 1,$/;"	e	enum:val_cont
+V_EXT_P	val.h	57;"	d
+V_LIST_K	val.h	/^	V_LIST_K = V_SUB_K << 1$/;"	e	enum:val_cont
+V_LIST_P	val.h	59;"	d
+V_NUM	val.h	62;"	d
+V_NUM_K	val.h	/^	V_NUM_K = V_STR_K << 1,$/;"	e	enum:val_cont
+V_NUM_P	val.h	56;"	d
+V_STR	val.h	61;"	d
+V_STR_K	val.h	/^	V_STR_K = 1,$/;"	e	enum:val_cont
+V_STR_P	val.h	55;"	d
+V_SUB_K	val.h	/^	V_SUB_K = V_EXT_K << 1,$/;"	e	enum:val_cont
+V_SUB_P	val.h	58;"	d
+V_UNDEF	val.h	/^	V_UNDEF = 0,$/;"	e	enum:val_cont
+V_xxx_OFF	val.h	64;"	d
+Var_hash	main.c	/^t_vr_container *Var_plain, *Var_hash;$/;"	v
+Var_plain	main.c	/^t_vr_container *Var_plain, *Var_hash;$/;"	v
+Venus	main.c	/^struct venus Venus;$/;"	v	typeref:struct:venus
+Version	version.c	/^const char *Version =$/;"	v
+WARN	main.c	35;"	d	file:
+WMODE	IO.c	259;"	d	file:
+XFACTOR	xmalloc.c	61;"	d	file:
+XMALLOC_H_	xmalloc.h	2;"	d
+XMODE	IO.c	251;"	d	file:
+XRESIZN	hash.c	107;"	d	file:
+XSTEP	hash.c	108;"	d	file:
+ZZ_H_	zz.h	2;"	d
+a	run.h	/^	} a;$/;"	m	struct:Interp	typeref:struct:Interp::__anon35
+alloc	re.c	/^	t_block alloc;$/;"	m	struct:my_regex	file:
+alter	re.c	/^	} alter;$/;"	m	union:my_node	typeref:struct:my_node::__anon12	file:
+alternation	re.c	/^PRAZ(alternation);$/;"	v
+anchorbegp	re.c	/^static int anchorbegp(const re_node *node) {$/;"	f	file:
+arg	op.h	/^	struct expr *arg;$/;"	m	struct:op	typeref:struct:op::expr
+arg	re.c	/^		union my_node *arg;$/;"	m	struct:my_node::__anon12	typeref:union:my_node::__anon12::my_node	file:
+arg	re.c	/^		union my_node *arg;$/;"	m	struct:my_node::__anon17	typeref:union:my_node::__anon17::my_node	file:
+arg	re.c	/^		union my_node *arg;$/;"	m	struct:my_node::__anon19	typeref:union:my_node::__anon19::my_node	file:
+arg	re.c	/^		union my_node *arg;$/;"	m	struct:my_node::__anon20	typeref:union:my_node::__anon20::my_node	file:
+arg	run.h	/^	struct val arg;$/;"	m	struct:Interp	typeref:struct:Interp::val
+argc	run.h	/^		size_t argc;$/;"	m	struct:Interp::__anon35
+argv	run.h	/^		struct val *argv;$/;"	m	struct:Interp::__anon35	typeref:struct:Interp::__anon35::val
+arh	op.h	/^	} arh;$/;"	m	struct:op	typeref:union:op::__anon32
+array	venus.c	/^	struct op **array;$/;"	m	struct:sorted	typeref:struct:sorted::op	file:
+atechit	atechit.c	/^void atechit(void (*f)(void)) {$/;"	f
+backref	re.c	/^	} backref;$/;"	m	union:my_node	typeref:struct:my_node::__anon13	file:
+bah	atechit.c	/^static void bah(void) {$/;"	f	file:
+base	re.c	/^	t_regex *base;$/;"	m	struct:parse_context	file:
+binopE	expr.h	/^	binopE,$/;"	e	enum:t_expr
+bl_free	re_block.c.h	/^static void bl_free(t_block *b) {$/;"	f
+bl_init	re_block.c.h	/^static void bl_init(t_block *b) {$/;"	f
+bl_node	re_block.c.h	/^static re_node *bl_node(t_block *b) {$/;"	f
+bl_string	re_block.c.h	/^static unsigned char *bl_string(t_block *b, size_t len) {$/;"	f
+block	re.c	/^	t_block *block;$/;"	m	struct:parse_context	file:
+block_node	re_block.c.h	/^struct block_node {$/;"	s
+bonus	expr.h	/^		size_t bonus;$/;"	m	union:expr::__anon27
+branch	re.c	/^PRAZ(branch);$/;"	v
+branchend	re.c	/^static const re_node *branchend(const re_node *a, const re_node *b) {$/;"	f	file:
+brk	hash.h	/^	size_t brk;$/;"	m	struct:__anon29
+buf	IO.c	/^	String *buf;$/;"	m	struct:IO	file:
+buf	Str.h	/^	char *buf;$/;"	m	struct:__anon25
+buf	expr.h	/^	jmp_buf buf;$/;"	m	struct:__anon28
+buf	re.c	/^		unsigned char *buf;$/;"	m	struct:my_node::__anon18	file:
+buf	re_block.c.h	/^	unsigned char *buf;$/;"	m	struct:txt_node
+bufk	IO.c	/^static void bufk(IO *f, size_t n) {$/;"	f	file:
+cached	re.c	/^static String cached[MAGIC];$/;"	v	file:
+cachfill	re.c	/^static size_t cachfill, cachlast;$/;"	v	file:
+cachlast	re.c	/^static size_t cachfill, cachlast;$/;"	v	file:
+cap_ptr	re.c	/^	struct cap_state *cap_ptr;$/;"	m	struct:my_regex	typeref:struct:my_regex::cap_state	file:
+cap_state	re.c	/^struct cap_state {$/;"	s	file:
+capture	re.c	/^	} capture;$/;"	m	union:my_node	typeref:struct:my_node::__anon14	file:
+captures	re.c	/^	size_t captures;$/;"	m	struct:my_regex	file:
+cat	Str.c	/^static void cat(String *s, const void *m, size_t n) {$/;"	f	file:
+cat_n	re.c	/^static void cat_n(String *s, unsigned long n) {$/;"	f	file:
+cdefclass	re.c	/^static int cdefclass(const unsigned char *ptr) {$/;"	f	file:
+class	re.c	/^	} class;$/;"	m	union:my_node	typeref:struct:my_node::__anon15	file:
+cleanup	run.c	/^static void cleanup(void) {$/;"	f	file:
+cmp	hash.h	/^	int (*cmp)(const void *, const void *);$/;"	m	struct:__anon29
+compar	re.c	/^static int compar(const void *a, const void *b) {$/;"	f	file:
+compar	strhash.c	/^static int compar(const void *ap, const void *bp) {$/;"	f	file:
+compar	venus.c	/^static int compar(const void *a, const void *b) {$/;"	f	file:
+compile	compile.c	/^void compile(struct text *code) {$/;"	f
+con_error	expr.c	/^static void con_error(const t_context *x, const t_context *y) {$/;"	f	file:
+con_nop	expr.c	/^static void con_nop(t_context *c) {$/;"	f	file:
+cond	re.c	/^		union my_node *cond;$/;"	m	struct:my_node::__anon20	typeref:union:my_node::__anon20::my_node	file:
+const_binop	transmogrify.c	51;"	d	file:
+const_unop	transmogrify.c	37;"	d	file:
+content	run.h	/^	struct val content;$/;"	m	struct:__anon39	typeref:struct:__anon39::val
+data	mars.h	/^	struct op *data;$/;"	m	struct:mars	typeref:struct:mars::op
+data	variable.h	/^	void **data;$/;"	m	struct:__anon41
+dc_alpha	re.c	/^static unsigned char dc_alpha[CLASS_SIZE];$/;"	v	file:
+dc_cntrl	re.c	/^static unsigned char dc_cntrl[CLASS_SIZE];$/;"	v	file:
+dc_digit	re.c	/^static unsigned char dc_digit[CLASS_SIZE];$/;"	v	file:
+dc_lower	re.c	/^static unsigned char dc_lower[CLASS_SIZE];$/;"	v	file:
+dc_print	re.c	/^static unsigned char dc_print[CLASS_SIZE];$/;"	v	file:
+dc_space	re.c	/^static unsigned char dc_space[CLASS_SIZE];$/;"	v	file:
+dc_upper	re.c	/^static unsigned char dc_upper[CLASS_SIZE];$/;"	v	file:
+dc_word	re.c	/^static unsigned char dc_word[CLASS_SIZE];$/;"	v	file:
+dc_xdigit	re.c	/^static unsigned char dc_xdigit[CLASS_SIZE];$/;"	v	file:
+debug	main_opt.h	/^	} debug;$/;"	m	struct:Options	typeref:enum:Options::__anon31
+decom_class	re.c	/^static void decom_class(String *s, const unsigned char vec[HAVE_C99(static CLASS_SIZE)]) {$/;"	f	file:
+decom_liter	re.c	/^static void decom_liter(String *s, const re_node *node) {$/;"	f	file:
+decom_node	re.c	/^static void decom_node(String *s, const re_node *node, const re_node *end) {$/;"	f	file:
+decom_repeat	re.c	/^static void decom_repeat(String *s, const re_node *node) {$/;"	f	file:
+decouple	list.c	/^static void decouple(struct list *l, size_t slack) {$/;"	f	file:
+decr	kork.c	/^static void decr(dolphin *dp) {$/;"	f	file:
+decr	list.c	/^static void decr(whale *wp) {$/;"	f	file:
+defclass	re.c	/^	} defclass;$/;"	m	union:my_node	typeref:struct:my_node::__anon16	file:
+del	variable.h	/^	void (*del)(void *);$/;"	m	struct:__anon41
+delcookie	variable.c	/^static void delcookie(void *c) {$/;"	f	file:
+delk	hash.h	/^	void (*delk)(void *);$/;"	m	struct:__anon29
+delk	strhash.c	/^static void delk(void *p) {$/;"	f	file:
+delk	venus.c	/^static void delk(void *k) {$/;"	f	file:
+delv	hash.h	/^	void (*delv)(void *);$/;"	m	struct:__anon29
+delv	re.c	/^static void delv(void *v) {$/;"	f	file:
+delv	venus.c	/^static void delv(void *v) {$/;"	f	file:
+deparse	deparse.c	/^void deparse(const struct text *t) {$/;"	f
+deparse	main_opt.h	/^	int deparse;$/;"	m	struct:Options
+depth	expr.h	/^	size_t depth;$/;"	m	struct:__anon28
+depth_get	run.c	/^size_t depth_get(void) {$/;"	f
+depth_restore	run.c	/^void depth_restore(size_t n) {$/;"	f
+dirct	IO.c	/^	} dirct;$/;"	m	struct:IO	typeref:enum:IO::__anon1	file:
+display	deparse.c	/^static int display(enum t_binop b) {$/;"	f	file:
+do_indent	re.c	/^static void do_indent(FILE *fp, size_t n) {$/;"	f	file:
+do_match	match.c	/^void do_match(struct val *v, t_regex *re) {$/;"	f
+do_stuff	deparse.c	/^static void do_stuff(struct op *op) {$/;"	f	file:
+dolphin	kork.c	/^typedef struct ko_dolphin dolphin;$/;"	t	typeref:struct:ko_dolphin	file:
+dostuff	re.c	/^static void dostuff(t_regex *re) {$/;"	f	file:
+dump_expr	deparse.c	/^static void dump_expr(const struct expr *e, int inlist) {$/;"	f	file:
+dump_ko	deparse.c	/^static void dump_ko(const struct kork *k) {$/;"	f	file:
+dump_op	deparse.c	/^static void dump_op(struct op *op) {$/;"	f	file:
+dump_str	deparse.c	/^static void dump_str(const String *s) {$/;"	f	file:
+dumpclass	re.c	/^static void dumpclass(FILE *fp, const unsigned char vec[HAVE_C99(static CLASS_SIZE)]) {$/;"	f	file:
+dumpliter	re.c	/^static void dumpliter(FILE *fp, const re_node *node) {$/;"	f	file:
+dumpnode	re.c	/^static void dumpnode(FILE *fp, const re_node *node, const re_node *end, const size_t indent) {$/;"	f	file:
+dup	sub.c	/^static struct expr *dup(const struct expr *e) {$/;"	f	file:
+end	re.c	/^	size_t start, end, pending;$/;"	m	struct:cap_state	file:
+end_if	compile.c	/^static struct op *end_if(struct text *code, size_t *n) {$/;"	f	file:
+entries	hash.h	/^	size_t entries, size, newsize;$/;"	m	struct:__anon29
+entry	hash.c	/^	struct h_entry entry;$/;"	m	struct:h_node	typeref:struct:h_node::h_entry	file:
+eval_expr	expr.c	/^struct val *eval_expr(struct expr *e) {$/;"	f
+eval_into	expr.c	/^void eval_into(struct expr *e, struct val *v) {$/;"	f
+eval_pop	expr.c	/^struct val *eval_pop(void) {$/;"	f
+eval_push	expr.c	/^void eval_push(struct expr *ex) {$/;"	f
+execute	run.c	/^struct val *execute(const struct op *op, struct val *arg) {$/;"	f
+expr	expr.h	/^		struct expr *expr;$/;"	m	union:expr::__anon27	typeref:struct:expr::__anon27::expr
+expr	expr.h	/^struct expr {$/;"	s
+expr	op.h	/^		struct expr *expr;$/;"	m	union:op::__anon32	typeref:struct:op::__anon32::expr
+expr	sub.c	/^	struct expr *expr;$/;"	m	struct:sub	typeref:struct:sub::expr	file:
+expr_binop	expr.c	/^enum t_binop expr_binop(int c) {$/;"	f
+expr_end	expr.c	/^void expr_end(void) {$/;"	f
+expr_init	expr.c	/^void expr_init(void) {$/;"	f
+expr_pp	expr.c	/^void expr_pp(enum t_binop op, struct val *x, struct val *y) {$/;"	f
+ext	val.h	/^		IO *ext;$/;"	m	union:val::__anon40
+field	list.h	/^	struct val **field;$/;"	m	struct:li_whale	typeref:struct:li_whale::val
+file	xmalloc.c	/^		const char *file;$/;"	m	struct:node	file:
+fill	re.c	/^static void fill(unsigned char *v, int (*pred)(int)) {$/;"	f	file:
+flags	re.c	/^	enum re_flags flags;$/;"	m	struct:my_regex	typeref:enum:my_regex::re_flags	file:
+fp	IO.c	/^	FILE *fp;$/;"	m	struct:IO	file:
+free_expr	expr.c	/^void free_expr(struct expr *x) {$/;"	f
+get_expr	expr.c	/^struct expr *get_expr(struct op *op) {$/;"	f
+get_iobj	expr.c	/^struct expr *get_iobj(struct op *op) {$/;"	f
+get_list	expr.c	/^static struct expr *get_list(struct op *op, int null) {$/;"	f	file:
+get_lval	expr.c	/^struct expr *get_lval(struct op *op) {$/;"	f
+get_value	expr.c	/^static struct expr *get_value(struct op *op, int null) {$/;"	f	file:
+h_del	hash.c	/^int h_del(Hash *h, const void *key) {$/;"	f
+h_end	hash.c	/^void h_end(Hash *h) {$/;"	f
+h_entry	hash.c	/^struct h_entry {$/;"	s	file:
+h_get	hash.c	/^int h_get(Hash *h, const void *key, void **res) {$/;"	f
+h_init	hash.c	/^void h_init($/;"	f
+h_nextkv	hash.c	/^int h_nextkv(Hash *h, void **k, void **v) {$/;"	f
+h_node	hash.c	/^struct h_node {$/;"	s	file:
+h_put	hash.c	/^int h_put(Hash *h, void *key, void *val, int replace) {$/;"	f
+h_reset	hash.c	/^void h_reset(Hash *h) {$/;"	f
+hang	hang.c	/^void hang(void) {$/;"	f
+hash	expr.h	/^		t_strhash *hash;$/;"	m	union:expr::__anon26
+hash	hash.c	/^	size_t hash;$/;"	m	struct:h_entry	file:
+hash	hash.h	/^	size_t (*hash)(const void *, size_t);$/;"	m	struct:__anon29
+hash	re.c	/^static size_t hash(const void *s, size_t seed) {$/;"	f	file:
+hash	strhash.c	/^	Hash hash;$/;"	m	struct:strhash	file:
+hash	strhash.c	/^static size_t hash(const void *p, size_t h) {$/;"	f	file:
+hash	variable.h	/^	t_strhash *hash;$/;"	m	struct:__anon41
+hash	venus.c	/^static size_t hash(const void *p, size_t seed) {$/;"	f	file:
+hp_expr	transmogrify.c	/^static int hp_expr(const struct expr *e) {$/;"	f	file:
+id	sub.c	/^	size_t id;$/;"	m	struct:sub	file:
+inc_ludes	inc.c	/^const char *inc_ludes[] = INC_PREFIX_LIST;$/;"	v
+incr	kork.c	/^static dolphin *incr(dolphin *dp) {$/;"	f	file:
+incr	list.c	/^static whale *incr(whale *wp) {$/;"	f	file:
+indep	re.c	/^	} indep;$/;"	m	union:my_node	typeref:struct:my_node::__anon17	file:
+index	run.h	/^		size_t *index;$/;"	m	struct:Interp::__anon37
+index	run.h	/^		size_t *index;$/;"	m	struct:Interp::__anon38
+io_bufptr	IO.c	/^const char *io_bufptr(IO *io) {$/;"	f
+io_bufred	IO.c	/^int io_bufred(const IO *io) {$/;"	f
+io_clearerr	IO.c	/^void io_clearerr(IO *f) {$/;"	f
+io_close	IO.c	/^int io_close(IO *io) {$/;"	f
+io_cmppeek	IO.c	/^int io_cmppeek(IO *f, size_t o, const void *p, size_t n) {$/;"	f
+io_decr	IO.c	/^void io_decr(IO *io) {$/;"	f
+io_delete	IO.c	/^static void io_delete(IO *io) {$/;"	f	file:
+io_end	IO.c	/^void io_end(void) {$/;"	f
+io_enter	IO.c	/^IO *io_enter(const char *name, FILE *fp, enum io_flags mode) {$/;"	f
+io_eof	IO.c	/^int io_eof(const IO *f) {$/;"	f
+io_err	IO.c	/^int io_err(const IO *f) {$/;"	f
+io_flags	IO.h	/^enum io_flags {$/;"	g
+io_flush	IO.c	/^int io_flush(IO *f) {$/;"	f
+io_fp	IO.c	/^FILE *io_fp(const IO *io) {$/;"	f
+io_getc	IO.c	/^int io_getc(IO *f) {$/;"	f
+io_getline	IO.c	/^size_t io_getline(IO *f, String *s) {$/;"	f
+io_incr	IO.c	/^IO *io_incr(IO *io) {$/;"	f
+io_init	IO.c	/^void io_init(void) {$/;"	f
+io_name	IO.c	/^const char *io_name(const IO *io, String *s) {$/;"	f
+io_open	IO.c	/^IO *io_open(const char *name, enum io_flags mode) {$/;"	f
+io_peek	IO.c	/^int io_peek(IO *f, size_t pos) {$/;"	f
+io_putc	IO.c	/^int io_putc(IO *f, int c) {$/;"	f
+io_read	IO.c	/^size_t io_read(IO *f, String *s, size_t n) {$/;"	f
+io_seek	IO.c	/^int io_seek(IO *f, long off, enum io_whence w) {$/;"	f
+io_tell	IO.c	/^long io_tell(IO *f) {$/;"	f
+io_unbuffer	IO.c	/^void io_unbuffer(IO *io) {$/;"	f
+io_whence	IO.h	/^enum io_whence {$/;"	g
+io_write	IO.c	/^size_t io_write(IO *f, const String *s) {$/;"	f
+io_write_m	IO.c	/^size_t io_write_m(IO *f, const void *p, size_t n) {$/;"	f
+io_write_s	IO.c	/^size_t io_write_s(IO *f, const char *s) {$/;"	f
+io_xcmp	IO.c	/^int io_xcmp(IO *f, size_t a, size_t b, size_t n) {$/;"	f
+iomatch	re.c	/^static size_t iomatch(const t_regex *base, const re_node *node, IO *s, size_t o) {$/;"	f	file:
+iter	hash.h	/^	size_t iter;$/;"	m	struct:__anon29
+iterptr	hash.h	/^	struct h_node *iterptr;$/;"	m	struct:__anon29	typeref:struct:__anon29::h_node
+kdp	kork.h	/^	struct ko_dolphin *kdp;$/;"	m	struct:kork	typeref:struct:kork::ko_dolphin
+key	hash.c	/^	void *key;$/;"	m	struct:h_entry	file:
+ko	val.h	/^	struct kork *ko;$/;"	m	struct:val	typeref:struct:val::kork
+ko_at	kork.c	/^int ko_at(const kork *k, size_t i) {$/;"	f
+ko_cat	kork.c	/^void ko_cat(kork *k, const kork *z) {$/;"	f
+ko_cat_c	kork.c	/^void ko_cat_c(kork *k, char c) {$/;"	f
+ko_cat_m	kork.c	/^void ko_cat_m(kork *k, const void *p, size_t n) {$/;"	f
+ko_chr	kork.c	/^size_t ko_chr(const kork *k, int c) {$/;"	f
+ko_cmp	kork.c	/^int ko_cmp(const kork *k1, const kork *k2) {$/;"	f
+ko_cpy	kork.c	/^void ko_cpy(kork *k, const kork *z) {$/;"	f
+ko_cpy_c	kork.c	/^void ko_cpy_c(kork *k, char c) {$/;"	f
+ko_cpy_m	kork.c	/^void ko_cpy_m(kork *k, const void *p, size_t n) {$/;"	f
+ko_cpy_s	kork.c	/^void ko_cpy_s(kork *k, const char *s) {$/;"	f
+ko_decouple	kork.c	/^void ko_decouple(kork *k) {$/;"	f
+ko_delete	kork.c	/^void ko_delete(kork *k) {$/;"	f
+ko_dolphin	kork.h	/^struct ko_dolphin {$/;"	s
+ko_dup	kork.c	/^kork *ko_dup(const kork *old) {$/;"	f
+ko_getline	kork.c	/^size_t ko_getline(IO *io, kork *k) {$/;"	f
+ko_grep	kork.c	/^void ko_grep(kork *k, int (*pred)(int)) {$/;"	f
+ko_lastchar	kork.c	/^int ko_lastchar(const kork *k) {$/;"	f
+ko_length	kork.c	/^size_t (ko_length)(const kork *k) {$/;"	f
+ko_length	kork.h	60;"	d
+ko_lower	kork.c	/^void ko_lower(kork *k) {$/;"	f
+ko_new	kork.c	/^kork *ko_new(void) {$/;"	f
+ko_num	kork.c	/^void ko_num(kork *k, double d) {$/;"	f
+ko_ptr	kork.c	/^const char *ko_ptr(const kork *k) {$/;"	f
+ko_read	kork.c	/^size_t ko_read(IO *io, kork *k, size_t n) {$/;"	f
+ko_reverse	kork.c	/^void ko_reverse(kork *k) {$/;"	f
+ko_shift	kork.c	/^void ko_shift(kork *k, size_t n) {$/;"	f
+ko_shiftws	kork.c	/^void ko_shiftws(kork *k) {$/;"	f
+ko_str	kork.c	/^const String *ko_str(kork *k) {$/;"	f
+ko_szp	kork.c	/^const char *ko_szp(kork *k) {$/;"	f
+ko_trunc	kork.c	/^void ko_trunc(kork *k, size_t n) {$/;"	f
+ko_upper	kork.c	/^void ko_upper(kork *k) {$/;"	f
+ko_zero	kork.c	/^void (ko_zero)(kork *k) {$/;"	f
+ko_zero	kork.h	61;"	d
+kork	kork.c	/^typedef struct kork kork;$/;"	t	typeref:struct:kork	file:
+kork	kork.h	/^struct kork {$/;"	s
+left	expr.h	/^	} left;$/;"	m	struct:expr	typeref:union:expr::__anon27
+len	re.c	/^		size_t len;$/;"	m	struct:my_node::__anon18	file:
+length	Str.h	/^	size_t length;$/;"	m	struct:__anon25
+length	deparse.c	/^	size_t length;$/;"	m	struct:__anon4	file:
+length	kork.h	/^	size_t offset, length;$/;"	m	struct:kork
+length	list.h	/^	size_t length, size;$/;"	m	struct:li_whale
+length	list.h	/^	size_t offset, length;$/;"	m	struct:list
+length	mars.h	/^	size_t size, length;$/;"	m	struct:mars
+length	run.h	/^		size_t length, size;$/;"	m	struct:Interp::__anon36
+length	strhash.c	/^	size_t length;$/;"	m	struct:strx	file:
+length	strhash.c	/^	size_t length;$/;"	m	struct:strx_const	file:
+length	text.h	/^	size_t length, size;$/;"	m	struct:text
+length	variable.h	/^	size_t size, length;$/;"	m	struct:__anon41
+length	venus.c	/^	size_t size, length;$/;"	m	struct:sorted	file:
+li_append	list.c	/^void li_append(struct list *k, const struct list *l) {$/;"	f
+li_at	list.c	/^struct val *li_at(const struct list *l, size_t i) {$/;"	f
+li_cmp	list.c	/^int li_cmp(const struct list *k, const struct list *l) {$/;"	f
+li_decouple	list.c	/^void li_decouple(struct list *l) {$/;"	f
+li_delete	list.c	/^void li_delete(struct list *l) {$/;"	f
+li_dup	list.c	/^struct list *li_dup(const struct list *k) {$/;"	f
+li_length	list.c	/^size_t (li_length)(const struct list *l) {$/;"	f
+li_length	list.h	39;"	d
+li_new	list.c	/^struct list *li_new(void) {$/;"	f
+li_push	list.c	/^void li_push(struct list *l, struct val *v) {$/;"	f
+li_push_cpy	list.c	/^void li_push_cpy(struct list *l, const struct val *v) {$/;"	f
+li_reverse	list.c	/^void li_reverse(struct list *l) {$/;"	f
+li_shift	list.c	/^void li_shift(struct list *l, size_t n) {$/;"	f
+li_trunc	list.c	/^void li_trunc(struct list *l, size_t n) {$/;"	f
+li_whale	list.h	/^struct li_whale {$/;"	s
+li_zero	list.c	/^void (li_zero)(struct list *l) {$/;"	f
+li_zero	list.h	40;"	d
+line	op.h	/^	size_t line;$/;"	m	struct:op
+line	xmalloc.c	/^		unsigned line;$/;"	m	struct:node	file:
+list	list.h	/^struct list {$/;"	s
+list	val.h	/^		struct list *list;$/;"	m	union:val::__anon40	typeref:struct:val::__anon40::list
+listE	expr.h	/^	listE$/;"	e	enum:t_expr
+literE	expr.h	/^	literE,$/;"	e	enum:t_expr
+liter_expr	transmogrify.c	/^static int liter_expr(const struct expr *const e) {$/;"	f	file:
+literal	re.c	/^	} literal;$/;"	m	union:my_node	typeref:struct:my_node::__anon18	file:
+lwp	list.h	/^	struct li_whale *lwp;$/;"	m	struct:list	typeref:struct:list::li_whale
+m_end	run.h	/^	} m_end;$/;"	m	struct:Interp	typeref:struct:Interp::__anon38
+m_start	run.h	/^	} m_start;$/;"	m	struct:Interp	typeref:struct:Interp::__anon37
+ma_end	mars.c	/^void ma_end(struct mars *m) {$/;"	f
+ma_enter	mars.c	/^int ma_enter(struct mars *m, const String *key, struct op *value) {$/;"	f
+ma_exists	mars.c	/^int ma_exists(const struct mars *m, const String *key) {$/;"	f
+ma_find	mars.c	/^struct op *ma_find(const struct mars *m, String *key) {$/;"	f
+ma_init	mars.c	/^void ma_init(struct mars *m) {$/;"	f
+magic	val.h	/^	} magic;$/;"	m	struct:val	typeref:union:val::__anon40
+main	main.c	/^int main(int argc, char **argv) {$/;"	f
+make_var	deparse.c	/^static void make_var(struct val *v) {$/;"	f	file:
+mars	mars.h	/^struct mars {$/;"	s
+match	re.c	/^static size_t match(const t_regex *base, const re_node *node, const String *s, size_t o) {$/;"	f	file:
+match	run.h	/^	} match;$/;"	m	struct:Interp	typeref:struct:Interp::__anon36
+match_class	re.c	/^static int match_class(unsigned char c, const unsigned char *vec) {$/;"	f	file:
+matches	run.h	/^		struct val *matches;$/;"	m	struct:Interp::__anon36	typeref:struct:Interp::__anon36::val
+max	re.c	/^		size_t min, max;$/;"	m	struct:my_node::__anon19	file:
+maximum	re.c	/^static size_t maximum(const size_t a, const size_t b) {$/;"	f	file:
+mem_dup	re.c	/^static void *mem_dup(const void *p, size_t n, size_t m) {$/;"	f	file:
+min	re.c	/^		size_t min, max;$/;"	m	struct:my_node::__anon19	file:
+minimum	re.c	/^static size_t minimum(const size_t a, const size_t b) {$/;"	f	file:
+minlen	re.c	/^	size_t minlen;$/;"	m	struct:my_regex	file:
+minlen	re.c	/^static size_t minlen(const re_node *node) {$/;"	f	file:
+mode	IO.c	/^	enum io_flags mode;$/;"	m	struct:IO	typeref:enum:IO::io_flags	file:
+my_mars_end	main.c	/^static void my_mars_end(void) {$/;"	f	file:
+my_mars_end_flag	main.c	/^static int my_mars_end_flag;$/;"	v	file:
+my_node	re.c	/^typedef union my_node {$/;"	u	file:
+my_regex	re.c	/^struct my_regex {$/;"	s	file:
+my_text_off	main.c	/^static void my_text_off(void) {$/;"	f	file:
+my_venus_end	main.c	/^static void my_venus_end(void) {$/;"	f	file:
+my_x_end	run.c	/^static void my_x_end(void) {$/;"	f	file:
+n	re.c	/^		size_t n;$/;"	m	struct:my_node::__anon13	file:
+n	re.c	/^		size_t n;$/;"	m	struct:my_node::__anon14	file:
+n	re.c	/^		size_t n;$/;"	m	struct:my_node::__anon19	file:
+name	IO.c	/^	char *name;$/;"	m	struct:IO	file:
+newsize	hash.h	/^	size_t entries, size, newsize;$/;"	m	struct:__anon29
+next	IO.c	/^	struct IO *prev, *next;$/;"	m	struct:IO	typeref:struct:IO::	file:
+next	hash.c	/^	struct h_node *next;$/;"	m	struct:h_node	typeref:struct:h_node::h_node	file:
+next	op.h	/^	struct op *next;$/;"	m	struct:op	typeref:struct:op::op
+next	re_block.c.h	/^	struct block_node *next;$/;"	m	struct:block_node	typeref:struct:block_node::block_node
+next	re_block.c.h	/^	struct txt_node *next;$/;"	m	struct:txt_node	typeref:struct:txt_node::txt_node
+next	xmalloc.c	/^	struct node *prev, *next;$/;"	m	struct:node	typeref:struct:node::	file:
+nextchar	opt.c	/^static const char *nextchar;$/;"	v	file:
+nfill	re.c	/^static void nfill(unsigned char *v, int (*pred)(int)) {$/;"	f	file:
+nil	kork.c	/^static dolphin nil = {$/;"	v	file:
+node	xmalloc.c	/^static struct node {$/;"	s	file:
+nodes	re_block.c.h	/^	re_node nodes[BLOCK_MAGIC];$/;"	m	struct:block_node
+notcomplex	re.c	/^static re_node **notcomplex(re_node **node, const re_node *end) {$/;"	f	file:
+null	mars.c	/^static void null(struct mars **p, size_t n) {$/;"	f	file:
+num	val.h	/^	double num;$/;"	m	struct:val
+offoff	kork.c	/^static void offoff(kork *k) {$/;"	f	file:
+offset	Str.h	/^	size_t offset;$/;"	m	struct:__anon25
+offset	kork.h	/^	size_t offset, length;$/;"	m	struct:kork
+offset	list.h	/^	size_t offset, length;$/;"	m	struct:list
+omgwtf_this_cant_be_happening	zz.c	/^void omgwtf_this_cant_be_happening(const char *file, unsigned long line) {$/;"	f
+op	expr.h	/^		struct op *op;$/;"	m	union:expr::__anon27	typeref:struct:expr::__anon27::op
+op	expr.h	/^	int op;$/;"	m	struct:expr
+op	op.h	/^		struct op *op;$/;"	m	union:op::__anon32	typeref:struct:op::__anon32::op
+op	op.h	/^struct op {$/;"	s
+op_end	op.c	/^void op_end(struct op *op) {$/;"	f
+op_getop	op.c	/^void op_getop(struct op *p) {$/;"	f
+op_init	op.c	/^void op_init(struct op *op) {$/;"	f
+op_resolve	compile.c	/^static void op_resolve(struct op *o) {$/;"	f	file:
+open_inc	parse.c	/^static IO *open_inc(const char *s, enum io_flags mode) {$/;"	f	file:
+ops	deparse.c	/^	struct op **ops;$/;"	m	struct:__anon4	typeref:struct:__anon4::op	file:
+opt_arg	opt.c	/^const char *opt_arg;$/;"	v
+opt_err	opt.c	/^int opt_err;$/;"	v
+opt_get	opt.c	/^int opt_get(int argc, char *const *argv, const char *opts) {$/;"	f
+opt_ind	opt.c	/^int opt_ind;$/;"	v
+p	re.c	/^	size_t *p;$/;"	m	struct:parse_context	file:
+parse	parse.c	/^void parse(IO *f, struct text *text, size_t *line) {$/;"	f
+parse_context	re.c	/^struct parse_context {$/;"	s	file:
+pending	re.c	/^	size_t start, end, pending;$/;"	m	struct:cap_state	file:
+pp_abs	pp.c	/^void pp_abs(struct val *v) {$/;"	f
+pp_abs	pp.h	/^DECL_PP1(pp_abs);$/;"	v
+pp_acos	pp.c	/^void pp_acos(struct val *v) {$/;"	f
+pp_acos	pp.h	/^DECL_PP1(pp_acos);$/;"	v
+pp_add	pp.c	/^void pp_add(struct val *v, struct val *b) {$/;"	f
+pp_add	pp.h	/^DECL_PP2(pp_add);$/;"	v
+pp_and	pp.c	/^void pp_and(struct val *v, struct val *b) {$/;"	f
+pp_and	pp.h	/^DECL_PP2(pp_and);$/;"	v
+pp_asin	pp.c	/^void pp_asin(struct val *v) {$/;"	f
+pp_asin	pp.h	/^DECL_PP1(pp_asin);$/;"	v
+pp_atan	pp.c	/^void pp_atan(struct val *v) {$/;"	f
+pp_atan	pp.h	/^DECL_PP1(pp_atan);$/;"	v
+pp_atan2	pp.c	/^void pp_atan2(struct val *v) {$/;"	f
+pp_atan2	pp.h	/^DECL_PP1(pp_atan2);$/;"	v
+pp_chr	pp.c	/^void pp_chr(struct val *v) {$/;"	f
+pp_chr	pp.h	/^DECL_PP1(pp_chr);$/;"	v
+pp_comma	pp.c	/^void pp_comma(struct val *v, struct val *b) {$/;"	f
+pp_comma	pp.h	/^DECL_PP2(pp_comma);$/;"	v
+pp_concat	pp.c	/^void pp_concat(struct val *v, struct val *b) {$/;"	f
+pp_concat	pp.h	/^DECL_PP2(pp_concat);$/;"	v
+pp_cos	pp.c	/^void pp_cos(struct val *v) {$/;"	f
+pp_cos	pp.h	/^DECL_PP1(pp_cos);$/;"	v
+pp_defined	pp.c	/^void pp_defined(struct val *v) {$/;"	f
+pp_defined	pp.h	/^DECL_PP1(pp_defined);$/;"	v
+pp_div	pp.c	/^void pp_div(struct val *v, struct val *b) {$/;"	f
+pp_div	pp.h	/^DECL_PP2(pp_div);$/;"	v
+pp_eof	pp.c	/^void pp_eof(struct val *v) {$/;"	f
+pp_eof	pp.h	/^DECL_PP1(pp_eof);$/;"	v
+pp_eq	pp.c	/^void pp_eq(struct val *v, struct val *b) {$/;"	f
+pp_eq	pp.h	/^DECL_PP2(pp_eq);$/;"	v
+pp_eq_n	pp.c	/^void pp_eq_n(struct val *v, struct val *b) {$/;"	f
+pp_eq_n	pp.h	/^DECL_PP2(pp_eq_n);$/;"	v
+pp_error	pp.c	/^void pp_error(struct val *v) {$/;"	f
+pp_error	pp.h	/^DECL_PP1(pp_error);$/;"	v
+pp_escape	pp.c	/^void pp_escape(struct val *v) {$/;"	f
+pp_escape	pp.h	/^DECL_PP1(pp_escape);$/;"	v
+pp_frombase	pp.c	/^void pp_frombase(struct val *v, struct val *b) {$/;"	f
+pp_frombase	pp.h	/^DECL_PP2(pp_frombase);$/;"	v
+pp_getc	pp.c	/^void pp_getc(struct val *v) {$/;"	f
+pp_getc	pp.h	/^DECL_PP1(pp_getc);$/;"	v
+pp_getenv	pp.c	/^void pp_getenv(struct val *v) {$/;"	f
+pp_getenv	pp.h	/^DECL_PP1(pp_getenv);$/;"	v
+pp_gets	pp.c	/^void pp_gets(struct val *v) {$/;"	f
+pp_gets	pp.h	/^DECL_PP1(pp_gets);$/;"	v
+pp_gt	pp.c	/^void pp_gt(struct val *v, struct val *b) {$/;"	f
+pp_gt	pp.h	/^DECL_PP2(pp_gt);$/;"	v
+pp_gt_n	pp.c	/^void pp_gt_n(struct val *v, struct val *b) {$/;"	f
+pp_gt_n	pp.h	/^DECL_PP2(pp_gt_n);$/;"	v
+pp_int	pp.c	/^void pp_int(struct val *v) {$/;"	f
+pp_int	pp.h	/^DECL_PP1(pp_int);$/;"	v
+pp_io	pp.c	/^void pp_io(struct val *v) {$/;"	f
+pp_io	pp.h	/^DECL_PP1(pp_io);$/;"	v
+pp_length	pp.c	/^void pp_length(struct val *v) {$/;"	f
+pp_length	pp.h	/^DECL_PP1(pp_length);$/;"	v
+pp_log	pp.c	/^void pp_log(struct val *v) {$/;"	f
+pp_log	pp.h	/^DECL_PP1(pp_log);$/;"	v
+pp_log10	pp.c	/^void pp_log10(struct val *v) {$/;"	f
+pp_log10	pp.h	/^DECL_PP1(pp_log10);$/;"	v
+pp_lower	pp.c	/^void pp_lower(struct val *v) {$/;"	f
+pp_lower	pp.h	/^DECL_PP1(pp_lower);$/;"	v
+pp_lt	pp.c	/^void pp_lt(struct val *v, struct val *b) {$/;"	f
+pp_lt	pp.h	/^DECL_PP2(pp_lt);$/;"	v
+pp_lt_n	pp.c	/^void pp_lt_n(struct val *v, struct val *b) {$/;"	f
+pp_lt_n	pp.h	/^DECL_PP2(pp_lt_n);$/;"	v
+pp_match	pp.c	/^void pp_match(struct val *v, struct val *b) {$/;"	f
+pp_match	pp.h	/^DECL_PP2(pp_match);$/;"	v
+pp_mod	pp.c	/^void pp_mod(struct val *v, struct val *b) {$/;"	f
+pp_mod	pp.h	/^DECL_PP2(pp_mod);$/;"	v
+pp_moend	pp.c	/^void pp_moend(struct val *v) {$/;"	f
+pp_moend	pp.h	/^DECL_PP1(pp_moend);$/;"	v
+pp_mostart	pp.c	/^void pp_mostart(struct val *v) {$/;"	f
+pp_mostart	pp.h	/^DECL_PP1(pp_mostart);$/;"	v
+pp_mult	pp.c	/^void pp_mult(struct val *v, struct val *b) {$/;"	f
+pp_mult	pp.h	/^DECL_PP2(pp_mult);$/;"	v
+pp_ne	pp.c	/^void pp_ne(struct val *v, struct val *b) {$/;"	f
+pp_ne	pp.h	/^DECL_PP2(pp_ne);$/;"	v
+pp_ne_n	pp.c	/^void pp_ne_n(struct val *v, struct val *b) {$/;"	f
+pp_ne_n	pp.h	/^DECL_PP2(pp_ne_n);$/;"	v
+pp_neg	pp.c	/^void pp_neg(struct val *v) {$/;"	f
+pp_neg	pp.h	/^DECL_PP1(pp_neg);$/;"	v
+pp_not	pp.c	/^void pp_not(struct val *v) {$/;"	f
+pp_not	pp.h	/^DECL_PP1(pp_not);$/;"	v
+pp_num	pp.c	/^void pp_num(struct val *v) {$/;"	f
+pp_num	pp.h	/^DECL_PP1(pp_num);$/;"	v
+pp_open	pp.c	/^void pp_open(struct val *v) {$/;"	f
+pp_open	pp.h	/^DECL_PP1(pp_open);$/;"	v
+pp_openr	pp.c	/^void pp_openr(struct val *v) {$/;"	f
+pp_openr	pp.h	/^DECL_PP1(pp_openr);$/;"	v
+pp_openw	pp.c	/^void pp_openw(struct val *v) {$/;"	f
+pp_openw	pp.h	/^DECL_PP1(pp_openw);$/;"	v
+pp_or	pp.c	/^void pp_or(struct val *v, struct val *b) {$/;"	f
+pp_or	pp.h	/^DECL_PP2(pp_or);$/;"	v
+pp_ord	pp.c	/^void pp_ord(struct val *v) {$/;"	f
+pp_ord	pp.h	/^DECL_PP1(pp_ord);$/;"	v
+pp_pop	pp.c	/^void pp_pop(struct val *v, struct val *b) {$/;"	f
+pp_pop	pp.h	/^DECL_PP2(pp_pop);$/;"	v
+pp_pow	pp.c	/^void pp_pow(struct val *v, struct val *b) {$/;"	f
+pp_pow	pp.h	/^DECL_PP2(pp_pow);$/;"	v
+pp_quote	pp.c	/^void pp_quote(struct val *v) {$/;"	f
+pp_quote	pp.h	/^DECL_PP1(pp_quote);$/;"	v
+pp_read	pp.c	/^void pp_read(struct val *v, struct val *b) {$/;"	f
+pp_read	pp.h	/^DECL_PP2(pp_read);$/;"	v
+pp_remove	pp.c	/^void pp_remove(struct val *v) {$/;"	f
+pp_remove	pp.h	/^DECL_PP1(pp_remove);$/;"	v
+pp_rename	pp.c	/^void pp_rename(struct val *v) {$/;"	f
+pp_rename	pp.h	/^DECL_PP1(pp_rename);$/;"	v
+pp_reverse	pp.c	/^void pp_reverse(struct val *v) {$/;"	f
+pp_reverse	pp.h	/^DECL_PP1(pp_reverse);$/;"	v
+pp_seek	pp.c	/^void pp_seek(struct val *v) {$/;"	f
+pp_seek	pp.h	/^DECL_PP1(pp_seek);$/;"	v
+pp_shift	pp.c	/^void pp_shift(struct val *v, struct val *b) {$/;"	f
+pp_shift	pp.h	/^DECL_PP2(pp_shift);$/;"	v
+pp_sin	pp.c	/^void pp_sin(struct val *v) {$/;"	f
+pp_sin	pp.h	/^DECL_PP1(pp_sin);$/;"	v
+pp_sqrt	pp.c	/^void pp_sqrt(struct val *v) {$/;"	f
+pp_sqrt	pp.h	/^DECL_PP1(pp_sqrt);$/;"	v
+pp_str	pp.c	/^void pp_str(struct val *v) {$/;"	f
+pp_str	pp.h	/^DECL_PP1(pp_str);$/;"	v
+pp_sub	pp.c	/^void pp_sub(struct val *v, struct val *b) {$/;"	f
+pp_sub	pp.h	/^DECL_PP2(pp_sub);$/;"	v
+pp_tan	pp.c	/^void pp_tan(struct val *v) {$/;"	f
+pp_tan	pp.h	/^DECL_PP1(pp_tan);$/;"	v
+pp_tell	pp.c	/^void pp_tell(struct val *v) {$/;"	f
+pp_tell	pp.h	/^DECL_PP1(pp_tell);$/;"	v
+pp_tobase	pp.c	/^void pp_tobase(struct val *v, struct val *b) {$/;"	f
+pp_tobase	pp.h	/^DECL_PP2(pp_tobase);$/;"	v
+pp_typeof	pp.c	/^void pp_typeof(struct val *v) {$/;"	f
+pp_typeof	pp.h	/^DECL_PP1(pp_typeof);$/;"	v
+pp_upper	pp.c	/^void pp_upper(struct val *v) {$/;"	f
+pp_upper	pp.h	/^DECL_PP1(pp_upper);$/;"	v
+prev	IO.c	/^	struct IO *prev, *next;$/;"	m	struct:IO	typeref:struct:IO::IO	file:
+prev	xmalloc.c	/^	struct node *prev, *next;$/;"	m	struct:node	typeref:struct:node::node	file:
+printinc	main.c	/^static void printinc(void) {$/;"	f	file:
+ptr	strhash.c	/^	char *ptr;$/;"	m	struct:strx	file:
+ptr	strhash.c	/^	const char *ptr;$/;"	m	struct:strx_const	file:
+ptr	xmalloc.c	/^	void *ptr;$/;"	m	struct:node	file:
+quantifire	re.c	/^PRAZ(quantifire);$/;"	v
+randseed	random.c	/^void randseed(void) {$/;"	f
+randval	random.c	/^double randval(void) {$/;"	f
+rcache	re.c	/^static Hash rcache;$/;"	v	file:
+re_backref	re.c	/^int re_backref(const t_regex *re, size_t i, size_t *a, size_t *z) {$/;"	f
+re_cabra	re.c	/^size_t re_cabra(const t_regex *re) {$/;"	f
+re_compile	re.c	/^t_regex *re_compile(const String *s) {$/;"	f
+re_decompile	re.c	/^void re_decompile(const t_regex *re, String *s) {$/;"	f
+re_dump	re.c	/^void re_dump(const t_regex *re, FILE *fp) {$/;"	f
+re_dup	re.c	/^t_regex *re_dup(t_regex *re) {$/;"	f
+re_end	re.c	/^void re_end(void) {$/;"	f
+re_flags	re.c	/^enum re_flags {$/;"	g	file:
+re_free	re.c	/^void re_free(t_regex *re) {$/;"	f
+re_init	re.c	/^void re_init(void) {$/;"	f
+re_iomatch	re.c	/^int re_iomatch(t_regex *re, IO *io, size_t *ms, size_t *me) {$/;"	f
+re_match	re.c	/^int re_match(t_regex *re, const String *s, size_t *ms, size_t *me) {$/;"	f
+re_node	re.c	/^} re_node;$/;"	t	typeref:union:my_node	file:
+refs	IO.c	/^	size_t refs;$/;"	m	struct:IO	file:
+refs	kork.h	/^	size_t refs;$/;"	m	struct:ko_dolphin
+refs	list.h	/^	size_t refs;$/;"	m	struct:li_whale
+refs	re.c	/^	size_t refs;$/;"	m	struct:my_regex	file:
+refs	sub.c	/^	size_t refs;$/;"	m	struct:sub	file:
+reinit	re.c	/^static void reinit(t_regex *re) {$/;"	f	file:
+rep	re.c	/^	} rep;$/;"	m	union:my_node	typeref:struct:my_node::__anon19	file:
+repbuf	re.c	/^	size_t *repbuf;$/;"	m	struct:my_regex	file:
+repets	re.c	/^	size_t repets;$/;"	m	struct:my_regex	file:
+resolve	compile.c	/^static void resolve(struct expr *e) {$/;"	f	file:
+result	run.h	/^	struct val result;$/;"	m	struct:Interp	typeref:struct:Interp::val
+right	expr.h	/^	struct expr *right;$/;"	m	struct:expr	typeref:struct:expr::expr
+root	re_block.c.h	/^	struct block_node *root;$/;"	m	struct:__anon34	typeref:struct:__anon34::block_node
+run	run.c	/^void run(const struct text *t, size_t argc, char **argv) {$/;"	f
+rx	expr.h	/^		t_regex *rx;$/;"	m	union:expr::__anon27
+s	re.c	/^	const String *s;$/;"	m	struct:parse_context	file:
+s_lastof	main.c	/^static char *s_lastof(const char *s, const char *set) {$/;"	f	file:
+sanitycheck	IO.c	/^static void sanitycheck(enum io_flags m) {$/;"	f	file:
+save_pair	run.h	/^} save_pair;$/;"	t	typeref:struct:__anon39
+seed	hash.h	/^	size_t seed;$/;"	m	struct:__anon29
+seen	deparse.c	/^} seen;$/;"	v	typeref:struct:__anon4	file:
+select	re.c	/^	} select;$/;"	m	union:my_node	typeref:struct:my_node::__anon20	file:
+sh_delete	strhash.c	/^void sh_delete(t_strhash *sh) {$/;"	f
+sh_get	strhash.c	/^void *sh_get(t_strhash *sh, const char *key, size_t keylen) {$/;"	f
+sh_new	strhash.c	/^t_strhash *sh_new(void (*delv)(void *)) {$/;"	f
+sh_put	strhash.c	/^void sh_put(t_strhash *sh, const char *key, size_t keylen, void *val) {$/;"	f
+simplerep	re.c	/^static void simplerep(re_node *node, const re_node *const end) {$/;"	f	file:
+siword	re.c	/^static int siword(int c) {$/;"	f	file:
+size	Str.h	/^	size_t size;$/;"	m	struct:__anon25
+size	deparse.c	/^	size_t size;$/;"	m	struct:__anon4	file:
+size	hash.h	/^	size_t entries, size, newsize;$/;"	m	struct:__anon29
+size	list.h	/^	size_t length, size;$/;"	m	struct:li_whale
+size	mars.h	/^	size_t size, length;$/;"	m	struct:mars
+size	run.h	/^		size_t length, size;$/;"	m	struct:Interp::__anon36
+size	run.h	/^		size_t size;$/;"	m	struct:Interp::__anon37
+size	run.h	/^		size_t size;$/;"	m	struct:Interp::__anon38
+size	text.h	/^	size_t length, size;$/;"	m	struct:text
+size	variable.h	/^	size_t size, length;$/;"	m	struct:__anon41
+size	venus.c	/^	size_t size, length;$/;"	m	struct:sorted	file:
+size	xmalloc.c	/^	size_t size;$/;"	m	struct:node	file:
+skipline	parse.c	/^static void skipline(IO *f) {$/;"	f	file:
+skipspace	re.c	/^static void skipspace(const String *const s, size_t *const p) {$/;"	f	file:
+solid	sub.c	/^static void solid(struct expr *e) {$/;"	f	file:
+sorted	venus.c	/^struct sorted {$/;"	s	file:
+sp_error	run.c	/^static void sp_error(const save_pair *x, const save_pair *y) {$/;"	f	file:
+sp_nop	run.c	/^static void sp_nop(save_pair *sp) {$/;"	f	file:
+sp_writeback	run.c	/^static void sp_writeback(save_pair *sp) {$/;"	f	file:
+stack	stack.h	9;"	d
+stack_declare	stack.h	12;"	d
+stack_define	stack.h	33;"	d
+stack_func	stack.h	10;"	d
+stack_store	run.c	/^void stack_store(struct val *target, const struct val *value) {$/;"	f
+stack_store_del	run.c	/^static void stack_store_del(struct val *target, struct val *value) {$/;"	f	file:
+start	re.c	/^	re_node *start;$/;"	m	struct:my_regex	file:
+start	re.c	/^	size_t start, end, pending;$/;"	m	struct:cap_state	file:
+start	text.h	/^	struct op **start;$/;"	m	struct:text	typeref:struct:text::op
+str	kork.h	/^	String str;$/;"	m	struct:ko_dolphin
+strhash	strhash.c	/^struct strhash {$/;"	s	file:
+strx	strhash.c	/^struct strx {$/;"	s	file:
+strx_const	strhash.c	/^struct strx_const {$/;"	s	file:
+sub	sub.c	/^struct sub {$/;"	s	file:
+sub	val.h	/^		struct sub *sub;$/;"	m	union:val::__anon40	typeref:struct:val::__anon40::sub
+sub_decr	sub.c	/^void sub_decr(struct sub *p) {$/;"	f
+sub_expr	sub.c	/^struct expr *sub_expr(const struct sub *p) {$/;"	f
+sub_id	sub.c	/^size_t sub_id(const struct sub *p) {$/;"	f
+sub_incr	sub.c	/^struct sub *sub_incr(struct sub *p) {$/;"	f
+sub_new	sub.c	/^struct sub *sub_new(const struct expr *e) {$/;"	f
+subminlen	re.c	/^static size_t subminlen(const re_node *node, const re_node *end, size_t start) {$/;"	f	file:
+sv_copy	expr.c	/^static void sv_copy(svalp *dst, const svalp *src) {$/;"	f	file:
+sv_end	expr.c	/^static void sv_end(svalp *x) {$/;"	f	file:
+sv_init	expr.c	/^static void sv_init(svalp *x) {$/;"	f	file:
+svalp	expr.c	/^typedef struct val *svalp;$/;"	t	typeref:struct:val	file:
+symbolE	expr.h	/^	symbolE,$/;"	e	enum:t_expr
+t_binop	expr.h	/^enum t_binop {$/;"	g
+t_block	re_block.c.h	/^} t_block;$/;"	t	typeref:struct:__anon34
+t_context	expr.h	/^} t_context;$/;"	t	typeref:struct:__anon28
+t_expr	expr.h	/^enum t_expr {$/;"	g
+t_func	expr.h	/^enum t_func {$/;"	g
+t_op	op.h	/^enum t_op {$/;"	g
+t_re_node	re.c	/^enum t_re_node {$/;"	g	file:
+t_regex	re.h	/^typedef struct my_regex t_regex;$/;"	t	typeref:struct:my_regex
+t_strhash	strhash.h	/^typedef struct strhash t_strhash;$/;"	t	typeref:struct:strhash
+t_symbol	expr.h	/^enum t_symbol {$/;"	g
+t_vr_container	variable.h	/^} t_vr_container;$/;"	t	typeref:struct:__anon41
+t_vr_cookie	variable.h	/^typedef size_t t_vr_cookie;$/;"	t
+table	hash.h	/^	struct h_node **table;$/;"	m	struct:__anon29	typeref:struct:__anon29::h_node
+table	mars.h	/^	struct mars **table;$/;"	m	struct:mars	typeref:struct:mars::mars
+table	venus.h	/^	Hash table;$/;"	m	struct:venus
+target	run.h	/^	struct val *target;$/;"	m	struct:__anon39	typeref:struct:__anon39::val
+tent	expr.h	/^		t_vr_cookie tent;$/;"	m	union:expr::__anon26
+text	main.c	/^static struct text text;$/;"	v	typeref:struct:text	file:
+text	text.h	/^struct text {$/;"	s
+text_1	text.c	/^void text_1(struct text *p) {$/;"	f
+text_off	text.c	/^void text_off(struct text *p) {$/;"	f
+text_on	text.c	/^void text_on(struct text *p) {$/;"	f
+text_push	text.c	/^struct op *text_push(struct text *p, const struct op *src) {$/;"	f
+to_id	deparse.c	/^static void to_id(struct kork *k, size_t n) {$/;"	f	file:
+told	IO.c	/^	long told;$/;"	m	struct:IO	file:
+trans_fold	transmogrify.c	/^void trans_fold(struct expr **e) {$/;"	f
+transmogrify	transmogrify.c	/^void transmogrify(struct text *code) {$/;"	f
+trysimpl	re.c	/^static void trysimpl(re_node *node) {$/;"	f	file:
+txt	op.h	/^	String txt;$/;"	m	struct:op
+txt_node	re_block.c.h	/^struct txt_node {$/;"	s
+txt_root	re_block.c.h	/^	struct txt_node *txt_root;$/;"	m	struct:__anon34	typeref:struct:__anon34::txt_node
+type	expr.h	/^	enum t_expr type;$/;"	m	struct:expr	typeref:enum:expr::t_expr
+type	op.h	/^	enum t_op type;$/;"	m	struct:op	typeref:enum:op::t_op
+type	val.h	/^	enum val_cont type;$/;"	m	struct:val	typeref:enum:val::val_cont
+u_cmp	strutil.c	/^int u_cmp(const char *as, size_t al, const char *bs, size_t bl) {$/;"	f
+u_hash	strutil.c	/^size_t u_hash(const char *s, size_t l, size_t h) {$/;"	f
+undef	expr.c	/^static struct expr *undef(void) {$/;"	f	file:
+unopE	expr.h	/^	unopE,$/;"	e	enum:t_expr
+unoptimize	main_opt.h	/^	int unoptimize;$/;"	m	struct:Options
+usage	main.c	/^static void usage(void) {$/;"	f	file:
+used	re_block.c.h	/^	size_t used;$/;"	m	struct:block_node
+v	expr.h	/^	} v;$/;"	m	struct:expr	typeref:union:expr::__anon26
+v_cat	val.c	/^void v_cat(struct val *dst, struct val *src) {$/;"	f
+v_cat_c	val.c	/^void v_cat_c(struct val *v, char c) {$/;"	f
+v_cat_m	val.c	/^void v_cat_m(struct val *v, const void *p, size_t n) {$/;"	f
+v_cat_s	val.c	/^void v_cat_s(struct val *v, const String *s) {$/;"	f
+v_cmp_ls	val.c	/^int v_cmp_ls(struct val *v, struct val *b) {$/;"	f
+v_delete	val.c	/^void v_delete(struct val *v) {$/;"	f
+v_end	val.c	/^void v_end(struct val *v) {$/;"	f
+v_iniset	val.c	/^void v_iniset(struct val *dst, const struct val *src) {$/;"	f
+v_init	val.c	/^void v_init(struct val *v) {$/;"	f
+v_kork	val.c	/^struct kork *v_kork(struct val *v) {$/;"	f
+v_ok_num	val.c	/^void v_ok_num(struct val *v) {$/;"	f
+v_ok_str	val.c	/^void v_ok_str(struct val *v) {$/;"	f
+v_set	val.c	/^void v_set(struct val *dst, const struct val *src) {$/;"	f
+v_set_io	val.c	/^void v_set_io(struct val *v, IO *io) {$/;"	f
+v_set_m	val.c	/^void v_set_m(struct val *v, const void *p, size_t n) {$/;"	f
+v_set_n	val.c	/^void v_set_n(struct val *v, double d) {$/;"	f
+v_set_s	val.c	/^void v_set_s(struct val *v, const String *s) {$/;"	f
+v_set_sub	val.c	/^void v_set_sub(struct val *v, struct sub *s) {$/;"	f
+v_set_undef	val.c	/^void v_set_undef(struct val *v) {$/;"	f
+v_sptr	val.c	/^const char *v_sptr(struct val *v, size_t *l) {$/;"	f
+v_true	val.c	/^int v_true(const struct val *v) {$/;"	f
+v_undef	val.c	/^struct val *v_undef(void) {$/;"	f
+val	expr.h	/^		struct val *val;$/;"	m	union:expr::__anon26	typeref:struct:expr::__anon26::val
+val	val.h	/^struct val {$/;"	s
+val_cont	val.h	/^enum val_cont {$/;"	g
+value	hash.c	/^	void *value;$/;"	m	struct:h_entry	file:
+varE	expr.h	/^	varE,$/;"	e	enum:t_expr
+var_end	main.c	/^static void var_end(void) {$/;"	f	file:
+varhashE	expr.h	/^	varhashE,$/;"	e	enum:t_expr
+ve_end	venus.c	/^void ve_end(struct venus *v) {$/;"	f
+ve_enter	venus.c	/^void ve_enter(struct venus *v, const String *s, struct op *o) {$/;"	f
+ve_findnext	venus.c	/^struct op *ve_findnext(struct venus *v, const String *s, size_t line) {$/;"	f
+ve_findprev	venus.c	/^struct op *ve_findprev(struct venus *v, const String *s, size_t line) {$/;"	f
+ve_init	venus.c	/^void ve_init(struct venus *v) {$/;"	f
+ve_label	venus.c	/^const String *ve_label(struct venus *v, const struct op *op) {$/;"	f
+vec	re.c	/^		unsigned char *vec;$/;"	m	struct:my_node::__anon16	file:
+vec	re.c	/^		unsigned char vec[CLASS_SIZE];$/;"	m	struct:my_node::__anon15	file:
+venus	venus.h	/^struct venus {$/;"	s
+vpswap	list.c	/^static void vpswap(struct val **p, struct val **q) {$/;"	f	file:
+vr_data	variable.c	/^void *(vr_data)(const t_vr_container *v, t_vr_cookie c) {$/;"	f
+vr_data	variable.h	31;"	d
+vr_delete	variable.c	/^void vr_delete(t_vr_container *v) {$/;"	f
+vr_exists	variable.c	/^t_vr_cookie vr_exists(const t_vr_container *v, const char *key, size_t len) {$/;"	f
+vr_freeze	variable.c	/^void vr_freeze(t_vr_container *v) {$/;"	f
+vr_new	variable.c	/^t_vr_container *vr_new(void (*del)(void *)) {$/;"	f
+vr_register	variable.c	/^t_vr_cookie vr_register(t_vr_container *v, const char *key, size_t len, void *val) {$/;"	f
+walk	deparse.c	/^static int walk(const struct expr *e) {$/;"	f	file:
+walk	transmogrify.c	/^static void walk(struct expr *e) {$/;"	f	file:
+whale	list.c	/^typedef struct li_whale whale;$/;"	t	typeref:struct:li_whale	file:
+x	re.c	/^	} x;$/;"	m	union:my_node	typeref:struct:my_node::__anon11	file:
+xclip	hash.c	/^static size_t xclip(const Hash *h, const size_t hash_unc) {$/;"	f	file:
+xdigits	pp.c	/^static const char xdigits[] = "0123456789" "abcdefghijklmnopqrstuvwxyz";$/;"	v	file:
+xend	xmalloc.c	/^void xend(void) {$/;"	f
+xfree	xmalloc.c	/^void xfree(void *ptr) {$/;"	f
+xincentries	hash.c	/^static void xincentries(Hash *h) {$/;"	f	file:
+xmalloc	xmalloc.c	/^void *(xmalloc)(size_t nmemb, size_t size, const char *file, unsigned line) {$/;"	f
+xmalloc	xmalloc.h	12;"	d
+xrealloc	xmalloc.c	/^void *(xrealloc)(void *optr, size_t nmemb, const char *file, unsigned line) {$/;"	f
+xrealloc	xmalloc.h	13;"	d
+xsh_delete	main.c	/^static void xsh_delete(void *sh) {$/;"	f	file:
+xstep	hash.c	/^static void xstep(Hash *h) {$/;"	f	file:
+xstrdup	IO.c	/^static char *xstrdup(const char *s) {$/;"	f	file:
+xv_delete	expr.c	/^static void xv_delete(void *v) {$/;"	f	file:
+xv_delete	main.c	/^static void xv_delete(void *v) {$/;"	f	file:
+xval	expr.c	/^static int xval(int c) {$/;"	f	file:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/text.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,34 @@
+#include "text.h"
+#include "xmalloc.h"
+
+#include <stddef.h>
+
+enum {MAGIC = 42};
+
+void text_on(struct text *p) {
+	p->start = xmalloc(p->size = MAGIC, sizeof *p->start);
+	p->start[p->length = 0] = NULL;
+}
+
+void text_off(struct text *p) {
+	while (p->length) {
+		--p->length;
+		op_end(p->start[p->length]);
+		xfree(p->start[p->length]);
+	}
+	xfree(p->start);
+}
+
+void text_1(struct text *p) {
+	while (p->length >= p->size - 1) {
+		p->start = xrealloc(p->start, p->size *= 2);
+	}
+}
+
+struct op *text_push(struct text *p, const struct op *src) {
+	text_1(p);
+	p->start[p->length] = xmalloc(1, sizeof *p->start[p->length]);
+	*p->start[p->length++] = *src;
+	p->start[p->length] = NULL;
+	return p->start[p->length - 1];
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/text.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,2 @@
+text.o: text.c text.h op.h IO.h config.h Str.h expr.h re.h stack.h \
+  xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/text.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,18 @@
+#ifndef TEXT_H_
+#define TEXT_H_
+
+#include "op.h"
+
+#include <stddef.h>
+
+struct text {
+	size_t length, size;
+	struct op **start;
+};
+
+void text_on(struct text *);
+void text_off(struct text *);
+void text_1(struct text *);
+struct op *text_push(struct text *, const struct op *);
+
+#endif /* TEXT_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/transmogrify.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,598 @@
+#include "config.h"
+#include "Str.h"
+#include "expr.h"
+#include "kork.h"
+#include "main_label.h"
+#include "op.h"
+#include "text.h"
+#include "transmogrify.h"
+#include "val.h"
+#include "venus.h"
+#include "xmalloc.h"
+#include "zz.h"
+
+#include <ctype.h>
+#include <math.h>
+#include <stddef.h>
+#include <assert.h>
+
+ATTR_PURE
+static int liter_expr(const struct expr *const e) {
+	return
+		e->type == literE ||
+		(
+		 e->type == symbolE &&
+		 e->op != S_ARG && e->op != S_ARGC  && e->op != S_ARGV &&
+		 e->op != S_ERR && e->op != S_MATCH && e->op != S_RAND &&
+		 e->op != S_RESULT
+		) ||
+		(
+		 e->type == listE &&
+		 (e->right == NULL || liter_expr(e->right)) &&
+		 e->left.expr == NULL
+		)
+		;
+}
+
+#define const_unop(op) \
+( \
+ (op) == F_ABS   || (op) == F_ACOS   || (op) == F_ASIN    || \
+ (op) == F_ATAN  || (op) == F_ATAN2  || (op) == F_CHR     || \
+ (op) == F_CHR   || (op) == F_COS    || (op) == F_DEFINED || \
+ (op) == F_EXP   || (op) == F_GETENV || (op) == F_INT     || \
+ (op) == F_IO    || (op) == F_LENGTH || (op) == F_LOG     || \
+ (op) == F_LOG10 || (op) == F_LOWER  || (op) == F_NEG     || \
+ (op) == F_NOT   || (op) == F_NUM    || (op) == F_ORD     || \
+ (op) == F_QUOTE || (op) == F_RE_ESC || (op) == F_REVERSE || \
+ (op) == F_SIN   || (op) == F_TAN    || (op) == F_SQRT    || \
+ (op) == F_STR   || (op) == F_TYPEOF || (op) == F_UPPER      \
+)
+
+#define const_binop(op)                                      \
+(                                                            \
+  !((op) == B_SPOT || (op) == B_SQIGGLE || (op) == B_XMATCH) \
+)
+
+static int hp_expr(const struct expr *e) {
+	switch (e->type) {
+		case literE:
+			return 1;
+
+		case varE:
+			return 1;
+
+		case varhashE:
+			return hp_expr(e->right);
+
+		case symbolE:
+			if (e->op != S_RAND) {
+				return 1;
+			}
+			return 0;
+
+		case unopE:
+			if (!const_unop(e->op)) {
+				return 0;
+			}
+			return hp_expr(e->right);
+
+		case binopE:
+			if (!const_binop(e->op)) {
+				return 0;
+			}
+			if (!hp_expr(e->left.expr)) {
+				return 0;
+			}
+			return hp_expr(e->right);
+
+		case listE:
+			if (e->right) {
+				if (!hp_expr(e->right)) {
+					return 0;
+				}
+				if (!e->left.expr) {
+					return 1;
+				}
+				return hp_expr(e->left.expr);
+			}
+			return 1;
+	}
+
+	NOTREACHED;
+}
+
+void trans_fold(struct expr **e) {
+	struct val *v;
+
+	switch ((*e)->type) {
+		case literE:
+			break;
+
+		case varE:
+			break;
+
+		case varhashE:
+			trans_fold(&(*e)->right);
+			break;
+
+		case symbolE:
+			break;
+
+		case unopE:
+			trans_fold(&(*e)->right);
+			if (liter_expr((*e)->right)) {
+				if ((*e)->op == F_NUL) {
+					v = eval_expr((*e)->right);
+					free_expr((*e)->right);
+					(*e)->right = xmalloc(1, sizeof *(*e)->right);
+					(*e)->right->type = literE;
+					(*e)->right->left.expr = (*e)->right->right = NULL;
+					(*e)->right->v.val = v_undef();
+					TOLABEL(v);
+					if (((*e)->left.op = ve_findnext(&Venus, ko_str(v->ko), (*e)->left.bonus))) {
+						(*e)->op = F_CALL;
+					} else {
+						(*e)->op = F_HANG;
+					}
+					v_delete(v);
+				} else if (const_unop((*e)->op)) {
+					(*e)->v.val = eval_expr(*e);
+					free_expr((*e)->right);
+					(*e)->right = NULL;
+					(*e)->type = literE;
+				}
+			} else if (
+				(*e)->right->type == unopE &&
+				(*e)->op == (*e)->right->op &&
+				((*e)->op == F_STR || (*e)->op == F_NUM)
+			) {
+				struct expr *const tmp = (*e)->right;
+				(*e)->right = (*e)->right->right;
+				tmp->right = NULL;
+				free_expr(tmp);
+			}
+			break;
+
+		case binopE:
+			trans_fold(&(*e)->left.expr);
+			trans_fold(&(*e)->right);
+			if (liter_expr((*e)->left.expr) && liter_expr((*e)->right) && const_binop((*e)->op)) {
+				(*e)->v.val = eval_expr(*e);
+				free_expr((*e)->left.expr);
+				(*e)->left.expr = NULL;
+				free_expr((*e)->right);
+				(*e)->right = NULL;
+				(*e)->type = literE;
+			} else switch ((*e)->op) {
+				case B_INTERSECTION:
+					if ((*e)->left.expr->type == literE) {
+						V_NUM((*e)->left.expr->v.val);
+						if ((*e)->left.expr->v.val->num == 0.0) {
+							free_expr((*e)->left.expr);
+							(*e)->left.expr = NULL;
+							(*e)->op = F_NUM;
+							(*e)->type = unopE;
+						}
+					} else if ((*e)->right->type == literE) {
+						V_NUM((*e)->right->v.val);
+						if ((*e)->right->v.val->num == 0.0) {
+							free_expr((*e)->right);
+							(*e)->right = (*e)->left.expr;
+							(*e)->left.expr = NULL;
+							(*e)->op = F_NUM;
+							(*e)->type = unopE;
+						}
+					}
+					break;
+
+				case B_TAIL:
+					if (hp_expr((*e)->left.expr)) {
+						struct expr *tmp;
+
+						free_expr((*e)->left.expr);
+						tmp = *e;
+						*e = (*e)->right;
+						xfree(tmp);
+					}
+					break;
+
+				case B_WORM:
+					if ((*e)->left.expr->type == literE) {
+						V_NUM((*e)->left.expr->v.val);
+						if ((*e)->left.expr->v.val->num == 0.0) {
+							free_expr((*e)->left.expr);
+							(*e)->left.expr = NULL;
+							(*e)->op = F_NEG;
+							(*e)->type = unopE;
+						}
+					} else if ((*e)->right->type == literE) {
+						V_NUM((*e)->right->v.val);
+						if ((*e)->right->v.val->num == 0.0) {
+							free_expr((*e)->right);
+							(*e)->right = (*e)->left.expr;
+							(*e)->left.expr = NULL;
+							(*e)->op = F_NUM;
+							(*e)->type = unopE;
+						}
+					}
+					break;
+
+				case B_SHARK_FIN:
+					if ((*e)->left.expr->type == symbolE) {
+						if ((*e)->left.expr->op == S_EULER) {
+							free_expr((*e)->left.expr);
+							(*e)->left.expr = NULL;
+							(*e)->op = F_EXP;
+							(*e)->type = unopE;
+						}
+					} else if ((*e)->right->type == literE) {
+						V_NUM((*e)->right->v.val);
+						if ((*e)->right->v.val->num == 0.5) {
+							free_expr((*e)->right);
+							(*e)->right = (*e)->left.expr;
+							(*e)->left.expr = NULL;
+							(*e)->op = F_SQRT;
+							(*e)->type = unopE;
+						}
+					}
+					break;
+
+				case B_FLATWORM:
+					if ((*e)->left.expr->type == literE) {
+						V_STR((*e)->left.expr->v.val);
+						if (!ko_length((*e)->left.expr->v.val->ko)) {
+							free_expr((*e)->left.expr);
+							(*e)->left.expr = NULL;
+							(*e)->op = F_STR;
+							(*e)->type = unopE;
+						}
+					} else if ((*e)->right->type == literE) {
+						V_STR((*e)->right->v.val);
+						if (!ko_length((*e)->right->v.val->ko)) {
+							free_expr((*e)->right);
+							(*e)->right = (*e)->left.expr;
+							(*e)->left.expr = NULL;
+							(*e)->op = F_STR;
+							(*e)->type = unopE;
+						}
+					}
+					break;
+
+				case B_SQIGGLE:
+				case B_XMATCH:
+					if ((*e)->right->type == literE) {
+						t_regex *rx;
+						V_STR((*e)->right->v.val);
+						rx = re_compile(ko_str((*e)->right->v.val->ko));
+						free_expr((*e)->right);
+						(*e)->right = (*e)->left.expr;
+						(*e)->left.rx = rx;
+						(*e)->op = F_MATCH;
+						(*e)->type = unopE;
+					}
+					break;
+			}
+			break;
+
+		case listE:
+			if ((*e)->right) {
+				trans_fold(&(*e)->right);
+				if ((*e)->left.expr) {
+					trans_fold(&(*e)->left.expr);
+				}
+			}
+			break;
+	}
+}
+
+#define SNAP(p)                    \
+do {                               \
+	while (                        \
+			(p) &&                 \
+			(p)->type == OP_NOP && \
+			(p) != (p)->next       \
+		  ) {                      \
+		(p) = (p)->next;           \
+	}                              \
+} while (0)
+
+static void walk(struct expr *e) {
+	for (;;) {
+		switch (e->type) {
+			case literE:
+			case symbolE:
+				return;
+
+			case varE:
+				return;
+
+			case varhashE:
+				e = e->right;
+				continue;
+
+			case unopE:
+				if (e->op == F_CALL) {
+					SNAP(e->left.op);
+					if (
+							e->left.op &&
+							e->left.op->type == OP_RETURN &&
+							hp_expr(e->right) &&
+							liter_expr(e->left.op->arg)
+					   ) {
+						free_expr(e->right);
+						e->right = NULL;
+						switch (e->left.op->arg->type) {
+							case literE:
+								e->v.val = v_undef();
+								v_set(e->v.val, e->left.op->arg->v.val);
+								e->type = literE;
+								break;
+
+							case symbolE:
+								e->op = e->left.op->arg->op;
+								e->type = symbolE;
+								break;
+
+							case listE:
+								assert(!e->left.op->arg->left.expr && !e->left.op->arg->right);
+								e->type = listE;
+								break;
+
+							default:
+								NOTREACHED;
+						}
+						e->left.expr = NULL;
+						return;
+					}
+				}
+				e = e->right;
+				continue;
+
+			case binopE:
+				walk(e->left.expr);
+				e = e->right;
+				continue;
+
+			case listE:
+				if (e->right) {
+					if (e->left.expr) {
+						walk(e->left.expr);
+					}
+					e = e->right;
+					continue;
+				}
+				return;
+		}
+	}
+}
+
+void transmogrify(struct text *code) {
+	size_t i;
+	struct op *hang = NULL;
+
+	for (i = 0; i < code->length; ++i) {
+		struct op *op = code->start[i];
+
+		switch (op->type) {
+			case OP_CALL:
+			case OP_CALL_DYN:
+			case OP_CLOSE:
+			case OP_EXIT:
+			case OP_FLUSH:
+			case OP_GOBACK:
+			case OP_GOTO:
+			case OP_IF:
+			case OP_RESET:
+			case OP_RETURN:
+			case OP_SYSTEM:
+			case OP_THROW:
+				trans_fold(&op->arg);
+				break;
+
+			case OP_ASSIGN:
+				trans_fold(&op->arg);
+				if (!op->arh.expr) {
+					if (hp_expr(op->arg)) {
+						free_expr(op->arg);
+						op->arg = NULL;
+						op->type = OP_NOP;
+					}
+				} else {
+					trans_fold(&op->arh.expr);
+					if (
+							op->arh.expr->type == varE &&
+							op->arg->type == binopE &&
+							op->arg->left.expr->type == varE &&
+							op->arh.expr->v.val == op->arg->left.expr->v.val
+					   ) {
+						struct expr *const tmp = op->arg;
+						op->arh.expr->op = op->arg->op;
+						op->arg = op->arg->right;
+						xfree(tmp->left.expr);
+						xfree(tmp);
+						op->type = OP_MODIFY;
+					} else if (op->arg->type == literE) {
+						op->type = OP_SET_VAL;
+					}
+				}
+				break;
+
+			case OP_CALL_BACK:
+			case OP_MODIFY:
+				trans_fold(&op->arh.expr);
+				trans_fold(&op->arg);
+				break;
+
+			case OP_PRINT:
+			case OP_PUTC:
+			case OP_TEMP:
+				trans_fold(&op->arg);
+				if (op->arh.expr) {
+					trans_fold(&op->arh.expr);
+				}
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	for (i = 0; i < code->length; ++i) {
+		struct op *op = code->start[i];
+
+		switch (op->type) {
+			case OP_CALL_BACK:
+				if (liter_expr(op->arh.expr)) {
+					struct val *v;
+
+					v = eval_expr(op->arh.expr);
+					TOLABEL(v);
+					free_expr(op->arh.expr);
+					if ((op->arh.op = ve_findprev(&Venus, ko_str(v->ko), op->line))) {
+						op->type = OP_CALL;
+					} else {
+						free_expr(op->arg);
+						op->arg = NULL;
+						op->type = OP_HANG;
+						hang = op;
+					}
+					v_end(v);
+					xfree(v);
+				}
+				break;
+
+			case OP_CALL_DYN:
+			case OP_GOBACK:
+			case OP_GOTO:
+				if (liter_expr(op->arg)) {
+					struct val *v;
+
+					v = eval_expr(op->arg);
+					TOLABEL(v);
+					free_expr(op->arg);
+					op->arg = NULL;
+					if ((op->arh.op = (op->type == OP_GOBACK ? ve_findprev : ve_findnext)(&Venus, ko_str(v->ko), op->line))) {
+						if (op->type == OP_CALL_DYN) {
+							op->type = OP_CALL;
+							op->arg = xmalloc(1, sizeof *op->arg);
+							op->arg->left.expr = op->arg->right = NULL;
+							op->arg->type = literE;
+							op->arg->v.val = v_undef();
+						} else {
+							op->next = op->arh.op;
+							op->arh.op = NULL;
+							op->type = OP_NOP;
+						}
+					} else {
+						op->type = OP_HANG;
+						hang = op;
+					}
+					v_end(v);
+					xfree(v);
+				}
+				break;
+
+			case OP_HANG:
+				hang = op;
+				break;
+
+			case OP_IF:
+				if (liter_expr(op->arg)) {
+					struct val *v;
+
+					v = eval_expr(op->arg);
+					if (v_true(v)) {
+						op->next = op->arh.op;
+					}
+					v_delete(v);
+					op->type = OP_NOP;
+					op->arh.expr = NULL;
+					free_expr(op->arg);
+					op->arg = NULL;
+				}
+				break;
+
+			case OP_PRINT:
+				if (
+						op->arg->type == literE &&
+						(
+						 (
+						  op->arg->v.val->type == V_STR_K &&
+						  ko_length(op->arg->v.val->ko) == 0
+						 ) ||
+						 op->arg->v.val->type == V_UNDEF
+						)
+				   ) {
+					op->type = OP_NOP;
+					free_expr(op->arg);
+					op->arg = NULL;
+					if (op->arh.expr) {
+						free_expr(op->arh.expr);
+						op->arh.expr = NULL;
+					}
+					break;
+				}
+				if (
+						op->arh.expr &&
+						op->arh.expr->type == symbolE &&
+						op->arh.expr->op == S_STDOUT
+				   ) {
+					free_expr(op->arh.expr);
+					op->arh.expr = NULL;
+				}
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	if (!hang) {
+		struct op tmp;
+
+		tmp.type = OP_HANG;
+		tmp.arg = tmp.arh.expr = NULL;
+		tmp.next = NULL;
+		St_init(&tmp.txt);
+		St_clear(&tmp.txt);
+		tmp.line = -1;
+
+		hang = text_push(code, &tmp);
+	}
+
+	for (i = 0; i < code->length; ++i) {
+		struct op *op = code->start[i];
+
+		SNAP(op->next);
+		if (op->type == OP_IF) {
+			SNAP(op->arh.op);
+		} else if (op->type == OP_CALL) {
+			SNAP(op->arh.op);
+			if (!op->arh.op) {
+				struct expr *tmp = xmalloc(1, sizeof *tmp);
+				tmp->type = binopE;
+				tmp->op = B_TAIL;
+				tmp->left.expr = op->arg;
+				tmp->right = xmalloc(1, sizeof *tmp->right);
+				tmp->right->type = literE;
+				tmp->right->v.val = v_undef();
+				tmp->right->left.expr = tmp->right->right = NULL;
+				op->arg = tmp;
+				op->type = OP_EXIT;
+				trans_fold(&op->arg);
+			}
+		}
+
+		if (op == op->next) {
+			op->next = hang;
+		}
+		if (OP_1ARG_P(op->type)) {
+			walk(op->arg);
+		} else if (OP_2ARG_P(op->type)) {
+			if (op->arh.expr) {
+				walk(op->arh.expr);
+			}
+			walk(op->arg);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/transmogrify.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,3 @@
+transmogrify.o: transmogrify.c config.h Str.h expr.h op.h IO.h re.h \
+  stack.h xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h \
+  main_label.h mars.h venus.h hash.h text.h transmogrify.h zz.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/transmogrify.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,10 @@
+#ifndef TRANSMOGRIFY_H_
+#define TRANSMOGRIFY_H_
+
+#include "expr.h"
+#include "text.h"
+
+void trans_fold(struct expr **);
+void transmogrify(struct text *);
+
+#endif /* TRANSMOGRIFY_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/try/poly.lhs	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+poly.poly
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/try/poly.poly	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,94 @@
+ # /* [	<!-- */ include	<stdio.h> /*[0]0 	\
+	#{\
+`""""true \\#{"\n#"};    	  			\
+	\
+if [ -n "$ZSH_VERSION" ]; then		 		 	\
+	\
+    echo exec	echo I\'m a zsh script.; \
+	\
+elif [ -n "$BASH_VERSION" ]; then		    	\
+	\
+    echo exec	echo I\'m a bash script.; \
+else	\
+    echo exec	echo	I\'m	a sh	script.;		\
+fi`;	#\
+BEGIN{print"I'm a ",(("I'm a ploki program.\n")[0?0:0]?0:0)?"Ruby"	:"Perl",	" program.\n";	exit; }  
+	#\
+END{}    
+ #\
+%q~      		 	  	
+	
+set dummy =0; puts [list "I'm"	"a"	"tcl"	"script."]; exit	  
+	
+all: ; @echo "I'm a Makefile."		  	 	\
+	#)*/
+/*: */ enum {a, b}; 			  		\
+	\
+static int c99(void) {  			    
+	
+ #ifndef __cplusplus /*[0]0) bah */		    	
+	
+unused1: if ((enum {b, a})0) 		   		\
+	(void)0;
+ #endif    		  	 	
+	
+unused2:    return a;	     \
+}	\
+static int trigraphs(void) {  			    \
+	\
+    return sizeof	"??!"	==	2;  	 \
+}	\
+char X;    		 				\
+	\
+int main(void) {   		  			\
+	\
+    struct X	{		  	 \
+	\
+     	char	a[2];    	\
+	};\
+    if (sizeof(X)	!=	1) {		 	\
+	\
+printf("I'm a C++ program (trigraphs %sabled).\n",	 			 \
+	\
+     trigraphs()	? "en"	: "dis");\
+	\
+}else if (1//**/2
+
+
+)unused3 : { ; \
+        printf("I'm a C program (C%s, trigraphs %sabled).\n", \
+               c99() ? "89 with // comments" : "99", \
+               trigraphs() ? "en" : "dis"); \
+    } else { \
+        printf("I'm a C program (C89, trigraphs %sabled).\n", \
+               trigraphs() ? "en" : "dis"); \
+    } \
+    return 0; \
+} /*
+ # \
+
+> main :: IO () -- -- \
+> main = putStr "I'm a Literate Haskell program.\n"
+
+ # \
+]>++++++++[<+++++++++>-]<+.>>++++[<++++++++++>-]<-.[-]>++++++++++ \
+[<+++++++++++>-]<-.>>++++[<++++++++>-]<.>>++++++++++[<++++++++++> \
+-]<- - -.<.>+.->>++++++++++[<+++++++++++>-]<++++.<.>>>++++++++++[ \
+<++++++++++>-]<+++++.<<<<+.->>>>- - -.<+++.- - -<++.- ->>>>>+++++ \
++++++[<+++++++++++>-]<- - -.<<<<<.<+++.>>>.<<<-.- ->>>>+.<.<.<<.> \
+++++++++++++++.[-]++++++++++"""`
+ # \
+print "I'm a Python program."; """[-][--><html><head>
+<!--:--><title>I'm a HTML page</title></head><body>
+<!--:--><h1>I'm a <marquee><blink>horrible HTML</blink></marquee> page</h1>
+<!--:--><script language="JavaScript">
+<!--: # \
+setTimeout( // \
+   function () { // \
+      document.body.innerHTML = "<h1>I'm a javascript-generated HTML page</h1>"; // \
+   }, 10000); // \
+//-->
+</script><!--: \
+</body></html><!-- } # \
+say "I'm a Perl6 program", try { " ($?PUGS_VERSION)" } // "", "."; # """ # */
+ #define FOO ]-->~
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/try/t.pk	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,22 @@
+LET n "tmp.txt"
+LET fh @APERS #<n "W+"#>
+IF @NOT fh
+	WUNT \FEHL "open: " _ n _ ": " _ \! _ "
+	END 1
+END IF
+WUNT fh "zomg
+IF @SUCH #<fh 0#>
+	WUNT \FEHL "seek: " _ n _ ": " _ \! _ "
+	END 1
+FI
+LET s fh . @NEG 1
+IF @ERR-P fh
+	WUNT \FEHL "read: " _ n _ ": " _ \! _ "
+	END 1
+FI
+CLAUDS fh
+IF \_
+	WUNT \FEHL "close: " _ n _ ": " _ \! _ "
+	END 1
+END IF
+s
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/val.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,219 @@
+#include "config.h"
+#include "IO.h"
+#include "Str.h"
+#include "val.h"
+#include "xmalloc.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+void v_init(struct val *v) {
+	v->ko = ko_new();
+	v->type = V_UNDEF;
+}
+
+void v_end(struct val *v) {
+	assert(v != NULL);
+	V_xxx_OFF(v);
+	ko_delete(v->ko);
+	v->type = V_UNDEF;
+}
+
+int v_cmp_ls(struct val *v, struct val *b) {
+	if (V_LIST_P(v) && V_LIST_P(b)) {
+		return li_cmp(v->magic.list, b->magic.list);
+	}
+	V_STR(v);
+	V_STR(b);
+	return ko_cmp(v->ko, b->ko);
+}
+
+void v_ok_str(struct val *v) {
+	if (V_STR_P(v)) {
+		return;
+	}
+
+	if (V_EXT_P(v)) {
+		ko_cpy_s(v->ko, io_name(v->magic.ext, NULL));
+	} else if (V_LIST_P(v)) {
+		size_t i;
+		struct list *li = v->magic.list;
+		struct val *cur;
+
+		ko_zero(v->ko);
+		for (i = 0; i < li_length(li); ++i) {
+			cur = li_at(li, i);
+			V_STR(cur);
+			ko_cat(v->ko, cur->ko);
+		}
+	} else if (V_NUM_P(v)) {
+		ko_num(v->ko, v->num);
+	} else if (V_SUB_P(v)) {
+		v_ok_num(v);
+		ko_num(v->ko, v->num);
+	} else {
+		ko_zero(v->ko);
+	}
+	v->type |= V_STR_K;
+}
+
+void v_ok_num(struct val *v) {
+	if (V_NUM_P(v)) {
+		return;
+	}
+
+	if (V_EXT_P(v)) {
+		v->num = __LINE__;
+	} else if (V_SUB_P(v)) {
+		v->num = sub_id(v->magic.sub);
+	} else if (V_LIST_P(v)) {
+		size_t i;
+
+		v->num = 0.0;
+		for (i = 0; i < li_length(v->magic.list); ++i) {
+			V_NUM(li_at(v->magic.list, i));
+			v->num += li_at(v->magic.list, i)->num;
+		}
+	} else if (V_STR_P(v)) {
+		v->num = strtod(ko_szp(v->ko), NULL);
+	} else {
+		v->num = 0.0;
+	}
+	v->type |= V_NUM_K;
+}
+
+int v_true(const struct val *v) {
+	if (V_EXT_P(v) || V_SUB_P(v)) {
+		return 1;
+	}
+	if (V_LIST_P(v)) {
+		return li_length(v->magic.list) != 0;
+	}
+	if (V_STR_P(v)) {
+		return ko_length(v->ko) != 0 && (ko_length(v->ko) > 1u || ko_at(v->ko, 0) != '0');
+	}
+	if (V_NUM_P(v)) {
+		return v->num != 0.0;
+	}
+	return 0;
+}
+
+void v_iniset(struct val *dst, const struct val *src) {
+	v_init(dst);
+	v_set(dst, src);
+}
+
+void v_set(struct val *dst, const struct val *src) {
+	assert(src != NULL);
+
+	V_xxx_OFF(dst);
+	dst->type = V_UNDEF;
+
+	if (V_SUB_P(src)) {
+		dst->magic.sub = sub_incr(src->magic.sub);
+		dst->type |= V_SUB_K;
+	} else if (V_EXT_P(src)) {
+		dst->magic.ext = io_incr(src->magic.ext);
+		dst->type |= V_EXT_K;
+	} else if (V_LIST_P(src)) {
+		dst->magic.list = li_dup(src->magic.list);
+		dst->type |= V_LIST_K;
+	}
+
+	if (V_STR_P(src)) {
+		ko_cpy(dst->ko, src->ko);
+		dst->type |= V_STR_K;
+	}
+	if (V_NUM_P(src)) {
+		dst->num = src->num;
+		dst->type |= V_NUM_K;
+	}
+}
+
+void v_set_n(struct val *v, double d) {
+	V_xxx_OFF(v);
+	v->num = d;
+	v->type = V_NUM_K;
+}
+
+void v_set_s(struct val *v, const String *s) {
+	V_xxx_OFF(v);
+	ko_cpy_m(v->ko, St_ptr(s), St_len(s));
+	v->type = V_STR_K;
+}
+
+void v_set_m(struct val *v, const void *p, size_t n) {
+	V_xxx_OFF(v);
+	ko_cpy_m(v->ko, p, n);
+	v->type = V_STR_K;
+}
+
+void v_set_io(struct val *v, IO *io) {
+	V_xxx_OFF(v);
+	v->magic.ext = io_incr(io);
+	v->type = V_EXT_K;
+}
+
+void v_set_sub(struct val *v, struct sub *s) {
+	V_xxx_OFF(v);
+	v->magic.sub = sub_incr(s);
+	v->type = V_SUB_K;
+}
+
+void v_set_undef(struct val *v) {
+	V_xxx_OFF(v);
+	v->type = V_UNDEF;
+}
+
+void v_cat(struct val *dst, struct val *src) {
+	if (V_LIST_P(dst) && V_LIST_P(src)) {
+		li_append(dst->magic.list, src->magic.list);
+		return;
+	}
+
+	V_STR(src);
+	V_STR(dst);
+	V_xxx_OFF(dst);
+	ko_cat(dst->ko, src->ko);
+	dst->type = V_STR_K;
+}
+
+void v_cat_m(struct val *v, const void *p, size_t n) {
+	V_STR(v);
+	V_xxx_OFF(v);
+	ko_cat_m(v->ko, p, n);
+	v->type = V_STR_K;
+}
+
+void v_cat_s(struct val *v, const String *s) {
+	v_cat_m(v, St_ptr(s), St_len(s));
+}
+
+void v_cat_c(struct val *v, char c) {
+	v_cat_m(v, &c, 1);
+}
+
+struct val *v_undef(void) {
+	struct val *v;
+	v = xmalloc(1, sizeof *v);
+	v_init(v);
+	return v;
+}
+
+void v_delete(struct val *v) {
+	v_end(v);
+	xfree(v);
+}
+
+const char *v_sptr(struct val *v, size_t *l) {
+	V_STR(v);
+	if (l) {
+		*l = ko_length(v->ko);
+	}
+	return ko_ptr(v->ko);
+}
+
+struct kork *v_kork(struct val *v) {
+	V_STR(v);
+	return v->ko;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/val.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+val.o: val.c config.h IO.h Str.h val.h kork.h list.h sub.h xmalloc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/val.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,80 @@
+#ifndef VAL_H_
+#define VAL_H_
+
+#include "config.h"
+#include "IO.h"
+#include "Str.h"
+#include "kork.h"
+#include "list.h"
+#include "sub.h"
+
+enum val_cont {
+	V_UNDEF = 0,
+	V_STR_K = 1,
+	V_NUM_K = V_STR_K << 1,
+	V_EXT_K = V_NUM_K << 1,
+	V_SUB_K = V_EXT_K << 1,
+	V_LIST_K = V_SUB_K << 1
+};
+
+struct val {
+	enum val_cont type;
+	union {
+		IO *ext;
+		struct sub *sub;
+		struct list *list;
+	} magic;
+	struct kork *ko;
+	double num;
+};
+
+void v_init(struct val *);
+void v_end(struct val *);
+int v_cmp_ls(struct val *, struct val *);
+void v_ok_str(struct val *);
+void v_ok_num(struct val *);
+int v_true(const struct val *);
+void v_iniset(struct val *, const struct val *);
+void v_set(struct val *, const struct val *);
+void v_set_n(struct val *, double);
+void v_set_s(struct val *, const String *);
+void v_set_m(struct val *, const void *, size_t);
+void v_set_undef(struct val *);
+void v_set_io(struct val *, IO *);
+void v_set_sub(struct val *, struct sub *);
+void v_cat(struct val *, struct val *);
+void v_cat_s(struct val *, const String *);
+void v_cat_m(struct val *, const void *, size_t);
+void v_cat_c(struct val *, char);
+struct val *v_undef(void);
+void v_delete(struct val *);
+
+const char *v_sptr(struct val *, size_t *);
+struct kork *v_kork(struct val *);
+
+#define V_STR_P(v) ((v)->type & V_STR_K)
+#define V_NUM_P(v) ((v)->type & V_NUM_K)
+#define V_EXT_P(v) ((v)->type & V_EXT_K)
+#define V_SUB_P(v) ((v)->type & V_SUB_K)
+#define V_LIST_P(v) ((v)->type & V_LIST_K)
+
+#define V_STR(v) if (V_STR_P(v)); else v_ok_str(v)
+#define V_NUM(v) if (V_NUM_P(v)); else v_ok_num(v)
+
+#define V_xxx_OFF(v)                \
+do {                                \
+	if (V_EXT_P(v)) {               \
+		io_decr((v)->magic.ext);    \
+		(v)->type &= ~V_EXT_K;      \
+	} else if (V_SUB_P(v)) {        \
+		sub_decr((v)->magic.sub);   \
+		(v)->type &= ~V_SUB_K;      \
+	} else if (V_LIST_P(v)) {       \
+		li_delete((v)->magic.list); \
+		(v)->type &= ~V_LIST_K;     \
+	}                               \
+} while (0)
+
+#define RINT(x) ((x) + ((x) < 0.0 ? -.5 : .5))
+
+#endif /* VAL_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/variable.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,72 @@
+#include "config.h"
+#include "Str.h"
+#include "strhash.h"
+#include "variable.h"
+#include "xmalloc.h"
+
+#include <assert.h>
+
+enum {MAGIC = 23};
+
+static void delcookie(void *c) {
+	xfree(c);
+}
+
+t_vr_container *vr_new(void (*del)(void *)) {
+	t_vr_container *v = xmalloc(1, sizeof *v);
+	v->data = xmalloc(v->size = MAGIC, sizeof *v->data);
+	v->length = 0;
+	v->del = del;
+	v->hash = sh_new(delcookie);
+	return v;
+}
+
+void vr_delete(t_vr_container *v) {
+	assert(v != NULL);
+	if (v->hash) {
+		vr_freeze(v);
+	}
+	while (v->length) {
+		--v->length;
+		if (v->del) {
+			v->del(v->data[v->length]);
+		}
+	}
+	xfree(v->data);
+	xfree(v);
+}
+
+t_vr_cookie vr_exists(const t_vr_container *v, const char *key, size_t len) {
+	t_vr_cookie *c;
+	assert(v->hash != NULL);
+	return (c = sh_get(v->hash, key, len)) ? *c : VR_NO_COOKIE;
+}
+
+t_vr_cookie vr_register(t_vr_container *v, const char *key, size_t len, void *val) {
+	t_vr_cookie *cookie;
+
+	assert(v->hash != NULL);
+	assert(sh_get(v->hash, key, len) == NULL);
+
+	if (v->length >= v->size) {
+		v->data = xrealloc(v->data, v->size *= 2);
+	}
+
+	cookie = xmalloc(1, sizeof *cookie);
+	v->data[*cookie = v->length++] = val;
+	sh_put(v->hash, key, len, cookie);
+
+	return *cookie;
+}
+
+void vr_freeze(t_vr_container *v) {
+	assert(v->hash != NULL);
+	sh_delete(v->hash);
+	v->hash = NULL;
+}
+
+void *(vr_data)(const t_vr_container *v, t_vr_cookie c) {
+	assert(v->hash == NULL);
+	assert(c < v->length);
+	return v->data[c];
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/variable.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+variable.o: variable.c config.h Str.h strhash.h variable.h xmalloc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/variable.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,34 @@
+#ifndef VARIABLE_H_
+#define VARIABLE_H_
+
+#include "config.h"
+#include "strhash.h"
+
+#include <stddef.h>
+
+typedef size_t t_vr_cookie;
+#define VR_NO_COOKIE ((size_t)-1)
+
+typedef struct {
+	void **data;
+	size_t size, length;
+	t_strhash *hash;
+	void (*del)(void *);
+} t_vr_container;
+
+t_vr_container *vr_new(void (*)(void *));
+void vr_delete(t_vr_container *);
+
+t_vr_cookie vr_exists(const t_vr_container *, const char *, size_t);
+t_vr_cookie vr_register(t_vr_container *, const char *, size_t, void *);
+
+void vr_freeze(t_vr_container *);
+
+ATTR_PURE
+void *vr_data(const t_vr_container *, t_vr_cookie);
+
+#if !DEBUG_P
+#define vr_data(vc, c) ((vc)->data[c])
+#endif
+
+#endif /* VARIABLE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/venus.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,146 @@
+#include "Str.h"
+#include "hash.h"
+#include "op.h"
+#include "venus.h"
+#include "xmalloc.h"
+#include "zz.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+enum {MAGIC = 23};
+
+struct sorted {
+	size_t size, length;
+	struct op **array;
+};
+
+static size_t hash(const void *p, size_t seed) {
+	return St_hash(p, seed);
+}
+
+static int compar(const void *a, const void *b) {
+	return St_cmp(a, b);
+}
+
+static void delk(void *k) {
+	St_clear(k);
+	xfree(k);
+}
+
+static void delv(void *v) {
+	xfree(((struct sorted *)v)->array);
+	xfree(v);
+}
+
+void ve_init(struct venus *v) {
+	h_init(&v->table, hash, compar, delk, delv);
+}
+
+void ve_end(struct venus *v) {
+	h_end(&v->table);
+}
+
+void ve_enter(struct venus *v, const String *s, struct op *o) {
+	struct sorted *x;
+	void *tmp;
+	size_t a, z;
+
+	if (h_get(&v->table, s, &tmp) == H_NOENT) {
+		String *label;
+		x = xmalloc(1, sizeof *x);
+		x->array = xmalloc(x->size = MAGIC, sizeof *x->array);
+		x->array[0] = o;
+		x->length = 1;
+		label = xmalloc(1, sizeof *label);
+		St_init(label);
+		St_cpy(label, s);
+		h_put(&v->table, label, x, 1);
+		return;
+	}
+	x = tmp;
+
+	for (a = 0, z = x->length; a < z; ) {
+		if (x->array[(a + z) / 2]->line < o->line) {
+			a = (a + z) / 2 + 1u;
+		} else if (x->array[(a + z) / 2]->line > o->line) {
+			z = (a + z) / 2;
+		} else {
+			NOTREACHED;
+		}
+	}
+
+	if (x->length >= x->size) {
+		x->array = xrealloc(x->array, x->size *= 2);
+	}
+	memmove(x->array + a + 1u, x->array + a, x->length - a);
+	x->array[a] = o;
+	x->length++;
+}
+
+struct op *ve_findnext(struct venus *v, const String *s, size_t line) {
+	struct sorted *x;
+	void *tmp;
+	size_t a, z;
+
+	if (h_get(&v->table, s, &tmp) == H_NOENT) {
+		return NULL;
+	}
+	x = tmp;
+
+	for (a = 0, z = x->length; a < z; ) {
+		if (x->array[(a + z) / 2]->line < line) {
+			a = (a + z) / 2 + 1u;
+		} else if (x->array[(a + z) / 2]->line > line) {
+			z = (a + z) / 2;
+		} else {
+			a = (a + z) / 2 + 1u;
+			break;
+		}
+	}
+
+	return x->array[a % x->length];
+}
+
+struct op *ve_findprev(struct venus *v, const String *s, size_t line) {
+	struct sorted *x;
+	void *tmp;
+	size_t a, z;
+
+	if (h_get(&v->table, s, &tmp) == H_NOENT) {
+		return NULL;
+	}
+	x = tmp;
+
+	for (a = 0, z = x->length; a < z; ) {
+		if (x->array[(a + z) / 2]->line < line) {
+			a = (a + z) / 2 + 1u;
+		} else if (x->array[(a + z) / 2]->line > line) {
+			z = (a + z) / 2;
+		} else {
+			a = (a + z) / 2;
+			break;
+		}
+	}
+	a += x->length - 1u;
+	return x->array[a % x->length];
+}
+
+const String *ve_label(struct venus *v, const struct op *op) {
+	void *key, *value;
+
+	h_reset(&v->table);
+	while (h_nextkv(&v->table, &key, &value) == H_OK) {
+		const struct sorted *x = value;
+		size_t i;
+
+		for (i = 0; i < x->length; ++i) {
+			if (x->array[i] == op) {
+				return key;
+			}
+		}
+	}
+
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/venus.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,2 @@
+venus.o: venus.c Str.h config.h hash.h op.h IO.h expr.h re.h stack.h \
+  xmalloc.h strhash.h val.h kork.h list.h sub.h variable.h venus.h zz.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/venus.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,19 @@
+#ifndef VENUS_H_
+#define VENUS_H_
+
+#include "Str.h"
+#include "hash.h"
+#include "op.h"
+
+struct venus {
+	Hash table;
+};
+
+void ve_init(struct venus *);
+void ve_end(struct venus *);
+void ve_enter(struct venus *, const String *, struct op *);
+struct op *ve_findnext(struct venus *, const String *, size_t);
+struct op *ve_findprev(struct venus *, const String *, size_t);
+const String *ve_label(struct venus *, const struct op *);
+
+#endif /* VENUS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/version.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,5 @@
+#line 1 "version.c.in"
+#include "version.h"
+
+const char *Version =
+"0.6.5.1";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/version.c.in	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,4 @@
+#line 1 "version.c.in"
+#include "version.h"
+
+const char *Version =
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/version.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+version.o: version.c version.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/version.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,6 @@
+#ifndef VERSION_H_
+#define VERSION_H_
+
+extern const char *Version;
+
+#endif /* VERSION_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/xmalloc.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,168 @@
+#include "config.h"
+#include "main.h"
+#include "xmalloc.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef DEBUG_MALLOC
+	#define IF_DB(x) x
+#else
+	#define IF_DB(x)
+#endif
+
+static struct node {
+	void *ptr;
+	struct node *prev, *next;
+	size_t size;
+	#if DEBUG_P
+		const char *file;
+		unsigned line;
+	#endif
+} *Root;
+
+#if 0
+int xdump(void) {
+	struct node *p;
+
+	fprintf(stderr, ":%p\n", Root);
+	for (p = Root; p; p = p->next) {
+		fprintf(stderr, "\\<%p [%p]%p %p>\n", p->prev, p, p->ptr, p->next);
+		assert(p != p->next);
+	}
+	fprintf(stderr, "\n");
+	return 0;
+}
+#endif
+
+void xend(void) {
+	struct node *p;
+
+	while ((p = Root)) {
+#if DEBUG_P
+		unsigned char *ptr;
+		for (ptr = p->ptr; memcmp(ptr, &p, sizeof p); ++ptr)
+			;
+		ptr += sizeof p;
+		fprintf(stderr, "%s: autofreeing pointer %p (%p) from %s:%u\n", Prog, p->ptr, (void *)ptr, p->file, p->line);
+#else
+		fprintf(stderr, "%s: autofreeing pointer %p\n", Prog, p->ptr);
+#endif
+		free(p->ptr);
+		assert(Root != p->next);
+		Root = p->next;
+		free(p);
+	}
+}
+
+#define XFACTOR(size) ((sizeof (struct node *) - 1) / (size) + 1u)
+
+#if DEBUG_P
+void *(xrealloc)(void *optr, size_t nmemb, const char *file, unsigned line) {
+#else
+void *xrealloc(void *optr, size_t nmemb) {
+#endif
+	struct node *p;
+	size_t k;
+	size_t size;
+	unsigned char *r;
+	void *ptr;
+
+	assert(optr != NULL);
+
+	memcpy(&p, (unsigned char *)optr - sizeof Root, sizeof p);
+	size = p->size;
+	k = XFACTOR(size) * size;
+	if (!(ptr = realloc(p->ptr, size * nmemb + k))) {
+		fprintf(stderr, "%s: realloc(%p, %lu): %s\n", Prog, ptr, (unsigned long)(nmemb * size), strerror(errno));
+		abort();
+	}
+	r = (unsigned char *)ptr + k;
+	#if DEBUG_P
+		IF_DB(fprintf(stderr, "%s: xrealloc(%zu): %p (%p) from %s:%u -> %p (%p) from %s:%u\n", Prog, nmemb, p->ptr, optr, p->file, p->line, ptr, r, file, line));
+		p->file = file;
+		p->line = line;
+	#endif
+	p->ptr = ptr;
+	return r;
+}
+
+#if DEBUG_P
+void *(xmalloc)(size_t nmemb, size_t size, const char *file, unsigned line) {
+#else
+void *xmalloc(size_t nmemb, size_t size) {
+#endif
+	struct node *p;
+	size_t k;
+	unsigned char *r;
+
+	if ((p = malloc(sizeof *p))) {
+		k = XFACTOR(size) * size;
+		if (!(p->ptr = malloc(size * nmemb + k))) {
+			free(p);
+			goto failure;
+		}
+	} else failure: {
+		fprintf(stderr, "%s: malloc(%lu): %s\n", Prog, (unsigned long)(nmemb * size), strerror(errno));
+		abort();
+	}
+	#if DEBUG_P
+		memset(p->ptr, 'U', k);
+	#endif
+	r = (unsigned char *)p->ptr + k;
+	memcpy(r - sizeof Root, &p, sizeof p);
+	p->size = size;
+	p->prev = NULL;
+	p->next = Root;
+	#if DEBUG_P
+		p->file = file;
+		p->line = line;
+		IF_DB(fprintf(stderr, "%s: xmalloc(%zu, %zu): %p (%p) from %s:%u\n", Prog, nmemb, size, p->ptr, r, file, line));
+	#endif
+	if (Root) {
+		Root->prev = p;
+	}
+	Root = p;
+	return r;
+}
+
+#if 0
+#if DEBUG_P
+void *(xcalloc)(size_t nmemb, size_t size, const char *file, unsigned line) {
+	void *const p = xmalloc(nmemb, size, file, line);
+#else
+void *xcalloc(size_t nmemb, size_t size) {
+	void *const p = xmalloc(nmemb, size);
+#endif
+	memset(p, '\0', nmemb * size);
+	return p;
+}
+#endif
+
+void xfree(void *ptr) {
+	struct node *p;
+
+	if (!ptr)
+		return;
+	memcpy(&p, (unsigned char *)ptr - sizeof Root, sizeof p);
+	assert(p != NULL);
+	DEBUG(memset((unsigned char *)ptr - sizeof Root, 'F', sizeof Root));
+
+	if (p->next) {
+		p->next->prev = p->prev;
+	}
+	if (p->prev) {
+		p->prev->next = p->next;
+	} else {
+		assert(p == Root);
+		Root = Root->next;
+	}
+	#if DEBUG_P
+		IF_DB(fprintf(stderr, "%s: xfree(): %p (%p) from %s:%u\n", Prog, p->ptr, ptr, p->file, p->line));
+	#endif
+	free(p->ptr);
+	free(p);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/xmalloc.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+xmalloc.o: xmalloc.c config.h main.h xmalloc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/xmalloc.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,20 @@
+#ifndef XMALLOC_H_
+#define XMALLOC_H_
+
+#include "config.h"
+
+#include <stddef.h>
+
+void xend(void);
+#if DEBUG_P
+void *xmalloc(size_t, size_t, const char *, unsigned);
+void *xrealloc(void *, size_t, const char *, unsigned);
+#define xmalloc(n, m) xmalloc(n, m, __FILE__, __LINE__)
+#define xrealloc(p, n) xrealloc(p, n, __FILE__, __LINE__)
+#else
+void *xmalloc(size_t, size_t);
+void *xrealloc(void *, size_t);
+#endif
+void xfree(void *);
+
+#endif /* XMALLOC_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/zz.c	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,10 @@
+#include "zz.h"
+#include "main.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void omgwtf_this_cant_be_happening(const char *file, unsigned long line) {
+	fprintf(stderr, "%s: %s:%lu: error message\n", Prog, file, line);
+	abort();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/zz.depend	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,1 @@
+zz.o: zz.c zz.h config.h main.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ploki/zz.h	Fri Dec 20 22:04:38 2013 +0000
@@ -0,0 +1,11 @@
+#ifndef ZZ_H_
+#define ZZ_H_
+
+#include "config.h"
+
+ATTR_NORETURN
+void omgwtf_this_cant_be_happening(const char *, unsigned long);
+
+#define NOTREACHED omgwtf_this_cant_be_happening(__FILE__, __LINE__)
+
+#endif /* ZZ_H_ */