diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | Makefile | 26 | ||||
| -rwxr-xr-x | bfc.sh | 24 | ||||
| -rw-r--r-- | src/all.h | 65 | ||||
| -rw-r--r-- | src/bfc.c | 30 | ||||
| -rw-r--r-- | src/codegen.c | 148 | ||||
| -rw-r--r-- | src/ivec.c | 119 | ||||
| -rw-r--r-- | src/ivec.h | 18 | ||||
| -rw-r--r-- | src/lexer.c | 72 | ||||
| -rw-r--r-- | src/node_vec.c | 114 | ||||
| -rw-r--r-- | src/node_vec.h | 26 | ||||
| -rw-r--r-- | src/parser.c | 152 | ||||
| -rw-r--r-- | src/str.c | 119 | ||||
| -rw-r--r-- | src/str.h | 22 |
14 files changed, 939 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ecde1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/ +bf.out +compile_commands.json +test.bf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8c7470c --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CFLAGS := -Isrc -MMD -MP -Wall -Wextra +LDFLAGS := + +TARGET = build/bfc + +TARGET_SRC := $(shell find src -name "*.c") +TARGET_OBJ := $(patsubst src/%.c, build/%.o, $(TARGET_SRC)) +TARGET_DEP := $(patsubst build/%.o, build/%.d, $(TARGET_OBJ)) + +all: build $(TARGET) + +build: + mkdir -p $(sort $(dir $(TARGET_OBJ))) + +$(TARGET): $(TARGET_OBJ) + $(CC) $(LDFLAGS) $(TARGET_OBJ) -o $@ + +-include $(TARGET_DEP) + +build/%.o: src/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -rf build + +.PHONY: build clean @@ -0,0 +1,24 @@ +#!/usr/bin/env sh + +set -e + +BFC="./build/bfc" +SRC="$1" + +if [ -z "$SRC" ]; then + echo "usage: $0 <file.bf>" >&2 + exit 1 +fi + +# silently build compiler if missing +if [ ! -f "$BFC" ]; then + make -s || { + echo "build failed" >&2 + exit 1 + } +fi + +# compile pipeline +OUT=$(cat "$SRC" | "$BFC") + +echo "$OUT" | gcc -o ./bf.out -x c - diff --git a/src/all.h b/src/all.h new file mode 100644 index 0000000..c5a983c --- /dev/null +++ b/src/all.h @@ -0,0 +1,65 @@ +#pragma once +#include "ivec.h" +#include "node_vec.h" +#include "str.h" +#include <stdbool.h> +#include <stddef.h> + +// Lexer + +typedef enum { + LEX_PINC, + LEX_PDEC, + LEX_VINC, + LEX_VDEC, + LEX_OUT, + LEX_IN, + LEX_LB, + LEX_RB, + LEX_EOF +} lex_tok_e; + +typedef struct { + str *src; + size_t pos; + ivec tokens; +} lexer_t; + +lexer_t lex_init(str *src); +void lex_run(lexer_t *lx); +void lex_free(lexer_t *lx); + +// Parser/AST + +typedef struct ast_node ast_node_t; + +typedef enum { + AST_INC, + AST_DEC, + AST_PTR_INC, + AST_PTR_DEC, + AST_OUT, + AST_IN, + AST_LOOP +} ast_type_e; + +struct ast_node { + ast_type_e type; + int val; + + node_vec children; // vec<ast_node_t*> + ast_node_t *next; +}; + +typedef struct { + lexer_t *lx; + size_t pos; +} parser_t; + +parser_t parser_init(lexer_t *lx); +ast_node_t *parse_program(parser_t *p); +void ast_free(ast_node_t *node); + +// codegen + +str gen_code(ast_node_t *ast); diff --git a/src/bfc.c b/src/bfc.c new file mode 100644 index 0000000..2e369cf --- /dev/null +++ b/src/bfc.c @@ -0,0 +1,30 @@ +#include "all.h" +#include "str.h" +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +int main(void) { + str input = {0}; + str_append_fread(&input, stdin); + + lexer_t lexer = lex_init(&input); + lex_run(&lexer); + + parser_t p = parser_init(&lexer); + + ast_node_t *ast = parse_program(&p); + + // lex_free(&lexer); + // parser_free(&p); + + str c_code = gen_code(ast); + + ast_free(ast); + + printf("%s", c_code.data); + + free_str(&input); + return 0; +} diff --git a/src/codegen.c b/src/codegen.c new file mode 100644 index 0000000..e4972db --- /dev/null +++ b/src/codegen.c @@ -0,0 +1,148 @@ +#include "all.h" +#include "str.h" + +static void emit_includes(str *out) { + str_append(out, "#include <stdio.h>\n"); + str_append(out, "#include <stdlib.h>\n"); + str_append(out, "#include <string.h>\n\n"); +} + +static void emit_runtime(str *out) { + str_append( + out, + "typedef struct {\n" + " unsigned char *data;\n" + " size_t size;\n" + "} tape_t;\n\n" + + "static void tape_init(tape_t *t, size_t initial) {\n" + " t->data = calloc(initial, 1);\n" + " t->size = initial;\n" + "}\n\n" + + "static void tape_grow_right(tape_t *t, size_t needed) {\n" + " size_t new_size = t->size;\n" + " while (new_size <= needed) new_size *= 2;\n" + " unsigned char *new_data = calloc(new_size, 1);\n" + " memcpy(new_data, t->data, t->size);\n" + " free(t->data);\n" + " t->data = new_data;\n" + " t->size = new_size;\n" + "}\n\n" + + "static void tape_grow_left(tape_t *t, size_t add, size_t *ptr) {\n" + " size_t new_size = t->size + add;\n" + " unsigned char *new_data = calloc(new_size, 1);\n" + " memcpy(new_data + add, t->data, t->size);\n" + " free(t->data);\n" + " t->data = new_data;\n" + " t->size = new_size;\n" + " *ptr += add;\n" + "}\n\n" + + "static void tape_move_right(tape_t *t, size_t *ptr, size_t amount) {\n" + " *ptr += amount;\n" + " if (*ptr >= t->size)\n" + " tape_grow_right(t, *ptr);\n" + "}\n\n" + + "static void tape_move_left(tape_t *t, size_t *ptr, size_t amount) {\n" + " if (amount > *ptr) {\n" + " size_t need = amount - *ptr;\n" + " size_t add = t->size;\n" + " while (add <= need)\n" + " add *= 2;\n" + " tape_grow_left(t, add, ptr);\n" + " }\n" + " *ptr -= amount;\n" + "}\n\n"); +} + +static void emit_prelude(str *out) { + str_append(out, "int main(void)\n" + "{\n" + " tape_t tape;\n" + " tape_init(&tape, 32);\n" + " size_t ptr = tape.size / 2;\n\n"); +} + +static void emit_epilogue(str *out) { + str_append(out, " free(tape.data);\n" + " return 0;\n" + "}\n"); +} + +static void emit_indent(str *out, int indent) { + for (int i = 0; i < indent; i++) + str_append(out, " "); +} + +void emit_node(str *out, ast_node_t *node, int indent) { + for (; node; node = node->next) { + switch (node->type) { + case AST_INC: + emit_indent(out, indent); + str_append(out, "tape.data[ptr] += "); + str_appendf(out, "%d;\n", node->val); + break; + + case AST_DEC: + emit_indent(out, indent); + str_append(out, "tape.data[ptr] -= "); + str_appendf(out, "%d;\n", node->val); + break; + + case AST_PTR_INC: + emit_indent(out, indent); + str_append(out, "tape_move_right(&tape, &ptr, "); + str_appendf(out, "%d", node->val); + str_append(out, ");\n"); + break; + + case AST_PTR_DEC: + emit_indent(out, indent); + str_append(out, "tape_move_left(&tape, &ptr, "); + str_appendf(out, "%d", node->val); + str_append(out, ");\n"); + break; + + case AST_OUT: + emit_indent(out, indent); + str_append(out, "putchar((int)tape.data[ptr]);\n"); + break; + + case AST_IN: + emit_indent(out, indent); + str_append(out, + "{ int ch = getchar();\n" + " tape.data[ptr] = (ch == EOF) ? 0 : (unsigned char)ch; }\n"); + break; + + case AST_LOOP: + emit_indent(out, indent); + str_append(out, "while (tape.data[ptr]) {\n"); + + for (int i = 0; i < node->children.len; i++) { + emit_node(out, node->children._data[i], indent + 1); + } + + emit_indent(out, indent); + str_append(out, "}\n"); + break; + } + } +} + +str gen_code(ast_node_t *ast) { + str out = {0}; + + emit_includes(&out); + emit_runtime(&out); + emit_prelude(&out); + + emit_node(&out, ast, 1); + + emit_epilogue(&out); + + return out; +} diff --git a/src/ivec.c b/src/ivec.c new file mode 100644 index 0000000..9c0dd95 --- /dev/null +++ b/src/ivec.c @@ -0,0 +1,119 @@ +#include "ivec.h" +#include <stdio.h> +#include <stdlib.h> + +void iv_free(ivec *v) { + free(v->_data); + v->_data = NULL; + v->_cap = 0; + v->len = 0; +} + +static void _iv_ensure_init(ivec *v) { + if (v->_data) + return; + + v->_cap = 16; + v->len = 0; + + v->_data = malloc(v->_cap * sizeof(long long)); + if (!v->_data) { + perror("_v_ensure_init.malloc"); + abort(); + } +} + +static void _iv_ensure_capacity(ivec *v) { + _iv_ensure_init(v); + + if (v->len >= v->_cap) { + v->_cap = (v->_cap == 0) ? 16 : v->_cap * 2; + + long long *tmp = realloc(v->_data, v->_cap * sizeof(long long)); + if (!tmp) { + perror("_v_ensure_capacity.realloc"); + abort(); + } + + v->_data = tmp; + } +} + +size_t iv_push(ivec *v, long long data) { + _iv_ensure_capacity(v); + + v->_data[v->len++] = data; + return v->len - 1; +} + +// getter +long long iv_get(ivec *v, size_t idx) { + _iv_ensure_init(v); + + if (idx < v->len) { + return v->_data[idx]; + } + + return 0; +} + +// O(n) shift delete +long long iv_rem_shift(ivec *v, size_t idx) { + _iv_ensure_init(v); + + if (idx >= v->len) + return 0; + + int old = v->_data[idx]; + + for (size_t j = idx; j + 1 < v->len; j++) { + v->_data[j] = v->_data[j + 1]; + } + + v->len--; + return old; +} + +// O(1) swap delete +long long iv_rem(ivec *v, size_t idx) { + _iv_ensure_init(v); + + if (idx >= v->len) + return 0; + + int old = v->_data[idx]; + v->_data[idx] = v->_data[--v->len]; + return old; +} + +// search +long long iv_has(ivec *v, long long x, + int (*comp)(const long long, const long long)) { + _iv_ensure_init(v); + + if (!comp) + return 0; + + for (size_t i = 0; i < v->len; i++) { + if (comp(v->_data[i], x) == 0) { + return v->_data[i]; + } + } + + return 0; +} + +void iv_shrink(ivec *v) { + _iv_ensure_init(v); + + size_t new_cap = v->len ? v->len : 1; + + long long *tmp = realloc(v->_data, new_cap * sizeof(int)); // FIX + if (!tmp) { + perror("v_shrink.realloc"); + abort(); + } + + v->_cap = new_cap; + v->_data = tmp; +} diff --git a/src/ivec.h b/src/ivec.h new file mode 100644 index 0000000..8526171 --- /dev/null +++ b/src/ivec.h @@ -0,0 +1,18 @@ +#pragma once + +#include <stddef.h> + +typedef struct { + long long *_data; + size_t _cap; + size_t len; +} ivec; + +void iv_free(ivec *v); +size_t iv_push(ivec *v, long long data); +long long iv_get(ivec *v, size_t idx); +long long iv_rem_shift(ivec *v, size_t idx); +long long iv_rem(ivec *v, size_t idx); +long long iv_has(ivec *v, long long x, + int (*comp)(const long long, const long long)); +void iv_shrink(ivec *v); diff --git a/src/lexer.c b/src/lexer.c new file mode 100644 index 0000000..4f1fcd0 --- /dev/null +++ b/src/lexer.c @@ -0,0 +1,72 @@ +#include "all.h" +#include "ivec.h" +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +lexer_t lex_init(str *src) { + lexer_t lx = {0}; + + lx.src = src; + lx.pos = 0; + lx.tokens = (ivec){0}; + + return lx; +} + +void lex_run(lexer_t *lx) { + for (size_t i = 0; i < lx->src->length; i++) { + + char c = lx->src->data[i]; + lex_tok_e tok; + + switch (c) { + case '+': + tok = LEX_PINC; + break; + case '-': + tok = LEX_PDEC; + break; + case '>': + tok = LEX_VINC; + break; + case '<': + tok = LEX_VDEC; + break; + case '.': + tok = LEX_OUT; + break; + case ',': + tok = LEX_IN; + break; + case '[': + tok = LEX_LB; + break; + case ']': + tok = LEX_RB; + break; + default: + continue; + } + + iv_push(&lx->tokens, tok); + } + + iv_push(&lx->tokens, LEX_EOF); +} + +void lex_free(lexer_t *lx) { + if (!lx) + return; + + iv_free(&lx->tokens); + + if (lx->src) { + free(lx->src->data); + free(lx->src); + } + + lx->src = NULL; + lx->pos = 0; +} diff --git a/src/node_vec.c b/src/node_vec.c new file mode 100644 index 0000000..f327585 --- /dev/null +++ b/src/node_vec.c @@ -0,0 +1,114 @@ +#include "node_vec.h" +#include "all.h" +#include <stdio.h> +#include <stdlib.h> + +void node_vec_free(node_vec *v) { + free(v->_data); + v->_data = NULL; + v->_cap = 0; + v->len = 0; +} + +static void _node_vec_init(node_vec *v) { + if (v->_data) + return; + + v->_cap = 16; + v->len = 0; + + v->_data = malloc(v->_cap * sizeof(ast_node_t *)); + if (!v->_data) { + perror("_node_vec_init.malloc"); + abort(); + } +} + +static void _node_vec_ensure(node_vec *v) { + _node_vec_init(v); + + if (v->len >= v->_cap) { + v->_cap = (v->_cap == 0) ? 16 : v->_cap * 2; + + ast_node_t **tmp = realloc(v->_data, v->_cap * sizeof(ast_node_t *)); + if (!tmp) { + perror("_node_vec_ensure.realloc"); + abort(); + } + + v->_data = tmp; + } +} + +size_t node_vec_push(node_vec *v, ast_node_t *data) { + _node_vec_ensure(v); + + v->_data[v->len++] = data; + return v->len - 1; +} + +ast_node_t *node_vec_get(node_vec *v, size_t idx) { + _node_vec_init(v); + + if (idx < v->len) + return v->_data[idx]; + + return NULL; +} + +ast_node_t *node_vec_rem_shift(node_vec *v, size_t idx) { + _node_vec_init(v); + + if (idx >= v->len) + return NULL; + + ast_node_t *old = v->_data[idx]; + + for (size_t j = idx; j + 1 < v->len; j++) { + v->_data[j] = v->_data[j + 1]; + } + + v->len--; + return old; +} + +ast_node_t *node_vec_rem(node_vec *v, size_t idx) { + _node_vec_init(v); + + if (idx >= v->len) + return NULL; + + ast_node_t *old = v->_data[idx]; + v->_data[idx] = v->_data[--v->len]; + return old; +} + +ast_node_t *node_vec_has(node_vec *v, ast_node_t *x, + int (*comp)(const ast_node_t *, const ast_node_t *)) { + _node_vec_init(v); + + if (!comp) + return NULL; + + for (size_t i = 0; i < v->len; i++) { + if (comp(v->_data[i], x) == 0) + return v->_data[i]; + } + + return NULL; +} + +void node_vec_shrink(node_vec *v) { + _node_vec_init(v); + + size_t new_cap = v->len ? v->len : 1; + + ast_node_t **tmp = realloc(v->_data, new_cap * sizeof(ast_node_t *)); + if (!tmp) { + perror("node_vec_shrink.realloc"); + abort(); + } + + v->_cap = new_cap; + v->_data = tmp; +} diff --git a/src/node_vec.h b/src/node_vec.h new file mode 100644 index 0000000..8421d32 --- /dev/null +++ b/src/node_vec.h @@ -0,0 +1,26 @@ +#pragma once + +#include <stddef.h> + +typedef struct ast_node ast_node_t; + +typedef struct { + ast_node_t **_data; + size_t _cap; + size_t len; +} node_vec; + +void node_vec_free(node_vec *v); + +size_t node_vec_push(node_vec *v, ast_node_t *data); + +ast_node_t *node_vec_get(node_vec *v, size_t idx); + +ast_node_t *node_vec_rem_shift(node_vec *v, size_t idx); + +ast_node_t *node_vec_rem(node_vec *v, size_t idx); + +ast_node_t *node_vec_has(node_vec *v, ast_node_t *x, + int (*comp)(const ast_node_t *, const ast_node_t *)); + +void node_vec_shrink(node_vec *v); diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..855b94e --- /dev/null +++ b/src/parser.c @@ -0,0 +1,152 @@ +#include "all.h" +#include "ivec.h" +#include "node_vec.h" +#include <stdint.h> +#include <stdlib.h> + +static inline lex_tok_e peek(parser_t *p) { + return iv_get(&p->lx->tokens, p->pos); +} + +static inline lex_tok_e advance(parser_t *p) { + return iv_get(&p->lx->tokens, p->pos++); +} + +static ast_node_t *new_node(ast_type_e type, int val) { + ast_node_t *n = malloc(sizeof(ast_node_t)); + n->type = type; + n->val = val; + n->next = NULL; + n->children = (node_vec){0}; + return n; +} + +static ast_node_t *parse_block(parser_t *p); + +static ast_node_t *parse_single(parser_t *p) { + lex_tok_e t = peek(p); + + switch (t) { + + case LEX_PINC: { + int count = 0; + while (peek(p) == LEX_PINC) { + advance(p); + count++; + } + return new_node(AST_INC, count); + } + + case LEX_PDEC: { + int count = 0; + while (peek(p) == LEX_PDEC) { + advance(p); + count++; + } + return new_node(AST_DEC, count); + } + + case LEX_VINC: { + int count = 0; + while (peek(p) == LEX_VINC) { + advance(p); + count++; + } + return new_node(AST_PTR_INC, count); + } + + case LEX_VDEC: { + int count = 0; + while (peek(p) == LEX_VDEC) { + advance(p); + count++; + } + return new_node(AST_PTR_DEC, count); + } + + case LEX_OUT: + advance(p); + return new_node(AST_OUT, 1); + + case LEX_IN: + advance(p); + return new_node(AST_IN, 1); + + case LEX_LB: + advance(p); // consume '[' + return parse_block(p); + + case LEX_RB: + return NULL; + + case LEX_EOF: + return NULL; + + default: + advance(p); + return NULL; + } +} + +static ast_node_t *parse_block(parser_t *p) { + ast_node_t *loop = new_node(AST_LOOP, 1); + + while (1) { + lex_tok_e t = peek(p); + + if (t == LEX_EOF) { + break; + } + + if (t == LEX_RB) { + advance(p); // consume ']' + break; + } + + ast_node_t *child = parse_single(p); + if (child) { + node_vec_push(&loop->children, child); + } + } + + return loop; +} + +parser_t parser_init(lexer_t *lx) { return (parser_t){.lx = lx, .pos = 0}; } + +ast_node_t *parse_program(parser_t *p) { + ast_node_t *head = NULL; + ast_node_t *tail = NULL; + + while (peek(p) != LEX_EOF) { + + ast_node_t *node = parse_single(p); + if (!node) + continue; + + if (!head) { + head = tail = node; + } else { + tail->next = node; + tail = node; + } + } + + return head; +} + +void ast_free(ast_node_t *node) { + if (!node) + return; + + if (node->next) + ast_free(node->next); + + for (size_t i = 0; i < node->children.len; i++) { + ast_free(node->children._data[i]); + } + + node_vec_free(&node->children); + + free(node); +} diff --git a/src/str.c b/src/str.c new file mode 100644 index 0000000..ddca742 --- /dev/null +++ b/src/str.c @@ -0,0 +1,119 @@ +#include "str.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define STARTING_SIZE 256 +#define READ_CHUNK_SIZE 1024 + +static void str_ensure_init(str *s) { + if (s->data) + return; + + s->capacity = STARTING_SIZE; + s->length = 0; + s->data = malloc(s->capacity); + if (!s->data) + abort(); + + s->data[0] = '\0'; +} + +static void str_ensure_capacity(str *s, size_t extra) { + str_ensure_init(s); + + size_t needed = s->length + extra + 1; // +1 for '\0' + if (needed <= s->capacity) + return; + + while (s->capacity < needed) + s->capacity *= 2; + + char *new_data = realloc(s->data, s->capacity); + if (!new_data) + abort(); + + s->data = new_data; +} + +void free_str(str *s) { + free(s->data); + s->data = NULL; + s->length = 0; + s->capacity = 0; +} + +void str_append(str *s, const char *from) { + str_append_len(s, from, strlen(from)); +} + +void str_append_c(str *s, char c) { str_append_len(s, &c, 1); } + +void str_append_len(str *s, const char *from, size_t len) { + str_ensure_capacity(s, len); + + memcpy(s->data + s->length, from, len); + s->length += len; + s->data[s->length] = '\0'; +} + +void str_append_read(str *s, int fd) { + char buf[READ_CHUNK_SIZE]; + ssize_t n; + + while ((n = read(fd, buf, sizeof(buf))) > 0) { + str_append_len(s, buf, (size_t)n); + } +} + +void str_append_fread(str *s, FILE *file) { + char buf[READ_CHUNK_SIZE]; + size_t n; + + while ((n = fread(buf, 1, sizeof(buf), file)) > 0) { + str_append_len(s, buf, n); + } +} + +void str_append_fread_len(str *s, size_t size, FILE *file) { + char buf[READ_CHUNK_SIZE]; + + while (size > 0) { + size_t chunk = size > sizeof(buf) ? sizeof(buf) : size; + size_t n = fread(buf, 1, chunk, file); + + if (n == 0) + break; + + str_append_len(s, buf, n); + size -= n; + + if (n < chunk) + break; + } +} + +void str_appendf(str *s, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + + va_list copy; + va_copy(copy, args); + int needed = vsnprintf(NULL, 0, fmt, copy); + va_end(copy); + + if (needed < 0) { + va_end(args); + return; + } + + str_ensure_capacity(s, (size_t)needed); + + vsnprintf(s->data + s->length, s->capacity - s->length, fmt, args); + s->length += (size_t)needed; + + va_end(args); +} diff --git a/src/str.h b/src/str.h new file mode 100644 index 0000000..416a846 --- /dev/null +++ b/src/str.h @@ -0,0 +1,22 @@ +#pragma once + +#include <stddef.h> +#include <stdio.h> + +typedef struct { + char *data; + size_t length; + size_t capacity; +} str; + +void free_str(str *s); + +void str_append(str *s, const char *from); +void str_append_c(str *s, char c); +void str_append_len(str *s, const char *from, size_t len); + +void str_append_read(str *s, int fd); +void str_append_fread(str *s, FILE *file); +void str_append_fread_len(str *s, size_t size, FILE *file); + +void str_appendf(str *s, const char *fmt, ...); |
