From 6538e1d7f324eb5ae8f7828a49a8f03b14db85bd Mon Sep 17 00:00:00 2001 From: Ulysse Cura Date: Fri, 9 Oct 2026 10:30:00 +0200 Subject: [PATCH] Initial commit. Working interpreter a little optimized. --- .gitignore | 2 + .vscode/launch.json | 34 ++++++ .vscode/tasks.json | 117 ++++++++++++++++++ Makefile | 69 +++++++++++ brainfuck.bf | 6 + fibonacci.bf | 6 + game_of_life.bf | 20 ++++ ghost.bf | 17 +++ hello_world.bf | 1 + src/Makefile | 23 ++++ src/main.c | 285 ++++++++++++++++++++++++++++++++++++++++++++ utm.bf | 31 +++++ 12 files changed, 611 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Makefile create mode 100644 brainfuck.bf create mode 100644 fibonacci.bf create mode 100644 game_of_life.bf create mode 100644 ghost.bf create mode 100644 hello_world.bf create mode 100644 src/Makefile create mode 100644 src/main.c create mode 100644 utm.bf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4239b68 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.ccls-cache +build diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8f71199 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/brainfuck", + "args": [ + "${workspaceFolder}/fibonacci.bf" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..4d2fc7a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,117 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build", + "detail": "Build project", + "type": "shell", + "command": "make", + "args": [ + "-j$(nproc)" + ], + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "shared", + "showReuseMessage": true, + "clear": true + } + }, + { + "label": "Clean", + "detail": "Clean build directory", + "type": "shell", + "command": "make", + "args": [ + "clean" + ], + "group": "build", + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": true + } + }, + { + "label": "Run", + "detail": "Build project and run", + "type": "shell", + "command": "make", + "args": [ + "-j$(nproc)", + "run" + ], + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "shared", + "showReuseMessage": true, + "clear": true + } + }, + { + "label": "Build Verbose", + "detail": "Build project with verbosity on", + "type": "shell", + "command": "make", + "args": [ + "VERBOSE=1" + ], + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "shared", + "showReuseMessage": true, + "clear": true + } + }, + { + "label": "Build Debug", + "detail": "Build project in debug mode", + "type": "shell", + "command": "make", + "args": [ + "-j$(nproc)", + "DEBUG=2" + ], + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "shared", + "showReuseMessage": true, + "clear": true + } + }, + { + "label": "Run Debug", + "detail": "Build project in debug mode and run", + "type": "shell", + "command": "make", + "args": [ + "-j$(nproc)", + "DEBUG=2", + "run" + ], + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "shared", + "showReuseMessage": true, + "clear": true + } + } + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5fd7294 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +export + +SHELL = /usr/bin/env bash + +ROOT := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) + +# +# User settings +# + +BUILD_DIR = build + +SOURCE_DIR = src + +OUTPUT = brainfuck + +CC = gcc +CFLAGS = --std=c17 -Os +LD_FLAGS = + +ifneq ($(DEBUG),) +CFLAGS += -O0 -g -DDEBUG=$(DEBUG) -Wextra -Wall --pedantic -Wno-missing-braces \ + -Wno-missing-field-initializers -Wformat=2 -Wswitch-default -Wswitch \ + -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 \ + -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual \ + -Wshadow -Wunreachable-code -Wlogical-op -Wfloat-equal -Wstrict-aliasing=2 \ + -Wredundant-decls -Wold-style-definition +endif + +# +# End +# + +# Relocate vars +BUILD_DIR := $(ROOT)/$(BUILD_DIR) + +SOURCE_DIR := $(ROOT)/$(SOURCE_DIR) + +OUTPUT := $(BUILD_DIR)/$(OUTPUT) + +# Verbosity +export Q := @ +ifeq ($(VERBOSE), 1) +Q := +endif + +# Colors +GREEN := \033[32m +YELLOW := \033[33m +RESET := \033[m + +# Default targets +all: $(OUTPUT) + +$(OUTPUT): FORCE + $(Q)cd $(SOURCE_DIR) && $(MAKE) $@ + +FORCE: + +clean: + $(Q)rm -rf $(BUILD_DIR) + +run: all + $(Q)cd $(BUILD_DIR) && ./$(notdir $(OUTPUT)) + +print-var-% : + @echo $($*) + +.PHONY: clean run print-var-% \ No newline at end of file diff --git a/brainfuck.bf b/brainfuck.bf new file mode 100644 index 0000000..95d43e8 --- /dev/null +++ b/brainfuck.bf @@ -0,0 +1,6 @@ +>>>+[[-]>>[-]++>+>+++++++[<++++>>++<-]++>>+>+>+++++[>++>++++++<<-]+>>>,<++[[>[ +->>]<[>>]<<-]<[<]<+>>[>]>[<+>-[[<+>-]>]<[[[-]<]++<-[<+++++++++>[<->-]>>]>>]]<< +]<]<[[<]>[[>]>>[>>]+[<<]<[<]<+>>-]>[>]+[->>]<<<<[[<<]<[<]+<<[+>+<<-[>-->+<<-[> ++<[>>+<<-]]]>[<+>-]<]++>>-->[>]>>[>>]]<<[>>+<[[<]<]>[[<<]<[<]+[-<+>>-[<<+>++>- +[<->[<<+>>-]]]<[>+<-]>]>[>]>]>[>>]>>]<<[>>+>>+>>]<<[->>>>>>>>]<<[>.>>>>>>>]<<[ +>->>>>>]<<[>,>>>]<<[>+>]<<[+<<]<] \ No newline at end of file diff --git a/fibonacci.bf b/fibonacci.bf new file mode 100644 index 0000000..0be820b --- /dev/null +++ b/fibonacci.bf @@ -0,0 +1,6 @@ +>++++++++++>+>+[ + [+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[ + [-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- + [>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>> + ]<<< +] \ No newline at end of file diff --git a/game_of_life.bf b/game_of_life.bf new file mode 100644 index 0000000..6a8ec44 --- /dev/null +++ b/game_of_life.bf @@ -0,0 +1,20 @@ +>>>->+>+++++>(++++++++++)[[>>>+<<<-]>+++++>+>>+[<<+>>>>>+<<<-]<-]>>>>[ + [>>>+>+<<<<-]+++>>+[<+>>>+>+<<<-]>>[>[[>>>+<<<-]<]<<++>+>>>>>>-]<- +]+++>+>[[-]<+<[>+++++++++++++++++<-]<+]>>[ + [+++++++++.-------->>>]+[-<<<]>>>[>>,----------[>]<]<<[ + <<<[ + >--[<->>+>-<<-]<[[>>>]+>-[+>>+>-]+[<<<]<-]>++>[<+>-] + >[[>>>]+[<<<]>>>-]+[->>>]<-[++>]>[------<]>+++[<<<]> + ]< + ]>[ + -[+>>+>-]+>>+>>>+>[<<<]>->+>[ + >[->+>+++>>++[>>>]+++<<<++<<<++[>>>]>>>]<<<[>[>>>]+>>>] + <<<<<<<[<<++<+[-<<<+]->++>>>++>>>++<<<<]<<<+[-<<<+]+>->>->> + ]<<+<<+<<<+<<-[+<+<<-]+<+[ + ->+>[-<-<<[<<<]>[>>[>>>]<<+<[<<<]>-]] + <[<[<[<<<]>+>>[>>>]<<-]<[<<<]]>>>->>>[>>>]+> + ]>+[-<<[-]<]-[ + [>>>]<[<<[<<<]>>>>>+>[>>>]<-]>>>[>[>>>]<<<<+>[<<<]>>-]> + ]<<<<<<[---<-----[-[-[<->>+++<+++++++[-]]]]<+<+]> + ]>> +] \ No newline at end of file diff --git a/ghost.bf b/ghost.bf new file mode 100644 index 0000000..78f87bf --- /dev/null +++ b/ghost.bf @@ -0,0 +1,17 @@ +-[ + ->+++++++++>+++>->>>>->>->>>>->>->>>>->+++ + >>->+>++>>+>->++>>+>->++>>+>->++>+++++++++ + >+>[>>>]<[>------->-------->->-->>]>>-[<-]< + ++[[[>>]+++<[>]++[<]<-]>-<<++]>+>[+>]<<[++++[>++++<-]<<] + <<<[[>->>>++++++++++<<<<+]<[>->>>>+<<<<<+]<]>>[>]>[<+>>+>>+<<<-] + >>>[-[>>+<<-]>>]<[<+>-]<[>++<-]<[<<]<<[>->>[>]+[<]<<-]->>>[>]<+[<-<]>+ + >[.>+++++++++[-[>>+<<-]>.>]++++++++++.[-]>]+[<<]<[ + ,->>>---[<<<--->>>-],<-<<[ + <++>>>++<<---[ + <++[>-----<-]>>+>-<<[ + >--<----[>+<[-]]]]]>[<+>>++++++++++<-] + >[-[>>+<<-]>>]++++[>--------<-]>[-[[<<]<[-]<[-]>>>>[-]]]+[>>]+[[-]<<]<<<<[ + [>>[<<->>-]>>+<<<<[+<<]>>>>]>>[<<]<<<<[+>>]>>>>[<<]>>[<<<<<<->>->>>>-]<<< + ]-[+<-]-< + ]> +]>>-----[>+++<-]>-.----------.++++++.>---.[<<+>-->----]<-.+++++.----.<++. diff --git a/hello_world.bf b/hello_world.bf new file mode 100644 index 0000000..a0503f3 --- /dev/null +++ b/hello_world.bf @@ -0,0 +1 @@ +++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++.>+.+++++++..+++.<<++.>+++++++++++++++.>.+++.------.--------.<<.+.>>>++++++++++. \ No newline at end of file diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..b174caf --- /dev/null +++ b/src/Makefile @@ -0,0 +1,23 @@ +SOURCES += \ + main.c + +INCLUDES += + +OBJECTS := $(SOURCES:%=$(BUILD_DIR)/$(notdir $(SOURCE_DIR))/%.o) + +ifeq ($(TOTAL_BUILD_FILES),) +TOTAL_BUILD_FILES := $(shell echo $$(($$($(MAKE) -n $(OBJECTS) TOTAL_BUILD_FILES=1 2>/dev/null | grep -c Building) + 1))) +endif + +$(OUTPUT): $(OBJECTS) + @echo "[100%] $(YELLOW)Linking $(notdir $(OUTPUT))$(RESET)" + $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ + +$(BUILD_DIR)/$(notdir $(SOURCE_DIR))/%.o: $(SOURCE_DIR)/% + $(Q)mkdir -p $(dir $@) + $(eval CURRENT_FILE := $(shell echo $$(($(CURRENT_FILE) + 1)))) + $(eval PERCENTAGE := $(shell echo $$(($(CURRENT_FILE) * 100 / $(TOTAL_BUILD_FILES))))) + @echo "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)" + $(Q)$(CC) $(CFLAGS) $(INCLUDES:%=-I%) -MMD -MP -c $< -o $@ + +-include $(OBJECTS:.o=.d) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..4f88bd7 --- /dev/null +++ b/src/main.c @@ -0,0 +1,285 @@ +#include +#include +#include +#include +#include + +#include + +#define BRACKETS_NB_MAX 1024 +#define DATA_SIZE_MAX 65536 + +int main(int argc, char *argv[]) +{ + clock_t start_time = clock(); + + int status = EXIT_FAILURE; + + if(argc != 2) + { + puts("usage : brainfuck program"); + return EXIT_FAILURE; + } + + FILE *program_file = NULL; + FILE *stripped_file = NULL; + + program_file = fopen(argv[1], "r"); + if(program_file == NULL) + { + fprintf(stderr, "Error opening program file: %d\n", errno); + goto end; + } + + stripped_file = tmpfile(); + if(stripped_file == NULL) + { + fprintf(stderr, "Error creating tmp file: %d\n", errno); + goto end; + } + + // Strip program file + while(true) + { + int c = fgetc(program_file); + + if(c == EOF) + { + int err = ferror(program_file); + + if(err) + { + fprintf(stderr, "Error reading file: %d\n", ferror(program_file)); + return EXIT_FAILURE; + } + + break; + } + + switch(c) + { + case '>': + case '<': + case '+': + case '-': + case '[': + case ']': + case '.': + case ',': + fputc(c, stripped_file); + + default: + break; + } + } + + rewind(stripped_file); + + if(fclose(program_file) == EOF) + { + program_file = NULL; + + fprintf(stderr, "Error closing program file: %d\n", errno); + + goto end; + } + + program_file = NULL; + + size_t brackets_pos[BRACKETS_NB_MAX]; + size_t brackets_correspondances[BRACKETS_NB_MAX]; + + // Parse brackets + size_t bracket_nb = 0; + + while(true) + { + int c = fgetc(stripped_file); + + if(c == EOF) + { + int err = ferror(stripped_file); + + if(err) + { + fprintf(stderr, "Error reading file: %d\n", ferror(stripped_file)); + goto end; + } + + break; + } + + if(c == '[' || c == ']') + { + brackets_pos[bracket_nb] = ftell(stripped_file); + + if(c == '[') + { + // Find corresponding closing bracket index + size_t corresponding_bracket_nb = bracket_nb; + + for(size_t bracket_index = 1; bracket_index;) + { + int d = fgetc(stripped_file); + + if(d == EOF) + { + int err = ferror(stripped_file); + + if(err) + { + fprintf(stderr, "Error reading file: %d\n", argv[1], ferror(stripped_file)); + } + else + { + fprintf(stderr, "Reached EOF before finding closing bracket."); + } + + goto end; + } + + if(d == '[' || d == ']') + { + corresponding_bracket_nb++; + } + + if(d == '[') + { + bracket_index++; + } + else if(d == ']') + { + bracket_index--; + } + } + + brackets_correspondances[bracket_nb] = corresponding_bracket_nb; + brackets_correspondances[corresponding_bracket_nb] = bracket_nb; + + fseek(stripped_file, brackets_pos[bracket_nb], SEEK_SET); + } + + bracket_nb++; + } + } + + rewind(stripped_file); + + // Program memory + uint8_t mem[DATA_SIZE_MAX] = {0}; + size_t mem_pointer = 0; + + size_t bracket_index = 0; + + while(true) + { + int c = fgetc(stripped_file); + + if(c == EOF) + { + int err = ferror(stripped_file); + + if(err) + { + fprintf(stderr, "Error reading file: %d\n", ferror(stripped_file)); + goto end; + } + + break; + } + + if(c == '[' || c == ']') + { + if(c == '[' ? !mem[mem_pointer] : mem[mem_pointer]) + { + bracket_index = brackets_correspondances[bracket_index]; + fseek(stripped_file, brackets_pos[bracket_index], SEEK_SET); + } + } + else if(c == '>') + { + mem_pointer++; + } + else if(c == '<') + { + mem_pointer--; + } + else if(c == '+') + { + mem[mem_pointer]++; + } + else if(c == '-') + { + mem[mem_pointer]--; + } + else if(c == '.') + { + putchar(mem[mem_pointer]); + fflush(stdout); + } + else if(c == ',') + { +/* + static int next_input = -1; + int input; + + if(next_input != -1) + { + input = next_input; + next_input = -1; + } + else + { + input = getchar(); + } + + next_input = getchar(); + + if(input != '\n') + { + mem[mem_pointer] = input; + } + else + { + if(next_input == '\n') + { + mem[mem_pointer] = '\n'; + } + + next_input = -1; + } +*/ + mem[mem_pointer] = getchar(); + } + + if(c == '[' || c == ']') + { + bracket_index++; + } + } + + status = EXIT_SUCCESS; + +end: + if(stripped_file != NULL) + { + if(fclose(stripped_file)) + { + fprintf(stderr, "Failed to close tmp file: %d\n", errno); + } + } + + if(program_file != NULL) + { + if(fclose(program_file)) + { + fprintf(stderr, "Failed to close tmp file: %d\n", errno); + } + } + + clock_t end_time = clock(); + + fprintf(stderr, "\nExecution time : %fs\n", (float)(end_time - start_time) / (float)CLOCKS_PER_SEC); + + return status; +} diff --git a/utm.bf b/utm.bf new file mode 100644 index 0000000..7d6fa01 --- /dev/null +++ b/utm.bf @@ -0,0 +1,31 @@ +Universal Turing Machine + +If you just want a quick and complete test case the input b1b1bbb1c1c11111d +should produce the output 1c11111 + ++++>++>>>+[>>,[>+++++<[[->]<<]<[>]>]>-[<<+++++>>-[<<---->>-[->]<]] +<[<-<[<]+<+[>]<<+>->>>]<]<[<]>[-[>++++++<-]>[<+>-]+<<<+++>+> + [- + [<<+>->- + [<<[-]>>- + [<<++>+>- + [<<-->->>+++<- + [<<+>+>>--<- + [<<->->- + [<<++++>+>>+<- + [>-<- + [<<->->- + [<<->>- + [<<+++>>>-<- + [<<---->>>++<- + [<<++>>>+<- + [>[-]<- + [<<->>>+++<- + [<<->>>--<- + [<<++++>+>>+<- + [<<[-]>->>++<- + [<<+++++>+>>--<- + [<->>++< + [<<->>- +]]]]]]]]]]]]]]]]]]]]]]<[->>[<<+>>-]<<<[>>>+<<<-]<[>>>+<<<-]]>>] +>[-[---[-<]]>]>[+++[<+++++>--]>]+<++[[>+++++<-]<]>>[-.>] \ No newline at end of file