aboutsummaryrefslogtreecommitdiff
path: root/brainfuck.c
diff options
context:
space:
mode:
authorNebula Moe <nebula_moe@outlook.com>2023-07-24 19:47:03 +0800
committerNebula Moe <nebula_moe@outlook.com>2023-07-28 22:30:22 +0800
commit2161797b19b068622a692628f352c78254d8a354 (patch)
tree146272037ff9a79dbd3903dd9471a7713469419d /brainfuck.c
Diffstat (limited to 'brainfuck.c')
-rw-r--r--brainfuck.c212
1 files changed, 212 insertions, 0 deletions
diff --git a/brainfuck.c b/brainfuck.c
new file mode 100644
index 0000000..1579231
--- /dev/null
+++ b/brainfuck.c
@@ -0,0 +1,212 @@
+// Copyright (C) 2023 Dzshy <dzshy@outlook.com>. All Rights Reserved.
+// Licensed under Non-Profit Open Software License ("Non-Profit OSL") 3.0.
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "crc32.h"
+#include "htable.h"
+
+#define LOGGER(s, ...) \
+ do { \
+ fprintf(stderr, s "\n", ##__VA_ARGS__); \
+ } while (0)
+
+uint8_t *pbuf;
+uint8_t *nbuf;
+long nsz, psz;
+static const int INIT_SZ = 128;
+
+typedef struct {
+ long key;
+ long val;
+} JmpTblEntry;
+
+static bool jmptbl_eq(void *x, void *y)
+{
+ long *a = x, *b = y;
+ return *a == *b;
+}
+
+static uint32_t jmptbl_hash(void *k) { return crc32(0, k, sizeof(long)); }
+
+HTable jmptbl;
+
+struct listnode {
+ struct listnode *next;
+ long val;
+};
+typedef struct {
+ struct listnode *head;
+} Stack;
+
+void stack_push(Stack *s, long val)
+{
+ struct listnode *n = malloc(sizeof(struct listnode));
+ n->next = s->head;
+ n->val = val;
+ s->head = n;
+}
+
+long *stack_top(Stack *s)
+{
+ if (s->head == NULL)
+ return NULL;
+ return &(s->head->val);
+}
+
+void stack_pop(Stack *s)
+{
+ if (s->head == NULL)
+ return;
+ struct listnode *next = s->head->next;
+ free(s->head);
+ s->head = next;
+}
+
+void buildjmptable(char *buf, long len)
+{
+ Stack s = {0};
+ htable_init(&jmptbl, sizeof(JmpTblEntry), -1, jmptbl_hash, jmptbl_eq);
+ for (long i = 0; i < len; i++) {
+ if (buf[i] == '[') {
+ stack_push(&s, i);
+ } else if (buf[i] == ']') {
+ long j = *stack_top(&s);
+ stack_pop(&s);
+ JmpTblEntry e1 = {i, j};
+ JmpTblEntry e2 = {j, i};
+ htable_insert(&jmptbl, &e1);
+ htable_insert(&jmptbl, &e2);
+ }
+ }
+}
+
+static long tbllookup(long pos)
+{
+ JmpTblEntry *iter = htable_find(&jmptbl, &pos);
+ return iter->val;
+}
+
+void ensurespc_impl(uint8_t **buf, long *cursz, long ptr)
+{
+ if (ptr > *cursz) {
+ *buf = realloc(*buf, ptr * 2);
+ memset((*buf) + (*cursz), 0, 2 * ptr - (*cursz));
+ *cursz = ptr * 2;
+ }
+}
+
+void ensurespc(long ptr)
+{
+ if (ptr >= 0) {
+ ensurespc_impl(&pbuf, &psz, ptr);
+ } else {
+ ensurespc_impl(&nbuf, &nsz, -ptr);
+ }
+}
+
+uint8_t getdata(long ptr)
+{
+ ensurespc(ptr);
+ if (ptr >= 0)
+ return pbuf[ptr];
+ else
+ return nbuf[-ptr];
+}
+
+void setdata(long ptr, uint8_t val)
+{
+ ensurespc(ptr);
+ if (ptr >= 0)
+ pbuf[ptr] = val;
+ else
+ nbuf[-ptr] = val;
+}
+
+void interp(char *prog, long prog_sz)
+{
+ long prog_p = 0;
+ long data_p = 0;
+ nbuf = malloc(INIT_SZ);
+ pbuf = malloc(INIT_SZ);
+ nsz = INIT_SZ;
+ psz = INIT_SZ;
+ memset(nbuf, 0, INIT_SZ);
+ memset(pbuf, 0, INIT_SZ);
+
+ while (prog_p < prog_sz) {
+ switch (prog[prog_p]) {
+ case '>':
+ data_p++;
+ break;
+ case '<':
+ data_p--;
+ break;
+ case '+':
+ setdata(data_p, getdata(data_p) + 1);
+ break;
+ case '-':
+ setdata(data_p, getdata(data_p) - 1);
+ break;
+ case '.':
+ putchar(getdata(data_p));
+ break;
+ case ',':
+ setdata(data_p, getchar());
+ break;
+ case '[':
+ if (getdata(data_p) == 0) {
+ prog_p = tbllookup(prog_p);
+ }
+ break;
+ case ']':
+ if (getdata(data_p) != 0) {
+ prog_p = tbllookup(prog_p);
+ }
+ break;
+ default:
+ break;
+ }
+ prog_p++;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 2) {
+ LOGGER("invalid args");
+ return EXIT_FAILURE;
+ }
+ int fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ return EXIT_FAILURE;
+ }
+ struct stat fst;
+ int ret = fstat(fd, &fst);
+ if (ret != 0) {
+ LOGGER("error: failed to get file stat");
+ return EXIT_FAILURE;
+ }
+ long filesz = fst.st_size;
+ char *prog = malloc(filesz);
+ FILE *fp = fdopen(fd, "r");
+ if (fp == NULL) {
+ LOGGER("error opening file");
+ exit(-1);
+ }
+ size_t read = fread(prog, 1, filesz, fp);
+ if (read < filesz) {
+ LOGGER("erro reading");
+ exit(-1);
+ }
+ buildjmptable(prog, filesz);
+ interp(prog, filesz);
+ return EXIT_SUCCESS;
+}