Mercurial > repo
diff interps/bfjoust/gearlance.c @ 996:859f9b4339e6
<Gregor> tar xf egobot.tar.xz
author | HackBot |
---|---|
date | Sun, 09 Dec 2012 19:30:08 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interps/bfjoust/gearlance.c Sun Dec 09 19:30:08 2012 +0000 @@ -0,0 +1,814 @@ +/* + * gearlance bfjoust interpreter; based on cranklance, itself based on + * chainlance. + * + * Copyright 2011 Heikki Kallasjoki. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY HEIKKI KALLASJOKI ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL HEIKKI KALLASJOKI OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The views and conclusions contained in the software and + * documentation are those of the authors and should not be + * interpreted as representing official policies, either expressed or + * implied, of Heikki Kallasjoki. + */ + +#include <fcntl.h> +#include <setjmp.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#define MAXCYCLES 100000 +#define MAXNEST 256 + +#define MINTAPE 10 +#define MAXTAPE 30 + +/* #define TRACE 1 */ + +static int scores[2][MAXTAPE+1]; + +/* types */ + +enum optype +{ + OP_INC, OP_DEC, + OP_LEFT, OP_RIGHT, + OP_WAIT, + OP_LOOP1, OP_LOOP2, + OP_REP1, OP_REP2, + OP_INNER1, OP_INNER2 +}; + +struct op +{ + enum optype type; + int match; /* offset of matching delimiter for [({ })] pairs */ + int inner; /* extra links between matched ( .. { and } .. ) */ + int count; /* static repetition count for the ({}) instructions */ +}; + +struct oplist +{ + int len, size; + struct op *ops; +}; + +/* generic helpers */ + +static void die(const char *fmt, ...); +static void fail(const char *fmt, ...); + +static char fail_msg[256]; +static jmp_buf fail_buf; + +static void *smalloc(size_t size); +static void *srealloc(void *ptr, size_t size); + +static int sopen(const char *fname); + +/* oplist handling */ + +static struct oplist *opl_new(void); +static void opl_free(struct oplist *o); +static void opl_append(struct oplist *o, enum optype type); +static void opl_del(struct oplist *o, int start, int end); + +/* parsing and preprocessing */ + +static struct oplist *parse(int fd); + +/* actual interpretation */ + +static void run(struct oplist *opsA, struct oplist *opsB); + +/* main application */ + +int main(int argc, char *argv[]) +{ + /* check args */ + + if (argc < 3) + { + fprintf(stderr, "usage: %s prog1.bfjoust prog2.bfjoust [score pid]\n", argv[0]); + return 1; + } + + printf("%s vs %s\n", argv[1], argv[2]); + + /* parse competitors */ + + int fdA = sopen(argv[1]), fdB = sopen(argv[2]); + + if (setjmp(fail_buf)) + { + printf("parse error: %s\n\n", fail_msg); + return 1; + } + + struct oplist *opsA = parse(fdA), *opsB = parse(fdB); + + /* for debuggin purposes, dump out the parse */ + +#if 0 + unsigned char opchars[] = { + [OP_INC] = '+', [OP_DEC] = '-', [OP_LEFT] = '<', [OP_RIGHT] = '>', + [OP_WAIT] = '.', [OP_LOOP1] = '[', [OP_LOOP2] = ']', + [OP_REP1] = '(', [OP_REP2] = ')', [OP_INNER1] = '{', [OP_INNER2] = '}' + }; + for (int at = 0; at < opsA->len; at++) + { + struct op *op = &opsA->ops[at]; + printf("%3d: %c (%-2d {%-2d *%-2d\n", at, opchars[op->type], op->match, op->inner, op->count); + } + return 0; +#endif + + /* run them */ + + run(opsA, opsB); + + /* summarize results */ + + int score = 0; + + for (int pol = 0; pol < 2; pol++) + { + for (int tlen = MINTAPE; tlen <= MAXTAPE; tlen++) + { + putchar(scores[pol][tlen] ? (scores[pol][tlen] > 0 ? '<' : '>') : 'X'); + score += scores[pol][tlen]; + } + putchar(' '); + } + + /* score is also output to a pid */ + if (argc >= 4) { + write(atoi(argv[3]), &score, sizeof(int)); + } + + printf("%d\n", score); + + if (score < 0) { + printf("%s wins.\n\n", argv[1]); + } else if (score > 0) { + printf("%s wins.\n\n", argv[2]); + } else { + printf("Tie.\n\n"); + } + + opl_free(opsA); + opl_free(opsB); + + return 0; +} + +/* actual interpretation, impl */ + +static unsigned char tape[MAXTAPE]; + +static void run(struct oplist *opsA, struct oplist *opsB) +{ + struct op *oplA = opsA->ops, *oplB = opsB->ops; + + /* convert opcode types into pointers for both progs */ + + void **opcA = smalloc((opsA->len+1) * sizeof *opcA); + void **opcB = smalloc((opsB->len+1) * sizeof *opcB); + + for (int at = 0; at < opsA->len; at++) + { + struct op *op = &oplA[at]; + void **opc = &opcA[at]; + switch (op->type) + { + case OP_INC: *opc = &&op_incA; break; + case OP_DEC: *opc = &&op_decA; break; + case OP_LEFT: *opc = &&op_leftA; break; + case OP_RIGHT: *opc = &&op_rightA; break; + case OP_WAIT: *opc = &&op_waitA; break; + case OP_LOOP1: *opc = &&op_loop1A; break; + case OP_LOOP2: *opc = &&op_loop2A; break; + case OP_REP1: *opc = &&op_rep1A; break; + case OP_REP2: + if (op->inner != -1) *opc = &&op_irep2A; + else *opc = &&op_rep2A; + break; + case OP_INNER1: *opc = &&op_inner1A; break; + case OP_INNER2: *opc = &&op_inner2A; break; + } + } + + opcA[opsA->len] = &&op_doneA; + + for (int at = 0; at < opsB->len; at++) + { + struct op *op = &oplB[at]; + void **opc = &opcB[at]; + switch (op->type) + { + case OP_INC: *opc = &&op_incB; break; + case OP_DEC: *opc = &&op_decB; break; + case OP_LEFT: *opc = &&op_leftB; break; + case OP_RIGHT: *opc = &&op_rightB; break; + case OP_WAIT: *opc = &&op_waitB; break; + case OP_LOOP1: *opc = &&op_loop1B; break; + case OP_LOOP2: *opc = &&op_loop2B; break; + case OP_REP1: *opc = &&op_rep1B; break; + case OP_REP2: + if (op->inner != -1) *opc = &&op_irep2B; + else *opc = &&op_rep2B; + break; + case OP_INNER1: *opc = &&op_inner1B; break; + case OP_INNER2: *opc = &&op_inner2B; break; + } + } + + opcB[opsB->len] = &&nextcycle; /* a slight shortcut */ + + /* state-holding variables */ + + static int repStackA[MAXNEST], repStackB[MAXNEST]; + + int ipA = 0, ipB = 0; + unsigned char *ptrA = 0, *ptrB = 0, bcache = 0; + int repA = 0, repB = 0, *repSA = 0, *repSB = 0; + int deathsA = 0, deathsB = 0; + int cycles = 0; + int score = 0; + + /* execute with all tape lenghts and both relative polarities */ + +#define EXECUTE_ALL(sym, pol) \ + ret = &&sym; \ + for (tapesize = MINTAPE; tapesize <= MAXTAPE; tapesize++) \ + { \ + ipA = 0, ipB = 0; \ + \ + memset(tape, 0, tapesize); \ + ptrA = &tape[0], ptrB = &tape[tapesize-1]; \ + *ptrA = 128, *ptrB = 128; bcache = 128; \ + \ + repSA = repStackA, repSB = repStackB; \ + deathsA = 0, deathsB = 0; \ + \ + cycles = MAXCYCLES; \ + \ + score = 0; \ + goto *opcA[0]; \ + sym: \ + scores[pol][tapesize] = score; \ + } + + void *ret; + int tapesize = 0; + + EXECUTE_ALL(done_normal, 0); + + for (int at = 0; at < opsB->len; at++) + { + enum optype op = oplB[at].type; + if (op == OP_INC) opcB[at] = &&op_decB; + else if (op == OP_DEC) opcB[at] = &&op_incB; + } + + EXECUTE_ALL(done_flipped, 1); + + free(opcA); + free(opcB); + return; + + /* actual core */ + +#define NEXTA ipA++; goto *opcB[ipB] +#define NEXTB ipB++; goto nextcycle + +/* #define TRACE 1 */ + +nextcycle: + cycles--; + + if (!tape[0]) deathsA++; else if (deathsA == 1) deathsA = 0; + if (!tape[tapesize-1]) deathsB++; else if (deathsB == 1) deathsB = 0; + +#ifdef TRACE + printf("%6d: ", MAXCYCLES-cycles); + for (int i = 0; i < tapesize; i++) + printf("%c%02x ", + (ptrA - tape) == i + ? ((ptrB - tape) == i ? 'X' : 'A') + : ((ptrB - tape) == i ? 'B' : ' '), + tape[i]); + printf(" dA %d dB %d ipA %d ipB %d\n", deathsA, deathsB, ipA, ipB); +#endif + + if (deathsA >= 2 && deathsB >= 2) + goto *ret; + else if (deathsA >= 2) + { + score++; + goto *ret; + } + else if (deathsB >= 2) + { + score--; + goto *ret; + } + + if (!cycles) + goto *ret; + + bcache = *ptrB; + goto *opcA[ipA]; + +fallA: + deathsA = 2; + goto *opcB[ipB]; + +fallB: + if (!tape[0]) deathsA++; + if (deathsA >= 2) + goto *ret; + score--; + goto *ret; + +op_incA: + (*ptrA)++; + NEXTA; +op_incB: + (*ptrB)++; + NEXTB; +op_decA: + (*ptrA)--; + NEXTA; +op_decB: + (*ptrB)--; + NEXTB; + +op_leftA: + ptrA--; + if (ptrA < tape) goto fallA; + NEXTA; +op_leftB: + ptrB++; + if (ptrB >= &tape[tapesize]) goto fallB; + NEXTB; +op_rightA: + ptrA++; + if (ptrA >= &tape[tapesize]) goto fallA; + NEXTA; +op_rightB: + ptrB--; + if (ptrB < tape) goto fallB; + NEXTB; + +op_waitA: + NEXTA; +op_waitB: + NEXTB; + +op_loop1A: + if (!*ptrA) ipA = oplA[ipA].match; + NEXTA; +op_loop1B: + if (!bcache) ipB = oplB[ipB].match; + NEXTB; +op_loop2A: + if (*ptrA) ipA = oplA[ipA].match; + NEXTA; +op_loop2B: + if (bcache) ipB = oplB[ipB].match; + NEXTB; + + /* simple (..) repeats with no corresponding {..} block */ + +op_rep1A: + *repSA++ = repA; repA = oplA[ipA].count; + goto *opcA[++ipA]; +op_rep1B: + *repSB++ = repB; repB = oplB[ipB].count; + goto *opcB[++ipB]; + +op_rep2A: + if (--repA) ipA = oplA[ipA].match; + else repA = *--repSA; + goto *opcA[++ipA]; +op_rep2B: + if (--repB) ipB = oplB[ipB].match; + else repB = *--repSB; + goto *opcB[++ipB]; + + /* complex (..{ and }..) repeats; use .inner for target, count in different dirs */ + +op_inner1A: + if (--repA) ipA = oplA[ipA].inner; + else repA = *--repSA; + goto *opcA[++ipA]; +op_inner1B: + if (--repB) ipB = oplB[ipB].inner; + else repB = *--repSB; + goto *opcB[++ipB]; + +op_inner2A: + *repSA++ = repA; repA = 1; + goto *opcA[++ipA]; +op_inner2B: + *repSB++ = repB; repB = 1; + goto *opcB[++ipB]; + +op_irep2A: + if (++repA <= oplA[ipA].count) ipA = oplA[ipA].inner; + else repA = *--repSA; + goto *opcA[++ipA]; +op_irep2B: + if (++repB <= oplB[ipB].count) ipB = oplB[ipB].inner; + else repB = *--repSB; + goto *opcB[++ipB]; + +op_doneA: + goto *opcB[ipB]; +} + +/* parsing and preprocessing, impl */ + +static int readrepc(unsigned char *buf, ssize_t bsize, int *bat, int fd) +{ + /* read and parse a () count */ + + int at = *bat; + + int nextc(void) + { + if (at < bsize) + return buf[at++]; + + unsigned char c; + ssize_t t = read(fd, &c, 1); + if (t < 0) die("read error"); + if (t == 0) return -1; + return c; + } + + int c = 0, ch; + + ch = nextc(); + if (ch != '*' && ch != '%') + { + /* treat garbage as ()*0 in case it's inside a comment */ + buf[--at] = ch; + *bat = at-1; + return 0; + } + + int neg = -1; + + while (1) + { + ch = nextc(); + if (ch == '-' && neg < 0) + { + neg = 1; + continue; + } + if (ch < '0' || ch > '9') + break; + if (neg < 0) neg = 0; + + c = c*10 + (ch - '0'); + if (c > MAXCYCLES) + { + c = MAXCYCLES; + ch = 0; + break; + } + } + + buf[--at] = ch; + *bat = at-1; + + return neg > 0 ? -c : c; +} + +static struct oplist *readops(int fd) +{ + /* lexical tokenizing into initial oplist */ + + static unsigned char buf[65536]; + struct oplist *ops = opl_new(); + + while (1) + { + ssize_t got = read(fd, buf, sizeof buf); + if (got < 0) die("read error"); + if (got == 0) break; + + for (int i = 0; i < got; i++) + { + int c; + + switch (buf[i]) + { + case '+': opl_append(ops, OP_INC); break; + case '-': opl_append(ops, OP_DEC); break; + case '<': opl_append(ops, OP_LEFT); break; + case '>': opl_append(ops, OP_RIGHT); break; + case '.': opl_append(ops, OP_WAIT); break; + case '[': opl_append(ops, OP_LOOP1); break; + case ']': opl_append(ops, OP_LOOP2); break; + case '(': opl_append(ops, OP_REP1); break; + case ')': + /* need to extract the count */ + i++; + c = readrepc(buf, got, &i, fd); + if (c < 0) c = MAXCYCLES; + opl_append(ops, OP_REP2); + ops->ops[ops->len-1].count = c; + break; + case '{': opl_append(ops, OP_INNER1); break; + case '}': opl_append(ops, OP_INNER2); break; + } + } + } + + return ops; +} + +static void matchrep(struct oplist *ops) +{ + /* match (..) pairs and inner {..} blocks */ + + int stack[MAXNEST], mstack[MAXNEST]; + int depth = 0, mdepth = 0; + + for (int at = 0; at < ops->len; at++) + { + struct op *op = &ops->ops[at]; + + switch (op->type) /* in order of occurrence */ + { + case OP_REP1: + if (depth == MAXNEST) fail("maximum () nesting depth exceeded"); + stack[depth++] = at; + mstack[mdepth++] = at; + op->match = -1; + op->inner = -1; + break; + + case OP_INNER1: + if (depth == MAXNEST) fail("maximum {} nesting depth exceeded"); + if (!mdepth) fail("encountered { without matching ("); + stack[depth++] = at; + op->match = -1; + op->inner = mstack[--mdepth]; + if (ops->ops[op->inner].inner != -1) fail("same ( has multiple matching {s"); + ops->ops[op->inner].inner = at; + break; + + case OP_INNER2: + if (!depth) fail("terminating } without a matching {"); + op->match = stack[--depth]; + if (ops->ops[op->match].type != OP_INNER1) fail("mismatching }"); + op->inner = -1; + ops->ops[op->match].match = at; + mdepth++; + break; + + case OP_REP2: + if (!depth) fail("terminating ) without a matching ("); + op->match = stack[--depth]; + if (ops->ops[op->match].type != OP_REP1) fail("mismatching )"); + op->inner = (ops->ops[op->match].inner != -1 ? ops->ops[ops->ops[op->match].inner].match : -1); + ops->ops[op->match].match = at; + ops->ops[op->match].count = op->count; + if (op->inner != -1) + { + ops->ops[op->inner].inner = at; + ops->ops[op->inner].count = op->count; + ops->ops[ops->ops[op->inner].match].count = op->count; + } + mdepth--; + break; + + default: + /* do nothing */ + break; + } + } + + if (depth != 0) + fail("starting ( without a matching )"); +} + +static void cleanrep(struct oplist *ops) +{ + /* clean out (...)*0 and ()*N */ + + for (int at = 0; at < ops->len; at++) + { + struct op *op = &ops->ops[at]; + if (op->type == OP_REP1 && op->count == 0) + { + opl_del(ops, at, op->match+1); + at--; + } + } + + /* TODO: clean ()*N with a multipass thing */ +} + +static void matchloop(struct oplist *ops) +{ + /* match [..] pairs */ + + int stack[MAXNEST], nstack[MAXNEST]; + int depth = 0, ndepth = 0; + + for (int at = 0; at < ops->len; at++) + { + struct op *op = &ops->ops[at]; + + switch (op->type) + { + case OP_LOOP1: + if (depth == MAXNEST) fail("maximum [] nesting depth exceeded"); + stack[depth] = at; + nstack[depth] = ndepth; + op->match = -1; + depth++; + break; + + case OP_REP1: + case OP_INNER2: + ndepth++; + break; + + case OP_INNER1: + case OP_REP2: + if (!ndepth) fail("impossible: negative ({..}) nesting depth in [..] matching"); + ndepth--; + break; + + case OP_LOOP2: + if (!depth) fail("terminating ] without a matching ["); + depth--; + if (ndepth != nstack[depth]) fail("[..] crossing across ({..}) levels"); + op->match = stack[depth]; + ops->ops[op->match].match = at; + break; + + default: + /* do nothing */ + break; + } + } + + if (depth != 0) + fail("starting [ without a matching ]"); +} + +static struct oplist *parse(int fd) +{ + struct oplist *ops = readops(fd); + + /* handle (...) constructions first */ + + matchrep(ops); + cleanrep(ops); + + /* handle [...] constructions now that rep/inner levels are known */ + + matchloop(ops); + + return ops; +} + +/* oplist handling, impl */ + +static struct oplist *opl_new(void) +{ + struct oplist *o = smalloc(sizeof *o); + + o->len = 0; + o->size = 32; + o->ops = smalloc(o->size * sizeof *o->ops); + + return o; +} + +static void opl_free(struct oplist *o) +{ + free(o->ops); + free(o); +} + +static void opl_append(struct oplist *o, enum optype type) +{ + if (o->len == o->size) + { + o->size += o->size >> 1; + o->ops = srealloc(o->ops, o->size * sizeof *o->ops); + } + + o->ops[o->len].type = type; + o->ops[o->len].match = -1; + o->ops[o->len].inner = -1; + o->ops[o->len].count = -1; + o->len++; +} + +static void opl_del(struct oplist *o, int start, int end) +{ + int d = end - start; + if (!d) + return; + + if (end == o->len) + { + o->len = start; + return; + } + + memmove(&o->ops[start], &o->ops[end], (o->len - end) * sizeof *o->ops); + o->len -= d; + + for (int at = 0; at < o->len; at++) + { + struct op *op = &o->ops[at]; + if (op->match >= start && op->match < end) die("opl_del: dangling ->match"); + if (op->inner >= start && op->inner < end) die("opl_del: dangling ->inner"); + if (op->match >= end) op->match -= d; + if (op->inner >= end) op->inner -= d; + } +} + +/* generic helpers, impl */ + +static void die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + + exit(1); +} + +static void fail(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(fail_msg, sizeof fail_msg, fmt, ap); + va_end(ap); + + longjmp(fail_buf, 1); +} + +static void *smalloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr) die("out of memory"); + return ptr; +} + +static void *srealloc(void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + if (!ptr) die("out of memory"); + return ptr; +} + +static int sopen(const char *fname) +{ + int fd = open(fname, O_RDONLY); + if (fd == -1) + die("open failed: %s", fname); + return fd; +}