aboutsummaryrefslogtreecommitdiff
path: root/interpreters/bf-optimizing.c
diff options
context:
space:
mode:
Diffstat (limited to 'interpreters/bf-optimizing.c')
-rw-r--r--interpreters/bf-optimizing.c213
1 files changed, 213 insertions, 0 deletions
diff --git a/interpreters/bf-optimizing.c b/interpreters/bf-optimizing.c
new file mode 100644
index 0000000..b37c0f2
--- /dev/null
+++ b/interpreters/bf-optimizing.c
@@ -0,0 +1,213 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <errno.h>
+
+#define exit_fatal(...) \
+ do { \
+ fprintf (stderr, "fatal: " __VA_ARGS__); \
+ exit (EXIT_FAILURE); \
+ } while (0)
+
+// --- Safe memory management --------------------------------------------------
+
+static void *
+xcalloc (size_t m, size_t n)
+{
+ void *p = calloc (m, n);
+ if (!p)
+ exit_fatal ("calloc: %s\n", strerror (errno));
+ return p;
+}
+
+static void *
+xrealloc (void *o, size_t n)
+{
+ void *p = realloc (o, n);
+ if (!p && n)
+ exit_fatal ("realloc: %s\n", strerror (errno));
+ return p;
+}
+
+// --- Dynamically allocated strings -------------------------------------------
+
+struct str
+{
+ char *str; ///< String data, null terminated
+ size_t alloc; ///< How many bytes are allocated
+ size_t len; ///< How long the string actually is
+};
+
+static void
+str_init (struct str *self)
+{
+ self->len = 0;
+ self->str = xcalloc (1, (self->alloc = 16));
+}
+
+static void
+str_ensure_space (struct str *self, size_t n)
+{
+ // We allocate at least one more byte for the terminating null character
+ size_t new_alloc = self->alloc;
+ while (new_alloc <= self->len + n)
+ new_alloc <<= 1;
+ if (new_alloc != self->alloc)
+ self->str = xrealloc (self->str, (self->alloc = new_alloc));
+}
+
+static void
+str_append_data (struct str *self, const void *data, size_t n)
+{
+ str_ensure_space (self, n);
+ memcpy (self->str + self->len, data, n);
+ self->str[self->len += n] = '\0';
+}
+
+static void
+str_append_c (struct str *self, char c)
+{
+ str_append_data (self, &c, 1);
+}
+
+// --- Main --------------------------------------------------------------------
+
+struct str program; ///< Raw program
+struct str data; ///< Data tape
+
+enum command { RIGHT, LEFT, INC, DEC, SET, IN, OUT, BEGIN, END };
+bool grouped[] = { 1, 1, 1, 1, 1, 0, 0, 0, 0 };
+struct instruction { enum command cmd; size_t arg; };
+
+int
+main (int argc, char *argv[])
+{
+ (void) argc; str_init (&program);
+ (void) argv; str_init (&data);
+
+ int c;
+ while ((c = fgetc (stdin)) != EOF)
+ str_append_c (&program, c);
+ if (ferror (stdin))
+ exit_fatal ("can't read program\n");
+
+ FILE *input = fopen ("/dev/tty", "rb");
+ if (!input)
+ exit_fatal ("can't open terminal for reading\n");
+
+// - - Decode and group - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ struct instruction *parsed = xcalloc (sizeof *parsed, program.len);
+ size_t parsed_len = 0;
+
+ for (size_t i = 0; i < program.len; i++)
+ {
+ enum command cmd;
+ switch (program.str[i])
+ {
+ case '>': cmd = RIGHT; break;
+ case '<': cmd = LEFT; break;
+ case '+': cmd = INC; break;
+ case '-': cmd = DEC; break;
+ case '.': cmd = OUT; break;
+ case ',': cmd = IN; break;
+ case '[': cmd = BEGIN; break;
+ case ']': cmd = END; break;
+ default: continue;
+ }
+
+ if (!parsed_len || !grouped[cmd] || parsed[parsed_len - 1].cmd != cmd)
+ parsed_len++;
+
+ parsed[parsed_len - 1].cmd = cmd;
+ parsed[parsed_len - 1].arg++;
+ }
+
+// - - Simple optimization pass - - - - - - - - - - - - - - - - - - - - - - - -
+
+ size_t in = 0, out = 0;
+ for (; in < parsed_len; in++, out++)
+ {
+ if (in + 2 < parsed_len
+ && parsed[in ].cmd == BEGIN
+ && parsed[in + 1].cmd == DEC && parsed[in + 1].arg == 1
+ && parsed[in + 2].cmd == END)
+ {
+ parsed[out].cmd = SET;
+ parsed[out].arg = 0;
+ in += 2;
+ }
+ else if (out && parsed[out - 1].cmd == SET && parsed[in].cmd == INC)
+ parsed[--out].arg += parsed[in].arg;
+ else if (out != in)
+ parsed[out] = parsed[in];
+ }
+
+ parsed_len = out;
+
+// - - Loop pairing - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ size_t nesting = 0;
+ size_t *stack = xcalloc (sizeof *stack, parsed_len);
+ for (size_t i = 0; i < parsed_len; i++)
+ {
+ switch (parsed[i].cmd)
+ {
+ case BEGIN:
+ stack[nesting++] = i;
+ break;
+ case END:
+ assert (nesting > 0);
+
+ --nesting;
+ parsed[stack[nesting]].arg = i;
+ parsed[i].arg = stack[nesting];
+ default:
+ break;
+ }
+ }
+ assert (nesting == 0);
+
+// - - Runtime - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ size_t dataptr = 0;
+ str_append_c (&data, 0);
+
+ for (size_t i = 0; i < parsed_len; i++)
+ {
+ size_t arg = parsed[i].arg;
+ switch (parsed[i].cmd)
+ {
+ case RIGHT:
+ assert (SIZE_MAX - dataptr > arg);
+ dataptr += arg;
+
+ while (dataptr >= data.len)
+ str_append_c (&data, 0);
+ break;
+ case LEFT:
+ assert (dataptr >= arg);
+ dataptr -= arg;
+ break;
+
+ case INC: data.str[dataptr] += arg; break;
+ case DEC: data.str[dataptr] -= arg; break;
+ case SET: data.str[dataptr] = arg; break;
+
+ case OUT:
+ fputc (data.str[dataptr], stdout);
+ break;
+ case IN:
+ data.str[dataptr] = c = fgetc (input);
+ assert (c != EOF);
+ break;
+
+ case BEGIN: if (!data.str[dataptr]) i = arg; break;
+ case END: if ( data.str[dataptr]) i = arg; break;
+ }
+ }
+ return 0;
+}