Initial commit. Working interpreter a little optimized.

This commit is contained in:
Ulysse Cura 2026-10-09 10:30:00 +02:00
commit 6538e1d7f3
12 changed files with 611 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.ccls-cache
build

34
.vscode/launch.json vendored Normal file
View File

@ -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
}
]
}
]
}

117
.vscode/tasks.json vendored Normal file
View File

@ -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
}
}
]
}

69
Makefile Normal file
View File

@ -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-%

6
brainfuck.bf Normal file
View File

@ -0,0 +1,6 @@
>>>+[[-]>>[-]++>+>+++++++[<++++>>++<-]++>>+>+>+++++[>++>++++++<<-]+>>>,<++[[>[
->>]<[>>]<<-]<[<]<+>>[>]>[<+>-[[<+>-]>]<[[[-]<]++<-[<+++++++++>[<->-]>>]>>]]<<
]<]<[[<]>[[>]>>[>>]+[<<]<[<]<+>>-]>[>]+[->>]<<<<[[<<]<[<]+<<[+>+<<-[>-->+<<-[>
+<[>>+<<-]]]>[<+>-]<]++>>-->[>]>>[>>]]<<[>>+<[[<]<]>[[<<]<[<]+[-<+>>-[<<+>++>-
[<->[<<+>>-]]]<[>+<-]>]>[>]>]>[>>]>>]<<[>>+>>+>>]<<[->>>>>>>>]<<[>.>>>>>>>]<<[
>->>>>>]<<[>,>>>]<<[>+>]<<[+<<]<]

6
fibonacci.bf Normal file
View File

@ -0,0 +1,6 @@
>++++++++++>+>+[
[+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[
[-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-
[>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>
]<<<
]

20
game_of_life.bf Normal file
View File

@ -0,0 +1,20 @@
>>>->+>+++++>(++++++++++)[[>>>+<<<-]>+++++>+>>+[<<+>>>>>+<<<-]<-]>>>>[
[>>>+>+<<<<-]+++>>+[<+>>>+>+<<<-]>>[>[[>>>+<<<-]<]<<++>+>>>>>>-]<-
]+++>+>[[-]<+<[>+++++++++++++++++<-]<+]>>[
[+++++++++.-------->>>]+[-<<<]>>>[>>,----------[>]<]<<[
<<<[
>--[<->>+>-<<-]<[[>>>]+>-[+>>+>-]+[<<<]<-]>++>[<+>-]
>[[>>>]+[<<<]>>>-]+[->>>]<-[++>]>[------<]>+++[<<<]>
]<
]>[
-[+>>+>-]+>>+>>>+>[<<<]>->+>[
>[->+>+++>>++[>>>]+++<<<++<<<++[>>>]>>>]<<<[>[>>>]+>>>]
<<<<<<<[<<++<+[-<<<+]->++>>>++>>>++<<<<]<<<+[-<<<+]+>->>->>
]<<+<<+<<<+<<-[+<+<<-]+<+[
->+>[-<-<<[<<<]>[>>[>>>]<<+<[<<<]>-]]
<[<[<[<<<]>+>>[>>>]<<-]<[<<<]]>>>->>>[>>>]+>
]>+[-<<[-]<]-[
[>>>]<[<<[<<<]>>>>>+>[>>>]<-]>>>[>[>>>]<<<<+>[<<<]>>-]>
]<<<<<<[---<-----[-[-[<->>+++<+++++++[-]]]]<+<+]>
]>>
]

17
ghost.bf Normal file
View File

@ -0,0 +1,17 @@
-[
->+++++++++>+++>->>>>->>->>>>->>->>>>->+++
>>->+>++>>+>->++>>+>->++>>+>->++>+++++++++
>+>[>>>]<[>------->-------->->-->>]>>-[<-]<
++[[[>>]+++<[>]++[<]<-]>-<<++]>+>[+>]<<[++++[>++++<-]<<]
<<<[[>->>>++++++++++<<<<+]<[>->>>>+<<<<<+]<]>>[>]>[<+>>+>>+<<<-]
>>>[-[>>+<<-]>>]<[<+>-]<[>++<-]<[<<]<<[>->>[>]+[<]<<-]->>>[>]<+[<-<]>+
>[.>+++++++++[-[>>+<<-]>.>]++++++++++.[-]>]+[<<]<[
,->>>---[<<<--->>>-],<-<<[
<++>>>++<<---[
<++[>-----<-]>>+>-<<[
>--<----[>+<[-]]]]]>[<+>>++++++++++<-]
>[-[>>+<<-]>>]++++[>--------<-]>[-[[<<]<[-]<[-]>>>>[-]]]+[>>]+[[-]<<]<<<<[
[>>[<<->>-]>>+<<<<[+<<]>>>>]>>[<<]<<<<[+>>]>>>>[<<]>>[<<<<<<->>->>>>-]<<<
]-[+<-]-<
]>
]>>-----[>+++<-]>-.----------.++++++.>---.[<<+>-->----]<-.+++++.----.<++.

1
hello_world.bf Normal file
View File

@ -0,0 +1 @@
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++.>+.+++++++..+++.<<++.>+++++++++++++++.>.+++.------.--------.<<.+.>>>++++++++++.

23
src/Makefile Normal file
View File

@ -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)

285
src/main.c Normal file
View File

@ -0,0 +1,285 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#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;
}

31
utm.bf Normal file
View File

@ -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
+++>++>>>+[>>,[>+++++<[[->]<<]<[>]>]>-[<<+++++>>-[<<---->>-[->]<]]
<[<-<[<]+<+[>]<<+>->>>]<]<[<]>[-[>++++++<-]>[<+>-]+<<<+++>+>
[-
[<<+>->-
[<<[-]>>-
[<<++>+>-
[<<-->->>+++<-
[<<+>+>>--<-
[<<->->-
[<<++++>+>>+<-
[>-<-
[<<->->-
[<<->>-
[<<+++>>>-<-
[<<---->>>++<-
[<<++>>>+<-
[>[-]<-
[<<->>>+++<-
[<<->>>--<-
[<<++++>+>>+<-
[<<[-]>->>++<-
[<<+++++>+>>--<-
[<->>++<
[<<->>-
]]]]]]]]]]]]]]]]]]]]]]<[->>[<<+>>-]<<<[>>>+<<<-]<[>>>+<<<-]]>>]
>[-[---[-<]]>]>[+++[<+++++>--]>]+<++[[>+++++<-]<]>>[-.>]