{"id":17978176,"url":"https://github.com/overv/bfc","last_synced_at":"2025-04-04T00:23:46.765Z","repository":{"id":143138660,"uuid":"12451940","full_name":"Overv/BFC","owner":"Overv","description":"Standalone brainfuck compiler","archived":false,"fork":false,"pushed_at":"2013-08-29T05:25:37.000Z","size":88,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-09T12:19:46.342Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Overv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-08-29T05:05:48.000Z","updated_at":"2023-03-16T18:14:27.000Z","dependencies_parsed_at":"2023-03-16T17:46:01.586Z","dependency_job_id":null,"html_url":"https://github.com/Overv/BFC","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overv%2FBFC","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overv%2FBFC/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overv%2FBFC/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overv%2FBFC/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Overv","download_url":"https://codeload.github.com/Overv/BFC/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247099452,"owners_count":20883395,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-29T17:32:07.633Z","updated_at":"2025-04-04T00:23:46.749Z","avatar_url":"https://github.com/Overv.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"BFC\n===\n\nBFC is an ahead-of-time compiler for\n[brainfuck](http://en.wikipedia.org/wiki/Brainfuck). It takes a brainfuck source\nfile and outputs native code in the form of a 32-bit ELF executable. The\nexecutable only depends on Linux syscalls, not the C library or anything else.\nThis is basically as fast as unoptimized brainfuck code is going to get.\n\nThe output program allocates 1 MB (1048576 cells) of memory on the stack for the\ntape. That is more than enough, even for complex programs like the [mandelbrot\nrenderer](http://esoteric.sange.fi/brainfuck/utils/mandelbrot/mandelbrot.b) by\nErik Bosman. Because the stack grows downwards, the increase and decrease\npointer operations are reversed in the output code.\n\nAlthough a language like C is generally more suitable for low-level development\nlike this, I decided to implement it in Python because it makes the code simply\nmore readable and easy to understand. Additionally, it removes frustrations like\nhaving to implement file reading code for the 1000th time. The source should be\nvalid Python 2.7 and Python 3.x code.\n\nUsage\n-----\n\nPrepare a brainfuck source file, like `tests/hello.bf` and invoke the compiler\nby running:\n\n    $ ./bfc.py tests/hello.bf\n\nIf the file could be read and correctly parsed, then an executable named `hello`\nwill be produced in the same directory.\n\n    $ tests/hello\n    Hello World!\n\nTests\n-----\n\nSome sample brainfuck programs have been included for testing the compiler.\n\n- **hello**: Hello world\n- **infinite**: Infinite loop\n- **mandelbrot**: 128x48 ascii mandelbrot renderer by Erik Bosman\n- **rot13**: ROT13 cipher\n\nDetails\n-------\n\nSince this project simultaneously serves as a learning sample for basic compiler\narchitectures, it is set up like a regular compiler. The input source goes\nthrough the\n\n- Lexing\n- Parsing\n- Optimizing\n- Compiling\n- Linking\n\npasses to result in an output executable. The lexer simply discards all\ncharacters that are not part of the brainfuck language and turns the characters\ninto tokens, implemented as child classes of `Token`.\n\nThe parser is a recursive descent parser and basically only exists for loops.\nIt's included for completeness more than anything else, although the tree it\ncreates does help a bit with code generation logic.\n\nThe parser is implemented according to the following LL(1) grammar in EBNF form:\n\n    program  = { command | loop }\n    command  = \"\u003e\" | \"\u003c\" | \"+\" | \"-\" | \".\" | \",\"\n    loop     = \"[\", { command | loop }, \"]\n\nThe optimizer merges repeated increment and decrement instructions, so that the\ncode generator can easily emit add and subtract instructions instead.\n\nThe code generator produces one or two instructions for each node in the AST.\nJumps are written with the correct relative addresses corresponding to loop\nbeginnings and endings. Currently it always uses the 16/32 bit jump instructions\nfor simplicity. Optimization for executable size could be added by falling back\nto 8 bit jumps if possible.\n\nThe final pass creates a bare-bones ELF file that instructs the loader to load\nthe entire file into memory (as is common with executables) and run the program\npositioned in the file after the ELF and program header.\n\nThere are no checks at runtime, so if you mess up the program, it will happily\nwrite or read memory where it shouldn't. Because the behaviour of brainfuck is\nrather hard to predict, the only solution would be to add bounds checks to every\nread and write, but all these branches would significantly impact performance.\n\nThe code generated for each token is listed below. Input and output is handled\nby the read and write syscalls on Linux. They are called using interrupt `0x80`.\nThe exit syscall is used to end the program.\n\n**\u003e**\n```nasm\ndec esp\n```\n\n**\u003c**\n```nasm\ninc esp\n```\n\n**+**\n```nasm\ninc [esp]\n```\n\n**-**\n```nasm\ndec [esp]\n```\n\n**.**\n```nasm\nmov eax, 0x4 ; write\nmov ebx, 0x1 ; stdout\nmov ecx, esp\nmov edx, 0x1 ; write 1 byte at pointer\nint 0x80\n```\n\n**,**\n```nasm\nmov eax, 0x3 ; read\nmov ebx, 0x0 ; stdin\nmov ecx, esp\nmov edx, 0x1 ; read 1 byte to pointer\nint 0x80\n```\n\n**[**\n```nasm\nloop:\n    cmp [esp], 0\n    je loop_end\n```\n\n**]**\n```nasm\n    jmp loop\nloop_end:\n```\n\n**EOF**\n```nasm\nmov eax, 1 ; exit\nmov ebx, 0 ; EXIT_SUCCESS (= 0)\nint 0x80\n```\n\nIt may be an interesting experiment to mark the code section itself as writable\nand allow for self-modifying code that way.\n\nLicense\n-------\n\nBFC is licensed under the MIT license, of which the terms are outlined in the\n`LICENSE` file.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverv%2Fbfc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foverv%2Fbfc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverv%2Fbfc/lists"}