diff options
| author | Nebula Moe <nebula_moe@outlook.com> | 2023-07-24 19:47:03 +0800 |
|---|---|---|
| committer | Nebula Moe <nebula_moe@outlook.com> | 2023-07-28 22:30:22 +0800 |
| commit | 2161797b19b068622a692628f352c78254d8a354 (patch) | |
| tree | 146272037ff9a79dbd3903dd9471a7713469419d /brainfuck.c | |
Diffstat (limited to 'brainfuck.c')
| -rw-r--r-- | brainfuck.c | 212 |
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; +} |
