{"id":15060439,"url":"https://github.com/skx/bfcc","last_synced_at":"2025-04-10T05:50:59.616Z","repository":{"id":144213457,"uuid":"272168887","full_name":"skx/bfcc","owner":"skx","description":"BrainFuck Compiler Challenge","archived":false,"fork":false,"pushed_at":"2020-10-01T07:40:33.000Z","size":74,"stargazers_count":29,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-24T07:04:18.588Z","etag":null,"topics":["amd64","assembly-language","assembly-language-programming","brainfuck","brainfuck-compiler","golang","nasm"],"latest_commit_sha":null,"homepage":"https://blog.steve.fi/writing_a_brainfuck_compiler_.html","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/skx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"skx","custom":"https://steve.fi/donate/"}},"created_at":"2020-06-14T09:16:21.000Z","updated_at":"2025-02-21T17:05:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"6eb647e5-baa6-4a8f-9d4b-f4688b2a84f8","html_url":"https://github.com/skx/bfcc","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Fbfcc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Fbfcc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Fbfcc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skx%2Fbfcc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skx","download_url":"https://codeload.github.com/skx/bfcc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166926,"owners_count":21058480,"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":["amd64","assembly-language","assembly-language-programming","brainfuck","brainfuck-compiler","golang","nasm"],"created_at":"2024-09-24T22:58:44.050Z","updated_at":"2025-04-10T05:50:59.603Z","avatar_url":"https://github.com/skx.png","language":"Go","readme":"[![GoDoc](https://img.shields.io/static/v1?label=godoc\u0026message=reference\u0026color=blue)](https://pkg.go.dev/github.com/skx/bfcc)\n[![Go Report Card](https://goreportcard.com/badge/github.com/skx/bfcc)](https://goreportcard.com/report/github.com/skx/bfcc)\n[![license](https://img.shields.io/github/license/skx/bfcc.svg)](https://github.com/skx/bfcc/blob/master/LICENSE)\n[![Release](https://img.shields.io/github/release/skx/bfcc.svg)](https://github.com/skx/bfcc/releases/latest)\n\nTable of Contents\n=================\n\n* [BrainFuck Compiler Challenge](#brainfuck-compiler-challenge)\n   * [Usage](#usage)\n   * [My Approach](#my-approach)\n   * [Test Programs](#test-programs)\n      * [Debugging the generated program](#debugging-the-generated-program)\n   * [Future Plans?](#future-plans)\n      * [See Also](#see-also)\n   * [Bug Reports?](#bug-reports)\n   * [Github Setup](#github-setup)\n\n\n\n# BrainFuck Compiler Challenge\n\nI challenged myself to write a BrainFuck compiler, in less than a day.  This repository contains the result.  I had an initial sprint of 3-4 hours, which lead to a working system, and then spent a little longer tidying and cleaning it up.\n\n[Brainfuck](https://en.wikipedia.org/wiki/Brainfuck) is an esoteric programming language created in 1993 by Urban Müller, and is notable for its extreme minimalism.  It supports only a few instructions, and is practically unreadable.\n\nBrainfuck, despite the name, has a good history in the programming world as being something simple and fun to play with.\n\n\n\n## Usage\n\nYou can install the compiler via:\n\n    $ go get github.com/skx/bfcc\n\nOnce installed execute the compiler as follows to produce the default executable at `./a.out`:\n\n    $ bfcc ./examples/mandelbrot.bf\n    $ ./a.out\n\nRather than compile, then run, you can add `-run` to your invocation:\n\n    $ bfcc -run ./examples/bizzfuzz.bf\n\nFinally if you prefer you can specify an output name for the compiled result:\n\n    $ bfcc [-run] ./examples/bizzfuzz.bf ./bf\n    $ ./bf\n\nThere are three backends included:\n\n* `asm`\n  * Generates an assembly language source-file, and compiles with `gcc`\n* `c`\n  * Generates C-code which is also compiled via `gcc`.\n* `interpreter`\n  * This actually executes Brainfuck programs, and does zero compilation.\n\n\nBy default the assembly-language backend is selected, because this is the thing that I was more interested in writing.\n\nYou may use the `-backend` flag to specify the backend which you prefer to use:\n\n    $ bfcc -backend=c   ./examples/mandelbrot.bf ./mb-c\n    $ bfcc -backend=asm ./examples/mandelbrot.bf ./mb-asm\n\nYou'll see slightly difference sizes in the two executable:\n\n    $ ls -lash mb-*\n    24K -rwxr-xr-x 1 skx skx 21K Jun 16 14:54 mb-asm\n    36K -rwxr-xr-x 1 skx skx 34K Jun 16 14:54 mb-c\n\nBoth compiling-backends should produce binaries that are standalone, and work identically - if they do not that's a bug in the code-generation.\n\nThe interpreter backend is only included to show how much faster compilation is than interpreting.  The mandelbrot example takes almost two minutes upon my system, whereas the compiled version takes 1.2 seconds!\n\n    $ ./bfcc -backend=interpreter ./examples/hello-world.bf\n    Hello World!\n\n\n\n## My Approach\n\nIn the end it took me about four hours to get something I was happy with, and later I've improved it a little more:\n\n* Initially I generated C-code, [as you can see here](https://github.com/skx/bfcc/blob/cadb19d6c75a5febde56f53423a9668ee8f6bd25/main.go).\n  * This code was largely inspired by the [Wikipedia brainfuck page](https://en.wikipedia.org/wiki/Brainfuck).\n  * The C-code was compiled by GCC to produce an executable.\n* Then I started generating assembly-language code, which looked [something like this](https://github.com/skx/bfcc/blob/aebb14ccb548a2249bc32bb1f82fe9070518cc3c/main.go).\n  * The generated assembly was compiled by `nasm`, and linked with `ld` to produce an executable.\n* Once the assembly language code was working I optimized it.\n  * Collapsing multiple identical instructions to one, which looked [like this](https://github.com/skx/bfcc/blob/91d6712bcb4b41e9fd963f60da2753d62ee789d1/main.go).\n    * This was buggy, but that wasn't obvious at the time!\n  * Improving the way loop-handling was generated.\n    * This looked [like this](https://github.com/skx/bfcc/blob/88e2551fbafea7814de7fe6d7ef5df2b5a47abe2/main.go), and was designed to test the condition at the _end_ of the loop, as well as at the start.  The expectation being the comparison would be branch-predicted, and that would be cheaper for the processor than an extra unconditional jump each iteration.\n* Finally I cleaned up and improved the code.\n  * Adding a lexer in [#4](https://github.com/skx/bfcc/pull/4)\n  * Allowing the generation of either C or assembly in [#6](https://github.com/skx/bfcc/pull/6), via the addition of a backend-abstraction.\n  * Allow generating a breakpoint instruction (`int03`) when using the assembly-backend in [#7](https://github.com/skx/bfcc/pull/7).\n  * Switched to generating assembly to be compiled by `gcc` rather than `nasm` [#8](https://github.com/skx/bfcc/pull/8).\n  * Added an interpreter in [#11](https://github.com/skx/bfcc/pull/12).\n\n\n\n## Test Programs\n\nThere are a small collection of test-programs located beneath the [examples/](examples/) directory.\n\nEach example has a `.bf` suffix, and there is a corresponding output file for each input to show the expected output.\n\nYou can run `make test` to run all the scripts, and compare their generated output with the expected result.\n\n\n\n### Debugging the generated program\n\nIf you run the compiler with the `-debug` flag, using the assembly-language\nbackend, a breakpoint will be generated immediately at the start of the\nprogram.  You can use that breakpoint to easily debug the generated binary\nvia `gdb`.\n\n    $ bfcc -debug ./examples/hello-world.bf\n\nNow you can launch that binary under `gdb`, and run it:\n\n    $ gdb ./a.out\n    (gdb) run\n    ..\n    Program received signal SIGTRAP, Trace/breakpoint trap.\n    0x00000000004000bb in _start ()\n\nDisassemble the code via `disassemble`, and step over instructions one at a time via `stepi`.  If your program is long you might see a lot of output from the `disassemble` step.\n\n    (gdb) disassemble\n    Dump of assembler code for function _start:\n       0x00000000004000b0 \u003c+0\u003e:\tmovabs $0x600290,%r8\n       0x00000000004000ba \u003c+10\u003e:\tint3\n    =\u003e 0x00000000004000bb \u003c+11\u003e:\taddb   $0x8,(%r8)\n       0x00000000004000bf \u003c+15\u003e:\tcmpb   $0x0,(%r8)\n       0x00000000004000c3 \u003c+19\u003e:\tje     0x40013f \u003cclose_loop_1\u003e\n    End of assembler dump.\n\nYou can set a breakpoint at a line in the future, and continue running till\nyou hit it, with something like this:\n\n     (gdb) break *0x00000000004000c3\n     (gdb) cont\n\nOnce there inspect the registers with commands like these two:\n\n     (gdb) print $r8\n     (gdb) print *$r8\n     (gdb) info registers\n\n\u003e **NOTE**: `r8` is the register we use for our index/memory-pointer.  So viewing that can be useful.  The contents of a memory cell can be viewed via `*$r8`.\n\nFurther documentation can be found in the `gdb` manual, which is worth reading\nif you've an interest in compilers, debuggers, and decompilers.\n\n\n\n\n## Future Plans?\n\nMostly none.\n\nMore backends might be nice, but I guess the two existing ones are the most obvious.  Due to the way the code is structured adding a new one would be trivial.\n\n\n\n\n## See Also\n\nIf you enjoyed this repository you might also enjoy my simple math-compiler, which converts postfix mathematical operations to x86-64 assembly-language:\n\n* [https://github.com/skx/math-compiler](https://github.com/skx/math-compiler)\n\n\n\n\n## Bug Reports?\n\nPlease [file an issue](https://github.com/skx/bfcc/issues)\n\n\n\n\n# Github Setup\n\nThis repository is configured to run tests upon every commit, and when\npull-requests are created/updated.  The testing is carried out via\n[.github/run-tests.sh](.github/run-tests.sh) which is used by the\n[github-action-tester](https://github.com/skx/github-action-tester) action.\n\nReleases are automated in a similar fashion via [.github/build](.github/build),\nand the [github-action-publish-binaries](https://github.com/skx/github-action-publish-binaries) action.\n\n\nSteve\n--\n","funding_links":["https://github.com/sponsors/skx","https://steve.fi/donate/"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskx%2Fbfcc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskx%2Fbfcc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskx%2Fbfcc/lists"}