├── .github ├── FUNDING.yml ├── build ├── run-tests.sh └── workflows │ ├── codeql-analysis.yml │ ├── pull_request.yml │ ├── push.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── examples ├── bizzfuzz.bf ├── bizzfuzz.out ├── factor.bf ├── factor.in ├── factor.out ├── fibonacci.bf ├── fibonacci.out ├── hello-world.bf ├── hello-world.out ├── mandelbrot.bf ├── mandelbrot.out ├── quine.bf └── quine.out ├── generators ├── asm.go ├── c.go ├── generators.go └── interpreter.go ├── go.mod ├── lexer ├── lexer.go └── lexer_test.go └── main.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: skx 3 | custom: https://steve.fi/donate/ 4 | -------------------------------------------------------------------------------- /.github/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The basename of our binary 4 | BASE="bfcc" 5 | 6 | # 7 | # We build on multiple platforms/archs 8 | # 9 | BUILD_PLATFORMS="linux darwin freebsd" 10 | BUILD_ARCHS="amd64 386" 11 | 12 | # For each platform 13 | for OS in ${BUILD_PLATFORMS[@]}; do 14 | 15 | # For each arch 16 | for ARCH in ${BUILD_ARCHS[@]}; do 17 | 18 | # Setup a suffix for the binary 19 | SUFFIX="${OS}" 20 | 21 | # i386 is better than 386 22 | if [ "$ARCH" = "386" ]; then 23 | SUFFIX="${SUFFIX}-i386" 24 | else 25 | SUFFIX="${SUFFIX}-${ARCH}" 26 | fi 27 | 28 | # Windows binaries should end in .EXE 29 | if [ "$OS" = "windows" ]; then 30 | SUFFIX="${SUFFIX}.exe" 31 | fi 32 | 33 | echo "Building for ${OS} [${ARCH}] -> ${BASE}-${SUFFIX}" 34 | 35 | # Run the build 36 | export GOARCH=${ARCH} 37 | export GOOS=${OS} 38 | export CGO_ENABLED=0 39 | 40 | # Build the main-binary 41 | go build -ldflags "-X main.version=$(git describe --tags 2>/dev/null || echo 'master')" -o "${BASE}-${SUFFIX}" 42 | 43 | done 44 | done 45 | -------------------------------------------------------------------------------- /.github/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Install tools to test our code-quality. 4 | go get -u golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow 5 | go get -u honnef.co/go/tools/cmd/staticcheck 6 | 7 | # At this point failures cause aborts 8 | set -e 9 | 10 | # Run the static-check tool 11 | t=$(mktemp) 12 | staticcheck -checks all ./... > $t 13 | if [ -s $t ]; then 14 | echo "Found errors via 'staticcheck'" 15 | cat $t 16 | rm $t 17 | exit 1 18 | fi 19 | rm $t 20 | 21 | # Run the shadow-checker 22 | echo "Launching shadowed-variable check .." 23 | go vet -vettool=$(which shadow) ./... 24 | echo "Completed shadowed-variable check .." 25 | 26 | # Run golang tests 27 | go test ./... 28 | 29 | # Run the actual test-cases 30 | make test 31 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [master] 14 | schedule: 15 | - cron: '0 13 * * 3' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['go'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v2 34 | with: 35 | # We must fetch at least the immediate parents so that if this is 36 | # a pull request then we can checkout the head. 37 | fetch-depth: 2 38 | 39 | # If this run was triggered by a pull request event, then checkout 40 | # the head of the pull request instead of the merge commit. 41 | - run: git checkout HEAD^2 42 | if: ${{ github.event_name == 'pull_request' }} 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | on: pull_request 2 | name: Pull Request 3 | jobs: 4 | test: 5 | name: Run tests 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | - name: Test 10 | uses: skx/github-action-tester@master 11 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | name: Push Event 6 | jobs: 7 | test: 8 | name: Run tests 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@master 12 | - name: Test 13 | uses: skx/github-action-tester@master 14 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: release 2 | name: Handle Release 3 | jobs: 4 | upload: 5 | name: Upload release 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout the repository 9 | uses: actions/checkout@master 10 | - name: Generate the artifacts 11 | uses: skx/github-action-build@master 12 | - name: Upload the artifacts 13 | uses: skx/github-action-publish-binaries@master 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | with: 17 | args: bfcc-* 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bfcc 2 | a.out 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Simple Makefile, here to run the test-cases. 3 | # 4 | 5 | 6 | 7 | # Build the application 8 | build: 9 | go build . 10 | 11 | 12 | # Run the test-cases with both backends 13 | test: build test-asm test-c 14 | 15 | # Test the asm-backend. 16 | test-asm: 17 | @BACKEND=asm make test-implementation 18 | 19 | # Test the C-backend. 20 | test-c: 21 | @BACKEND=c make test-implementation 22 | 23 | 24 | # Actual test cases run here. 25 | # - For each file "examples/*.bf" 26 | # - Run the compiler with that input, and record the output 27 | # - Compare the output to that expected within the corresponding ".out" file. 28 | test-implementation: 29 | @for i in examples/*.bf; do \ 30 | echo -n "Running test [backend: $$BACKEND] $$i " ; \ 31 | nm=$$(basename $$i .bf) ;\ 32 | ./bfcc -backend=${BACKEND} $$i ; \ 33 | if [ -e examples/$${nm}.in ] ; then \ 34 | cat examples/$${nm}.in | ./a.out > x ; \ 35 | else \ 36 | ./a.out > x ;\ 37 | fi ;\ 38 | diff examples/$${nm}.out x || ( echo "Example failed: $$i"; exit 1 ) ;\ 39 | echo "OK" ; \ 40 | done 41 | 42 | time: 43 | make 44 | ./bfcc -backend=asm examples/mandelbrot.bf ; bash -c "time ./a.out" >/dev/null 45 | ./bfcc -backend=c examples/mandelbrot.bf ; bash -c "time ./a.out" >/dev/null 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/skx/bfcc) 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/skx/bfcc)](https://goreportcard.com/report/github.com/skx/bfcc) 3 | [![license](https://img.shields.io/github/license/skx/bfcc.svg)](https://github.com/skx/bfcc/blob/master/LICENSE) 4 | [![Release](https://img.shields.io/github/release/skx/bfcc.svg)](https://github.com/skx/bfcc/releases/latest) 5 | 6 | Table of Contents 7 | ================= 8 | 9 | * [BrainFuck Compiler Challenge](#brainfuck-compiler-challenge) 10 | * [Usage](#usage) 11 | * [My Approach](#my-approach) 12 | * [Test Programs](#test-programs) 13 | * [Debugging the generated program](#debugging-the-generated-program) 14 | * [Future Plans?](#future-plans) 15 | * [See Also](#see-also) 16 | * [Bug Reports?](#bug-reports) 17 | * [Github Setup](#github-setup) 18 | 19 | 20 | 21 | # BrainFuck Compiler Challenge 22 | 23 | I 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. 24 | 25 | [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. 26 | 27 | Brainfuck, despite the name, has a good history in the programming world as being something simple and fun to play with. 28 | 29 | 30 | 31 | ## Usage 32 | 33 | You can install the compiler via: 34 | 35 | $ go get github.com/skx/bfcc 36 | 37 | Once installed execute the compiler as follows to produce the default executable at `./a.out`: 38 | 39 | $ bfcc ./examples/mandelbrot.bf 40 | $ ./a.out 41 | 42 | Rather than compile, then run, you can add `-run` to your invocation: 43 | 44 | $ bfcc -run ./examples/bizzfuzz.bf 45 | 46 | Finally if you prefer you can specify an output name for the compiled result: 47 | 48 | $ bfcc [-run] ./examples/bizzfuzz.bf ./bf 49 | $ ./bf 50 | 51 | There are three backends included: 52 | 53 | * `asm` 54 | * Generates an assembly language source-file, and compiles with `gcc` 55 | * `c` 56 | * Generates C-code which is also compiled via `gcc`. 57 | * `interpreter` 58 | * This actually executes Brainfuck programs, and does zero compilation. 59 | 60 | 61 | By default the assembly-language backend is selected, because this is the thing that I was more interested in writing. 62 | 63 | You may use the `-backend` flag to specify the backend which you prefer to use: 64 | 65 | $ bfcc -backend=c ./examples/mandelbrot.bf ./mb-c 66 | $ bfcc -backend=asm ./examples/mandelbrot.bf ./mb-asm 67 | 68 | You'll see slightly difference sizes in the two executable: 69 | 70 | $ ls -lash mb-* 71 | 24K -rwxr-xr-x 1 skx skx 21K Jun 16 14:54 mb-asm 72 | 36K -rwxr-xr-x 1 skx skx 34K Jun 16 14:54 mb-c 73 | 74 | Both compiling-backends should produce binaries that are standalone, and work identically - if they do not that's a bug in the code-generation. 75 | 76 | The 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! 77 | 78 | $ ./bfcc -backend=interpreter ./examples/hello-world.bf 79 | Hello World! 80 | 81 | 82 | 83 | ## My Approach 84 | 85 | In the end it took me about four hours to get something I was happy with, and later I've improved it a little more: 86 | 87 | * Initially I generated C-code, [as you can see here](https://github.com/skx/bfcc/blob/cadb19d6c75a5febde56f53423a9668ee8f6bd25/main.go). 88 | * This code was largely inspired by the [Wikipedia brainfuck page](https://en.wikipedia.org/wiki/Brainfuck). 89 | * The C-code was compiled by GCC to produce an executable. 90 | * Then I started generating assembly-language code, which looked [something like this](https://github.com/skx/bfcc/blob/aebb14ccb548a2249bc32bb1f82fe9070518cc3c/main.go). 91 | * The generated assembly was compiled by `nasm`, and linked with `ld` to produce an executable. 92 | * Once the assembly language code was working I optimized it. 93 | * Collapsing multiple identical instructions to one, which looked [like this](https://github.com/skx/bfcc/blob/91d6712bcb4b41e9fd963f60da2753d62ee789d1/main.go). 94 | * This was buggy, but that wasn't obvious at the time! 95 | * Improving the way loop-handling was generated. 96 | * 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. 97 | * Finally I cleaned up and improved the code. 98 | * Adding a lexer in [#4](https://github.com/skx/bfcc/pull/4) 99 | * Allowing the generation of either C or assembly in [#6](https://github.com/skx/bfcc/pull/6), via the addition of a backend-abstraction. 100 | * Allow generating a breakpoint instruction (`int03`) when using the assembly-backend in [#7](https://github.com/skx/bfcc/pull/7). 101 | * Switched to generating assembly to be compiled by `gcc` rather than `nasm` [#8](https://github.com/skx/bfcc/pull/8). 102 | * Added an interpreter in [#11](https://github.com/skx/bfcc/pull/12). 103 | 104 | 105 | 106 | ## Test Programs 107 | 108 | There are a small collection of test-programs located beneath the [examples/](examples/) directory. 109 | 110 | Each example has a `.bf` suffix, and there is a corresponding output file for each input to show the expected output. 111 | 112 | You can run `make test` to run all the scripts, and compare their generated output with the expected result. 113 | 114 | 115 | 116 | ### Debugging the generated program 117 | 118 | If you run the compiler with the `-debug` flag, using the assembly-language 119 | backend, a breakpoint will be generated immediately at the start of the 120 | program. You can use that breakpoint to easily debug the generated binary 121 | via `gdb`. 122 | 123 | $ bfcc -debug ./examples/hello-world.bf 124 | 125 | Now you can launch that binary under `gdb`, and run it: 126 | 127 | $ gdb ./a.out 128 | (gdb) run 129 | .. 130 | Program received signal SIGTRAP, Trace/breakpoint trap. 131 | 0x00000000004000bb in _start () 132 | 133 | Disassemble 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. 134 | 135 | (gdb) disassemble 136 | Dump of assembler code for function _start: 137 | 0x00000000004000b0 <+0>: movabs $0x600290,%r8 138 | 0x00000000004000ba <+10>: int3 139 | => 0x00000000004000bb <+11>: addb $0x8,(%r8) 140 | 0x00000000004000bf <+15>: cmpb $0x0,(%r8) 141 | 0x00000000004000c3 <+19>: je 0x40013f 142 | End of assembler dump. 143 | 144 | You can set a breakpoint at a line in the future, and continue running till 145 | you hit it, with something like this: 146 | 147 | (gdb) break *0x00000000004000c3 148 | (gdb) cont 149 | 150 | Once there inspect the registers with commands like these two: 151 | 152 | (gdb) print $r8 153 | (gdb) print *$r8 154 | (gdb) info registers 155 | 156 | > **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`. 157 | 158 | Further documentation can be found in the `gdb` manual, which is worth reading 159 | if you've an interest in compilers, debuggers, and decompilers. 160 | 161 | 162 | 163 | 164 | ## Future Plans? 165 | 166 | Mostly none. 167 | 168 | More 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. 169 | 170 | 171 | 172 | 173 | ## See Also 174 | 175 | If you enjoyed this repository you might also enjoy my simple math-compiler, which converts postfix mathematical operations to x86-64 assembly-language: 176 | 177 | * [https://github.com/skx/math-compiler](https://github.com/skx/math-compiler) 178 | 179 | 180 | 181 | 182 | ## Bug Reports? 183 | 184 | Please [file an issue](https://github.com/skx/bfcc/issues) 185 | 186 | 187 | 188 | 189 | # Github Setup 190 | 191 | This repository is configured to run tests upon every commit, and when 192 | pull-requests are created/updated. The testing is carried out via 193 | [.github/run-tests.sh](.github/run-tests.sh) which is used by the 194 | [github-action-tester](https://github.com/skx/github-action-tester) action. 195 | 196 | Releases are automated in a similar fashion via [.github/build](.github/build), 197 | and the [github-action-publish-binaries](https://github.com/skx/github-action-publish-binaries) action. 198 | 199 | 200 | Steve 201 | -- 202 | -------------------------------------------------------------------------------- /examples/bizzfuzz.bf: -------------------------------------------------------------------------------- 1 | ++++++++++[>++++++++++<-]>>++++++++++>->>>>>>>>>>>>>>>>-->+++++++[->++ 2 | ++++++++<]>[->+>+>+>+<<<<]+++>>+++>>>++++++++[-<++++<++++<++++>>>]++++ 3 | +[-<++++<++++>>]>>-->++++++[->+++++++++++<]>[->+>+>+>+<<<<]+++++>>+>++ 4 | ++++>++++++>++++++++[-<++++<++++<++++>>>]++++++[-<+++<+++<+++>>>]>>--> 5 | ---+[-<+]-<[+[->+]-<<->>>+>[-]++[-->++]-->+++[---++[--<++]---->>-<+>[+ 6 | +++[----<++++]--[>]++[-->++]--<]>++[--+[-<+]->>[-]+++++[---->++++]-->[ 7 | ->+<]>>[.>]++[-->++]]-->+++]---+[-<+]->>-[+>>>+[-<+]->>>++++++++++<<[- 8 | >+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]> 9 | +>>]<<<<<]>[-]>>[>++++++[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->+++ 10 | +++++<]>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]+[-<+]->>]+[-]<<<.>>>+[ 11 | -<+]-<<] 12 | -------------------------------------------------------------------------------- /examples/bizzfuzz.out: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | Fizz 4 | 4 5 | Buzz 6 | Fizz 7 | 7 8 | 8 9 | Fizz 10 | Buzz 11 | 11 12 | Fizz 13 | 13 14 | 14 15 | FizzBuzz 16 | 16 17 | 17 18 | Fizz 19 | 19 20 | Buzz 21 | Fizz 22 | 22 23 | 23 24 | Fizz 25 | Buzz 26 | 26 27 | Fizz 28 | 28 29 | 29 30 | FizzBuzz 31 | 31 32 | 32 33 | Fizz 34 | 34 35 | Buzz 36 | Fizz 37 | 37 38 | 38 39 | Fizz 40 | Buzz 41 | 41 42 | Fizz 43 | 43 44 | 44 45 | FizzBuzz 46 | 46 47 | 47 48 | Fizz 49 | 49 50 | Buzz 51 | Fizz 52 | 52 53 | 53 54 | Fizz 55 | Buzz 56 | 56 57 | Fizz 58 | 58 59 | 59 60 | FizzBuzz 61 | 61 62 | 62 63 | Fizz 64 | 64 65 | Buzz 66 | Fizz 67 | 67 68 | 68 69 | Fizz 70 | Buzz 71 | 71 72 | Fizz 73 | 73 74 | 74 75 | FizzBuzz 76 | 76 77 | 77 78 | Fizz 79 | 79 80 | Buzz 81 | Fizz 82 | 82 83 | 83 84 | Fizz 85 | Buzz 86 | 86 87 | Fizz 88 | 88 89 | 89 90 | FizzBuzz 91 | 91 92 | 92 93 | Fizz 94 | 94 95 | Buzz 96 | Fizz 97 | 97 98 | 98 99 | Fizz 100 | Buzz 101 | -------------------------------------------------------------------------------- /examples/factor.bf: -------------------------------------------------------------------------------- 1 | * factor an arbitrarily large positive integer 2 | * 3 | * Copyright (C) 1999 by Brian Raiter 4 | * under the GNU General Public License 5 | 6 | >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>- 7 | 8 | * 9 | * read in the number 10 | * 11 | 12 | <<<<<<<<<+ 13 | [-[>>>>>>>>>>][-]<<<<<<<<<<[[->>>>>>>>>>+<<<<<<<<<<]<<<<<<<<<<] 14 | >>>>>>>>>>,----------] 15 | >>>>>>>>>>[------------------------------------->>>>>>>>>->] 16 | <[+>[>>>>>>>>>+>]<-<<<<<<<<<<]- 17 | 18 | * 19 | * display the number and initialize the loop variable to two 20 | * 21 | 22 | [>++++++++++++++++++++++++++++++++++++++++++++++++. 23 | ------------------------------------------------<<<<<<<<<<<] 24 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++. 25 | --------------------------.[-] 26 | >>>>>>>>>>>>++<<<<+ 27 | 28 | * 29 | * the main loop 30 | * 31 | 32 | [ [-]>> 33 | 34 | * 35 | * make copies of the number and the loop variable 36 | * 37 | 38 | [>>>>[-]>[-]>[-]>[-] 39 | >[-]>[-] 40 | <<<<<<<[->>>+>+<<<<]>>>>>>>>] 41 | <<<<<<<<<<[>>>>>>[-<<<<+>>>>]<<<<<<<<<<<<<<<<]>>>>>>>>>> 42 | [>[->>>+>>+<<<<<]>>>>>>>>>] 43 | <<<<<<<<<<[>>>>>>[-<<<<<+>>>>>]<<<<<<<<<<<<<<<<]>>>>>>>>>> 44 | 45 | * 46 | * divide the number by the loop variable 47 | * 48 | 49 | [>>>[-]>>>[-]>[-]>>>] initialize 50 | <<<<<<<<<<[<<<<<<<<<<] 51 | >>>>>>>>>[-]>>>>>>>+<<<<<<<<[+]+ 52 | [ ->> double divisor until above dividend 53 | [>>>>>>[->++<]>>>>]<<<<<<<<<< 54 | [>>>>>>>>[-]>[-] 55 | <<<<[->>>++<<<]<<<<<<<<<<<<<<<]>>>>>>>>>> 56 | [>>>>>>>>[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+< 57 | [->--------->>>>>>>>>+<<<<<<<<<<[->+<]]]]]]]]]]]>>] 58 | <<<<<<<<<<[>>>>>>>>>[-<+<<<+>>>>]<<<<<<<<<<<<<<<<<<<]>>>>>>>>>> 59 | [>>>>>>>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+>[-<+> 60 | [-<--------->>>>>>>>>>>+<<<<<<<<<<[-<+>]]]]]]]]]]]>>>] 61 | <<<<<<<<<< 62 | [>>>>[->>>+>>+<<<<<]<<<<<<<<<<<<<<] 63 | >>>>>>>>>>[>>>>>>>[-<<<+>>>]>>>]<<<<<<<<<< 64 | [>>>>>>>>[->-<]> 65 | [<<<<<<<<<[<[-]>>>>>>>>>>[-<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<] 66 | >>>>>>>>>>>>>>>>>>>] 67 | <<<<<<<<<<<<<<<<<<<] 68 | >>>>>>>>>[+[+[+[+[+[+[+[+[+[+[[-]<+>]]]]]]]]]]]< 69 | ] 70 | >>>>>>>> 71 | [ subtract divisor from dividend 72 | <<<<<< 73 | [>>>>>>>>[-]>[-]<<<<<[->>>+>+<<<<]>>>>>>]<<<<<<<<<< 74 | [>>>>>>>>[-<<<<+>>>>]<<<[->>>+>+<<<<]<<<<<<<<<<<<<<<]>>>>>>>>>> 75 | [>>>>>>>>>[-<<<<+>>>>]>]<<<<<<<<<< 76 | [>>>>>>>>[-<->]<<<<<<<<<<<<<<<<<<]>>>>>>>>>> 77 | [>>>>>>>[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+< 78 | [++++++++++[+>-<]>>>>>>>>>>-<<<<<<<<<<]]]]]]]]]]]>>>] 79 | >>>>>>>+ 80 | [ if difference is nonnegative then 81 | [-]<<<<<<<<<<<<<<<<< replace dividend and increment quotient 82 | [>>>>[-]>>>>[-<<<<+>>>>]<<[->>+<<]<<<<<<<<<<<<<<<<]>>>>>>>>>> 83 | [>>>>>>>>[->+<<<+>>]>>]<<<<<<<<<< 84 | [>>>[->>>>>>+<<<<<<]<<<<<<<<<<<<<]>>>>>>>>>> 85 | [>>>>>>>>>[-<<<<<<+>>>>>>[-<<<<<<+>>>>>> 86 | [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> 87 | [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> 88 | [-<<<<<<+>>>>>>[-<<<<<<+>>>>>> 89 | [-<<<<<<+>>>>>>[-<<<<<<--------->>>>>>>>>>>>>>>>+<<<<<<<<<< 90 | [-<<<<<<+>>>>>>]]]]]]]]]]]>] 91 | >>>>>>> 92 | ] halve divisor and loop until zero 93 | <<<<<<<<<<<<<<<<<[<<<<<<<<<<]>>>>>>>>>> 94 | [>>>>>>>>[-]<<[->+<]<[->>>+<<<]>>>>>]<<<<<<<<<< 95 | [+>>>>>>>[-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> 96 | [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> 97 | [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> 98 | [-<<<<<<<+>>>>>>>[-<<<<<<<->>>>>>+> 99 | [-<<<<<<<+>>>>>>>]]]]]]]]]<<<<<<< 100 | [->>>>>>>+<<<<<<<]-<<<<<<<<<<] 101 | >>>>>>> 102 | [-<<<<<<<<<<<+>>>>>>>>>>>] 103 | >>>[>>>>>>>[-<<<<<<<<<<<+++++>>>>>>>>>>>]>>>]<<<<<<<<<< 104 | [+>>>>>>>>[-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> 105 | [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> 106 | [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> 107 | [-<<<<<<<<+>>>>>>>>[-<<<<<<<<->>>>>+>>> 108 | [-<<<<<<<<+>>>>>>>>]]]]]]]]]<<<<<<<< 109 | [->>>>>>>>+<<<<<<<<]-<<<<<<<<<<] 110 | >>>>>>>>[-<<<<<<<<<<<<<+>>>>>>>>>>>>>]>> 111 | [>>>>>>>>[-<<<<<<<<<<<<<+++++>>>>>>>>>>>>>]>>]<<<<<<<<<< 112 | [<<<<<<<<<<]>>>>>>>>>> 113 | >>>>>> 114 | ] 115 | <<<<<< 116 | 117 | * 118 | * make copies of the loop variable and the quotient 119 | * 120 | 121 | [>>>[->>>>+>+<<<<<]>>>>>>>] 122 | <<<<<<<<<< 123 | [>>>>>>>[-<<<<+>>>>]<<<<<[->>>>>+>>+<<<<<<<]<<<<<<<<<<<<] 124 | >>>>>>>>>>[>>>>>>>[-<<<<<+>>>>>]>>>]<<<<<<<<<< 125 | 126 | * 127 | * break out of the loop if the quotient is larger than the loop variable 128 | * 129 | 130 | [>>>>>>>>>[-<->]< 131 | [<<<<<<<< 132 | [<<[-]>>>>>>>>>>[-<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<<<<<<<<<] 133 | >>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<] 134 | >>>>>>>>[>-<[+[+[+[+[+[+[+[+[+[[-]>+<]]]]]]]]]]]>+ 135 | 136 | [ [-] 137 | 138 | * 139 | * partially increment the loop variable 140 | * 141 | 142 | <[-]+>>>>+>>>>>>>>[>>>>>>>>>>]<<<<<<<<<< 143 | 144 | * 145 | * examine the remainder for nonzero digits 146 | * 147 | 148 | [<<<<<<[<<<<[<<<<<<<<<<]>>>>+<<<<<<<<<<]<<<<] 149 | >>>>>>>>>>>>>>>>>>>>[>>>>>>>>>>]<<<<<<<<<<[<<<<<<<<<<] 150 | >>>>- 151 | 152 | [ [+] 153 | 154 | * 155 | * decrement the loop variable and replace the number with the quotient 156 | * 157 | 158 | >>>>>>>>-<<[>[-]>>[-<<+>>]>>>>>>>]<<<<<<<<<< 159 | 160 | * 161 | * display the loop variable 162 | * 163 | 164 | [+>>[>>>>>>>>+>>]<<-<<<<<<<<<<]- 165 | [>>++++++++++++++++++++++++++++++++++++++++++++++++. 166 | ------------------------------------------------<<<<<<<<<<<<] 167 | ++++++++++++++++++++++++++++++++.[-]>>>> 168 | 169 | ] 170 | 171 | * 172 | * normalize the loop variable 173 | * 174 | 175 | >>>>>> 176 | [>>[->>>>>+<<<<<[->>>>>+<<<<< 177 | [->>>>>+<<<<<[->>>>>+<<<<< 178 | [->>>>>+<<<<<[->>>>>+<<<<< 179 | [->>>>>+<<<<<[->>>>>+<<<<< 180 | [->>>>>+<<<<<[->>>>>--------->>>>>+<<<<<<<<<< 181 | [->>>>>+<<<<<]]]]]]]]]]]>>>>>>>>] 182 | <<<<<<<<<<[>>>>>>>[-<<<<<+>>>>>]<<<<<<<<<<<<<<<<<] 183 | >>>>>>>>> 184 | 185 | ]< 186 | 187 | ]>> 188 | 189 | * 190 | * display the number and end 191 | * 192 | 193 | [>>>>>>>>>>]<<<<<<<<<<[+>[>>>>>>>>>+>]<-<<<<<<<<<<]- 194 | [>++++++++++++++++++++++++++++++++++++++++++++++++.<<<<<<<<<<<] 195 | ++++++++++. 196 | -------------------------------------------------------------------------------- /examples/factor.in: -------------------------------------------------------------------------------- 1 | 4098 2 | -------------------------------------------------------------------------------- /examples/factor.out: -------------------------------------------------------------------------------- 1 | 4098: 2 3 683 2 | -------------------------------------------------------------------------------- /examples/fibonacci.bf: -------------------------------------------------------------------------------- 1 | +++++++++++ 2 | >+>>>>++++++++++++++++++++++++++++++++++++++++++++ 3 | >++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+> 4 | +<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[- 5 | <-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<< 6 | -]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]] 7 | >[<<+>>[-]]<<<<<<<]>>>>>[+++++++++++++++++++++++++ 8 | +++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++ 9 | ++++++++++++++++++++++++++++++++++++++++++++.[-]<< 10 | <<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<< 11 | [-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-] 12 | -------------------------------------------------------------------------------- /examples/fibonacci.out: -------------------------------------------------------------------------------- 1 | 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 -------------------------------------------------------------------------------- /examples/hello-world.bf: -------------------------------------------------------------------------------- 1 | ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. 2 | -------------------------------------------------------------------------------- /examples/hello-world.out: -------------------------------------------------------------------------------- 1 | Hello World! 2 | -------------------------------------------------------------------------------- /examples/mandelbrot.bf: -------------------------------------------------------------------------------- 1 | +++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[ 2 | >>>>>>>>>]+[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-]>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+ 3 | <<<<<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>>+>>>>>>>>>>>>>>>>>>>>>>>>>> 4 | >+<<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+[>>>>>>[>>>>>>>[-]>>]<<<<<<<<<[<<<<<<<<<]>> 5 | >>>>>[-]+<<<<<<++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<+++++++[-[->>> 6 | >>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[[-]>>>>>>[>>>>> 7 | >>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>> 8 | [>>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<< 9 | <<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>+++++++++++++++[[ 10 | >>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[ 11 | >+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[ 12 | -<<+>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<< 13 | <<[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<< 14 | [>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>> 15 | >>>>[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+ 16 | <<<<<<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>> 17 | >>>>>>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<< 18 | +>>>>>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<< 19 | <]<+<<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>> 20 | >>>>>>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>> 21 | >>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<< 22 | <<<]>>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<< 23 | <<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[-> 24 | >>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<< 25 | <<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]<<<<<<<[->+>>>-<<<<]>>>>>>>>>+++++++++++++++++++ 26 | +++++++>>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>[<<<<<<<+<[-<+>>>>+<<[-]]>[-<<[->+>>>- 27 | <<<<]>>>]>>>>>>>>>>>>>[>>[-]>[-]>[-]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>>>>>>[>>>>> 28 | [-<<<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>[-<<<<<<<< 29 | <+>>>>>>>>>]>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>>>]+>[- 30 | ]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>>>>>>>>]<<< 31 | <<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+>>]< 32 | <[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[->>>> 33 | >>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-]<->>> 34 | [-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[>>>>>>[-< 35 | <<<<+>>>>>]<<<<<[->>>>>+<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>+>>>>>>>> 36 | ]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+ 37 | >>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[> 38 | [->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[- 39 | ]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>> 40 | [>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 41 | ]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+> 42 | >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>++++++++ 43 | +++++++[[>>>>>>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-<<<<<<<+ 44 | >>>>>>>]<<<<<<<[->>>>>>>+<<<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[ 45 | -]>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>>>]>[-<<<<<<[->>>>>+<++<<<<]>>>>>[-< 46 | <<<<+>>>>>]<->+>]<[->+<]<<<<<[->>>>>+<<<<<]>>>>>>[-]<<<<<<+>>>>[-<<<<->>>>]+<<<< 47 | [->>>>->>>>>[>>[-<<->>]+<<[->>->[-<<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-] 48 | +>>>>>>[>>>>>>>>>]>+<]]+>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<<<<<<<<<<<[<<<<< 49 | <<<<]>>>>[-]+>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<< 50 | [<<<<<<<<<]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<< 51 | <<<+<[>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<< 52 | <<<<<+>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<< 53 | <<<<<<<<<<]>>>>[-]<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+<]>>>>>>>>]<<< 54 | <<<<<+<[>[->>>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>[->>>>+<<<<]>]<[->>>>-<<<<<<< 55 | <<<<<<<+>>>>>>>>>>]<]>>[->>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>>+<<<< 56 | ]<<<<<<<<<<<]>>>>>>+<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>>>>>>>>>]<<<<<<<<< 57 | [>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<<<<<<< 58 | +>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<<<<<<< 59 | <<<<<]]>[-]>>[-]>[-]>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-< 60 | <<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[ 61 | [>>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+ 62 | [>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->> 63 | [-<<+>>]<<[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<< 64 | <[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[ 65 | >[-]<->>>[-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[ 66 | >>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]> 67 | >>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>[-]>>>>+++++++++++++++[[>>>>>>>>>]<<<<<<<<<-<<<<< 68 | <<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<< 69 | <<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[- 70 | <<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>> 71 | >>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>>> 72 | [-<<<->>>]<<<[->>>+<<<]>>>>>>>>]<<<<<<<<+<[>[->+>[-<-<<<<<<<<<<+>>>>>>>>>>>>[-<< 73 | +>>]<]>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<<<]>>[-<+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<]> 74 | [-<<+>>]<<<<<<<<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>> 75 | >>>>>>]<<<<<<<<+<[>[->+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>[-<+>]>]<[-<-<<<<<<<<<<+>>>> 76 | >>>>>>>]<<]>>>[-<<+>[-<-<<<<<<<<<<+>>>>>>>>>>>]>]<[-<+>]<<<<<<<<<<<<]>>>>>+<<<<< 77 | ]>>>>>>>>>[>>>[-]>[-]>[-]>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>>>[-<<<<< 78 | <+>>>>>>]<<<<<<[->>>>>>+<<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>> 79 | >]>>[-<<<<<<<[->>>>>+<++<<<<]>>>>>[-<<<<<+>>>>>]<->+>>]<<[->>+<<]<<<<<[->>>>>+<< 80 | <<<]+>>>>[-<<<<->>>>]+<<<<[->>>>->>>>>[>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<< 81 | <<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>[-<<->>]+<<[->>->[-<<<+>>>]< 82 | <<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]< 83 | <<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+ 84 | <]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>[->>>+<<<]>]<[->>>- 85 | <<<<<<<<<<<<<+>>>>>>>>>>]<]>>[->>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>+<<< 86 | ]<<<<<<<<<<<]>>>>>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]]>>>>[-<<<<+> 87 | >>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<<[->>>- 88 | <<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<<]>[->>>+<<[ 89 | ->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<<<<<<]]>>>>[-]<<<<]>>>>[-<<<<+>> 90 | >>]<<<<[->>>>+>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>[>>>>>> 91 | >>>]<<<<<<<<<[>[->>>>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<< 92 | <<<<<+>>>>>>>>>>>]<<]>[->>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<< 93 | <<<<]]>>>>>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>[-<<<<+ 94 | >>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<+>>>>> 95 | ]<<<<<[->>>>>+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>> 96 | >>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>> 97 | >>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[-<<+ 98 | >>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[> 99 | [->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[- 100 | ]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>> 101 | [>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<< 102 | <<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>> 103 | >>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<<+>>> 104 | >>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+ 105 | <<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>> 106 | >>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>> 107 | >]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<<<<<] 108 | >>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<<<<<< 109 | ]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[->>>+< 110 | <<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]> 111 | >>>>>>>]<<<<<<<<<[<<<<<<<<<]>>->>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>]<<+>>>>[-<<<< 112 | ->>>>]+<<<<[->>>>-<<<<<<.>>]>>>>[-<<<<<<<.>>>>>>>]<<<[-]>[-]>[-]>[-]>[-]>[-]>>>[ 113 | >[-]>[-]>[-]>[-]>[-]>[-]>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-]>>>>]<<<<<<<<< 114 | [<<<<<<<<<]>+++++++++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+>>>>>>>>>+<<<<<<<< 115 | <<<<<<[<<<<<<<<<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+[-]>>[>>>>>>>>>]<<<<< 116 | <<<<[>>>>>>>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<[<<<<<<<<<]>>>>>>>[-]+>>>]<<<< 117 | <<<<<<]]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+>>[>+>>>>[-<<<<->>>>]<<<<[->>> 118 | >+<<<<]>>>>>>>>]<<+<<<<<<<[>>>>>[->>+<<]<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<< 119 | <<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<<<<<<[->>>>>>+<<<<<<]< 120 | +<<<<<<<<<]>>>>>>>-<<<<[-]+<<<]+>>>>>>>[-<<<<<<<->>>>>>>]+<<<<<<<[->>>>>>>->>[>> 121 | >>>[->>+<<]>>>>]<<<<<<<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<< 122 | <<<<[->>>>>>+<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+<<< 123 | <<[<<<<<<<<<]>>>>>>>>>[>>>>>[-<<<<<->>>>>]+<<<<<[->>>>>->>[-<<<<<<<+>>>>>>>]<<<< 124 | <<<[->>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>[-< 125 | <<<<<<->>>>>>>]+<<<<<<<[->>>>>>>-<<[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<<<<<<<<<[<<< 126 | <<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<< 127 | <<[<<<<<<<<<]>>>>[-]<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>-<<<<<[<<<<<<< 128 | <<]]>>>]<<<<.>>>>>>>>>>[>>>>>>[-]>>>]<<<<<<<<<[<<<<<<<<<]>++++++++++[-[->>>>>>>> 129 | >+<<<<<<<<<]>>>>>>>>>]>>>>>+>>>>>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-<<<<<< 130 | <<+>>>>>>>>]<<<<<<<<[->>>>>>>>+[-]>[>>>>>>>>>]<<<<<<<<<[>>>>>>>>[-<<<<<<<+>>>>>> 131 | >]<<<<<<<[->>>>>>>+<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+>>]<<<<<<<<<<]]>>>>>>>>[-<<<<< 132 | <<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+>[>+>>>>>[-<<<<<->>>>>]<<<<<[->>>>>+<<<<<]>>>>>> 133 | >>]<+<<<<<<<<[>>>>>>[->>+<<]<<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[-]<- 134 | >>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]<+<<<<<< 135 | <<<]>>>>>>>>-<<<<<[-]+<<<]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>>->[>>> 136 | >>>[->>+<<]>>>]<<<<<<<<<[>[-]<->>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<< 137 | <<<<<[->>>>>>>+<<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>> 138 | +>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<<->>>>>>]+< 139 | <<<<<[->>>>>>->>[-<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<<<<<<<<<<[<<<<<<< 140 | <<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>> 141 | -<<[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>> 142 | >>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>[-]<<<++++ 143 | +[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>->>>>>>>>>>>>>>>>>>>>>>>>>>>-<<<<<<[<<<< 144 | <<<<<]]>>>] 145 | -------------------------------------------------------------------------------- /examples/mandelbrot.out: -------------------------------------------------------------------------------- 1 | AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDEGFFEEEEDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 2 | AAAAAAAAAAAAAAABBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDEEEFGIIGFFEEEDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBB 3 | AAAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDEEEEFFFI KHGGGHGEDDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBB 4 | AAAAAAAAAAAABBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEEFFGHIMTKLZOGFEEDDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBB 5 | AAAAAAAAAAABBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEEEFGGHHIKPPKIHGFFEEEDDDDDDDDDCCCCCCCCCCBBBBBBBBBBBBBBBBBB 6 | AAAAAAAAAABBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEEFFGHIJKS X KHHGFEEEEEDDDDDDDDDCCCCCCCCCCBBBBBBBBBBBBBBBB 7 | AAAAAAAAABBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEEFFGQPUVOTY ZQL[MHFEEEEEEEDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBB 8 | AAAAAAAABBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEFFFFFGGHJLZ UKHGFFEEEEEEEEDDDDDCCCCCCCCCCCCBBBBBBBBBBBB 9 | AAAAAAABBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEFFFFFFGGGGHIKP KHHGGFFFFEEEEEEDDDDDCCCCCCCCCCCBBBBBBBBBBB 10 | AAAAAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDEEEEEFGGHIIHHHHHIIIJKMR VMKJIHHHGFFFFFFGSGEDDDDCCCCCCCCCCCCBBBBBBBBB 11 | AAAAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDEEEEEEFFGHK MKJIJO N R X YUSR PLV LHHHGGHIOJGFEDDDCCCCCCCCCCCCBBBBBBBB 12 | AAAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDEEEEEEEEEFFFFGH O TN S NKJKR LLQMNHEEDDDCCCCCCCCCCCCBBBBBBB 13 | AAAAABBCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDEEEEEEEEEEEEFFFFFGHHIN Q UMWGEEEDDDCCCCCCCCCCCCBBBBBB 14 | AAAABBCCCCCCCCCCCCCCCCCCCCCCCCCDDDDEEEEEEEEEEEEEEEFFFFFFGHIJKLOT [JGFFEEEDDCCCCCCCCCCCCCBBBBB 15 | AAAABCCCCCCCCCCCCCCCCCCCCCCDDDDEEEEEEEEEEEEEEEEFFFFFFGGHYV RQU QMJHGGFEEEDDDCCCCCCCCCCCCCBBBB 16 | AAABCCCCCCCCCCCCCCCCCDDDDDDDEEFJIHFFFFFFFFFFFFFFGGGGGGHIJN JHHGFEEDDDDCCCCCCCCCCCCCBBB 17 | AAABCCCCCCCCCCCDDDDDDDDDDEEEEFFHLKHHGGGGHHMJHGGGGGGHHHIKRR UQ L HFEDDDDCCCCCCCCCCCCCCBB 18 | AABCCCCCCCCDDDDDDDDDDDEEEEEEFFFHKQMRKNJIJLVS JJKIIIIIIJLR YNHFEDDDDDCCCCCCCCCCCCCBB 19 | AABCCCCCDDDDDDDDDDDDEEEEEEEFFGGHIJKOU O O PR LLJJJKL OIHFFEDDDDDCCCCCCCCCCCCCCB 20 | AACCCDDDDDDDDDDDDDEEEEEEEEEFGGGHIJMR RMLMN NTFEEDDDDDDCCCCCCCCCCCCCB 21 | AACCDDDDDDDDDDDDEEEEEEEEEFGGGHHKONSZ QPR NJGFEEDDDDDDCCCCCCCCCCCCCC 22 | ABCDDDDDDDDDDDEEEEEFFFFFGIPJIIJKMQ VX HFFEEDDDDDDCCCCCCCCCCCCCC 23 | ACDDDDDDDDDDEFFFFFFFGGGGHIKZOOPPS HGFEEEDDDDDDCCCCCCCCCCCCCC 24 | ADEEEEFFFGHIGGGGGGHHHHIJJLNY TJHGFFEEEDDDDDDDCCCCCCCCCCCCC 25 | A PLJHGGFFEEEDDDDDDDCCCCCCCCCCCCC 26 | ADEEEEFFFGHIGGGGGGHHHHIJJLNY TJHGFFEEEDDDDDDDCCCCCCCCCCCCC 27 | ACDDDDDDDDDDEFFFFFFFGGGGHIKZOOPPS HGFEEEDDDDDDCCCCCCCCCCCCCC 28 | ABCDDDDDDDDDDDEEEEEFFFFFGIPJIIJKMQ VX HFFEEDDDDDDCCCCCCCCCCCCCC 29 | AACCDDDDDDDDDDDDEEEEEEEEEFGGGHHKONSZ QPR NJGFEEDDDDDDCCCCCCCCCCCCCC 30 | AACCCDDDDDDDDDDDDDEEEEEEEEEFGGGHIJMR RMLMN NTFEEDDDDDDCCCCCCCCCCCCCB 31 | AABCCCCCDDDDDDDDDDDDEEEEEEEFFGGHIJKOU O O PR LLJJJKL OIHFFEDDDDDCCCCCCCCCCCCCCB 32 | AABCCCCCCCCDDDDDDDDDDDEEEEEEFFFHKQMRKNJIJLVS JJKIIIIIIJLR YNHFEDDDDDCCCCCCCCCCCCCBB 33 | AAABCCCCCCCCCCCDDDDDDDDDDEEEEFFHLKHHGGGGHHMJHGGGGGGHHHIKRR UQ L HFEDDDDCCCCCCCCCCCCCCBB 34 | AAABCCCCCCCCCCCCCCCCCDDDDDDDEEFJIHFFFFFFFFFFFFFFGGGGGGHIJN JHHGFEEDDDDCCCCCCCCCCCCCBBB 35 | AAAABCCCCCCCCCCCCCCCCCCCCCCDDDDEEEEEEEEEEEEEEEEFFFFFFGGHYV RQU QMJHGGFEEEDDDCCCCCCCCCCCCCBBBB 36 | AAAABBCCCCCCCCCCCCCCCCCCCCCCCCCDDDDEEEEEEEEEEEEEEEFFFFFFGHIJKLOT [JGFFEEEDDCCCCCCCCCCCCCBBBBB 37 | AAAAABBCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDEEEEEEEEEEEEFFFFFGHHIN Q UMWGEEEDDDCCCCCCCCCCCCBBBBBB 38 | AAAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDEEEEEEEEEFFFFGH O TN S NKJKR LLQMNHEEDDDCCCCCCCCCCCCBBBBBBB 39 | AAAAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDEEEEEEFFGHK MKJIJO N R X YUSR PLV LHHHGGHIOJGFEDDDCCCCCCCCCCCCBBBBBBBB 40 | AAAAAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDEEEEEFGGHIIHHHHHIIIJKMR VMKJIHHHGFFFFFFGSGEDDDDCCCCCCCCCCCCBBBBBBBBB 41 | AAAAAAABBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEFFFFFFGGGGHIKP KHHGGFFFFEEEEEEDDDDDCCCCCCCCCCCBBBBBBBBBBB 42 | AAAAAAAABBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEFFFFFGGHJLZ UKHGFFEEEEEEEEDDDDDCCCCCCCCCCCCBBBBBBBBBBBB 43 | AAAAAAAAABBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEEFFGQPUVOTY ZQL[MHFEEEEEEEDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBB 44 | AAAAAAAAAABBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEEFFGHIJKS X KHHGFEEEEEDDDDDDDDDCCCCCCCCCCBBBBBBBBBBBBBBBB 45 | AAAAAAAAAAABBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEEEFGGHHIKPPKIHGFFEEEDDDDDDDDDCCCCCCCCCCBBBBBBBBBBBBBBBBBB 46 | AAAAAAAAAAAABBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEEFFGHIMTKLZOGFEEDDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBB 47 | AAAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDEEEEFFFI KHGGGHGEDDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBB 48 | AAAAAAAAAAAAAAABBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDEEEFGIIGFFEEEDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBB 49 | -------------------------------------------------------------------------------- /examples/quine.bf: -------------------------------------------------------------------------------- 1 | ->+>+++>>+>++>+>+++>>+>++>>>+>+>+>++>+>>>>+++>+>>++>+>+++>>++>++>>+>>+>++>++>+>>>>+++>+>>>>++>++>>>>+>>++>+>+++>>>++>>++++++>>+>>++>+>>>>+++>>+++++>>+>+++>>>++>>++>>+>>++>+>+++>>>++>>+++++++++++++>>+>>++>+>+++>+>+++>>>++>>++++>>+>>++>+>>>>+++>>+++++>>>>++>>>>+>+>++>>+++>+>>>>+++>+>>>>+++>+>>>>+++>>++>++>+>+++>+>++>++>>>>>>++>+>+++>>>>>+++>>>++>+>+++>+>+>++>>>>>>++>>>+>>>++>+>>>>+++>+>>>+>>++>+>++++++++++++++++++>>>>+>+>>>+>>++>+>+++>>>++>>++++++++>>+>>++>+>>>>+++>>++++++>>>+>++>>+++>+>+>++>+>+++>>>>>+++>>>+>+>>++>+>+++>>>++>>++++++++>>+>>++>+>>>>+++>>++++>>+>+++>>>>>>++>+>+++>>+>++>>>>+>+>++>+>>>>+++>>+++>>>+[[->>+<<]<+]+++++[->+++++++++<]>.[+]>>[<<+++++++[->+++++++++<]>-.------------------->-[-<.<+>>]<[+]<+>>>]<<<[-[-[-[>>+<++++++[->+++++<]]>++++++++++++++<]>+++<]++++++[->+++++++<]>+<<<-[->>>++<<<]>[->>.<<]<<] -------------------------------------------------------------------------------- /examples/quine.out: -------------------------------------------------------------------------------- 1 | ->+>+++>>+>++>+>+++>>+>++>>>+>+>+>++>+>>>>+++>+>>++>+>+++>>++>++>>+>>+>++>++>+>>>>+++>+>>>>++>++>>>>+>>++>+>+++>>>++>>++++++>>+>>++>+>>>>+++>>+++++>>+>+++>>>++>>++>>+>>++>+>+++>>>++>>+++++++++++++>>+>>++>+>+++>+>+++>>>++>>++++>>+>>++>+>>>>+++>>+++++>>>>++>>>>+>+>++>>+++>+>>>>+++>+>>>>+++>+>>>>+++>>++>++>+>+++>+>++>++>>>>>>++>+>+++>>>>>+++>>>++>+>+++>+>+>++>>>>>>++>>>+>>>++>+>>>>+++>+>>>+>>++>+>++++++++++++++++++>>>>+>+>>>+>>++>+>+++>>>++>>++++++++>>+>>++>+>>>>+++>>++++++>>>+>++>>+++>+>+>++>+>+++>>>>>+++>>>+>+>>++>+>+++>>>++>>++++++++>>+>>++>+>>>>+++>>++++>>+>+++>>>>>>++>+>+++>>+>++>>>>+>+>++>+>>>>+++>>+++>>>+[[->>+<<]<+]+++++[->+++++++++<]>.[+]>>[<<+++++++[->+++++++++<]>-.------------------->-[-<.<+>>]<[+]<+>>>]<<<[-[-[-[>>+<++++++[->+++++<]]>++++++++++++++<]>+++<]++++++[->+++++++<]>+<<<-[->>>++<<<]>[->>.<<]<<] -------------------------------------------------------------------------------- /generators/asm.go: -------------------------------------------------------------------------------- 1 | package generators 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | 10 | "github.com/skx/bfcc/lexer" 11 | ) 12 | 13 | // GeneratorASM is a generator that will produce an x86-64 assembly-language 14 | // version of the specified input-program. 15 | // 16 | // The assembly language file will be compiled by gcc. 17 | type GeneratorASM struct { 18 | 19 | // input source 20 | input string 21 | 22 | // file to write to 23 | output string 24 | } 25 | 26 | // generateSource produces a version of the program as X86-64 assembly language. 27 | func (g *GeneratorASM) generateSource() error { 28 | var buff bytes.Buffer 29 | var programStart = ` 30 | .intel_syntax noprefix 31 | .global _start 32 | 33 | write_to_stdout: 34 | mov %rax, 1 35 | mov %rdi, 1 36 | mov %rsi, %r8 37 | mov %rdx, 1 38 | syscall 39 | ret 40 | 41 | read_from_stdin: 42 | mov %rax, 0 43 | mov %rdi, 0 44 | mov %rsi, %r8 45 | mov %rdx, 1 46 | syscall 47 | ret 48 | 49 | _start: 50 | lea %r8, stack 51 | ` 52 | buff.WriteString(programStart) 53 | 54 | // 55 | // Should we generate a debug-breakpoint? 56 | // 57 | debug := os.Getenv("DEBUG") 58 | if debug == "1" { 59 | buff.WriteString(" int3\n") 60 | } 61 | 62 | // 63 | // Keep track of "[" here. 64 | // 65 | // These are loop opens. 66 | // 67 | opens := []int{} 68 | 69 | // 70 | // Create a lexer for the input program 71 | // 72 | l := lexer.New(g.input) 73 | 74 | // 75 | // Program consists of all tokens 76 | // 77 | program := l.Tokens() 78 | 79 | // 80 | // We keep track of the loop-labels here. 81 | // 82 | // Each time we see a new loop-open "[" we bump this 83 | // by one. 84 | // 85 | i := 0 86 | 87 | // 88 | // We'll process the complete program until 89 | // we hit an end of file/input 90 | // 91 | offset := 0 92 | for offset < len(program) { 93 | 94 | // 95 | // The current token 96 | // 97 | tok := program[offset] 98 | 99 | // 100 | // Output different things depending on the token-type 101 | // 102 | switch tok.Type { 103 | 104 | case lexer.INC_PTR: 105 | buff.WriteString(fmt.Sprintf(" add %%r8, %d\n", tok.Repeat)) 106 | 107 | case lexer.DEC_PTR: 108 | buff.WriteString(fmt.Sprintf(" sub %%r8, %d\n", tok.Repeat)) 109 | 110 | case lexer.INC_CELL: 111 | buff.WriteString(fmt.Sprintf(" add byte ptr [%%r8], %d\n", tok.Repeat)) 112 | 113 | case lexer.DEC_CELL: 114 | buff.WriteString(fmt.Sprintf(" sub byte ptr [%%r8], %d\n", tok.Repeat)) 115 | 116 | case lexer.OUTPUT: 117 | buff.WriteString(" call write_to_stdout\n") 118 | case lexer.INPUT: 119 | buff.WriteString(" call read_from_stdin\n") 120 | case lexer.LOOP_OPEN: 121 | 122 | // 123 | // We sneekily optimize "[-]" by converting it 124 | // into "move register, 0" 125 | // 126 | // Since this involves looking at future-tokens 127 | // we need to make sure we're not at the end of 128 | // the program. 129 | // 130 | if offset+2 < len(program) { 131 | 132 | // 133 | // Look for the next two tokens "-]", if 134 | // we find them then we're looking at "[-]" 135 | // which is something we can optimize. 136 | // 137 | if program[offset+1].Type == lexer.DEC_CELL && 138 | program[offset+2].Type == lexer.LOOP_CLOSE { 139 | // register == zero 140 | buff.WriteString(" mov byte ptr [%r8], 0\n\n") 141 | 142 | // 1. Skip this instruction, 143 | // 2. the next one "-" 144 | // 3. and the final one "]" 145 | offset += 3 146 | 147 | // And continue the loop again. 148 | continue 149 | } 150 | } 151 | 152 | // 153 | // Open of a block. 154 | // 155 | // If the index-value is zero then jump to the 156 | // end of the while-loop. 157 | // 158 | // NOTE: We repeat the test at the end of the 159 | // loop so the label here is AFTER our condition 160 | // 161 | i++ 162 | buff.WriteString(" cmp byte ptr [%r8], 0\n") 163 | buff.WriteString(fmt.Sprintf(" je close_loop_%d\n", i)) 164 | buff.WriteString(fmt.Sprintf("label_loop_%d:\n", i)) 165 | opens = append(opens, i) 166 | 167 | case lexer.LOOP_CLOSE: 168 | 169 | // "]" can only follow an "[". 170 | // 171 | // Every time we see a "[" we save the ID onto a 172 | // temporary stack. So we're gonna go back to the 173 | // most recent open. 174 | // 175 | // This will cope with nesting. 176 | // 177 | if len(opens) < 1 { 178 | fmt.Printf("close before open. bug? bogus program?\n") 179 | os.Exit(1) 180 | } 181 | 182 | // 183 | // Get the last label-ID 184 | // 185 | last := opens[len(opens)-1] 186 | 187 | // 188 | // Remove it from our list now. 189 | // 190 | opens = opens[:len(opens)-1] 191 | 192 | // 193 | // What we could do here is jump back to the 194 | // start of our loop. 195 | // 196 | // The test would be made, and if it failed we'd 197 | // end up back at the end of the loop. 198 | // 199 | // However we're tricksy hobbitses, so we run 200 | // the test again, and only jump back if the 201 | // loop is not yet over. 202 | // 203 | // As per suggestion from Wikipedia. 204 | // 205 | // This has a cost of comparing twice, but 206 | // a benefit of ensuring we don't jump more than 207 | // we need to. 208 | // 209 | // NOTE: That we jump AFTER the conditional 210 | // test at the start of the loop, because 211 | // running it twice would be pointless. 212 | // 213 | buff.WriteString(" cmp byte ptr [r8], 0\n") 214 | 215 | buff.WriteString(fmt.Sprintf(" jne label_loop_%d\n", last)) 216 | buff.WriteString(fmt.Sprintf("close_loop_%d:\n", last)) 217 | 218 | default: 219 | fmt.Printf("token not handled: %v\n", tok) 220 | os.Exit(1) 221 | } 222 | 223 | // 224 | // Keep processing 225 | // 226 | offset++ 227 | } 228 | 229 | // terminate 230 | buff.WriteString(" mov %rax, 60\n") 231 | buff.WriteString(" mov %rdi, 0\n") 232 | buff.WriteString(" syscall\n") 233 | 234 | buff.WriteString(".bss\n") 235 | buff.WriteString("stack:\n") 236 | buff.WriteString(".rept 30000\n") 237 | buff.WriteString(" .byte 0x0\n") 238 | buff.WriteString(".endr\n") 239 | 240 | // Output to a file 241 | err := ioutil.WriteFile(g.output+".s", buff.Bytes(), 0644) 242 | return err 243 | } 244 | 245 | // compileSource passes our generated source-program through `gcc` 246 | // to compile it to an executable. 247 | func (g *GeneratorASM) compileSource() error { 248 | 249 | // Use gcc to compile our object-code 250 | gcc := exec.Command( 251 | "gcc", 252 | "-static", 253 | "-fPIC", 254 | "-nostdlib", 255 | "-nostartfiles", 256 | "-nodefaultlibs", 257 | "-o", g.output, 258 | g.output+".s") 259 | 260 | gcc.Stdout = os.Stdout 261 | gcc.Stderr = os.Stderr 262 | 263 | err := gcc.Run() 264 | if err != nil { 265 | return err 266 | } 267 | 268 | // Strip the binary - unless compiling for debug-usage 269 | debug := os.Getenv("DEBUG") 270 | if debug == "0" { 271 | strip := exec.Command("strip", g.output) 272 | strip.Stdout = os.Stdout 273 | strip.Stderr = os.Stderr 274 | 275 | err = strip.Run() 276 | if err != nil { 277 | return err 278 | } 279 | } 280 | return nil 281 | } 282 | 283 | // Generate takes the specified input-string and writes it as a compiled 284 | // binary to the named output-path. 285 | // 286 | // We generate a temporary file, write our assembly language file to that 287 | // and then compile via gcc. 288 | func (g *GeneratorASM) Generate(input string, output string) error { 289 | 290 | // 291 | // Save the input and output path away. 292 | // 293 | g.input = input 294 | g.output = output 295 | 296 | // 297 | // Generate our output program 298 | // 299 | err := g.generateSource() 300 | if err != nil { 301 | return err 302 | } 303 | 304 | // 305 | // Compile it 306 | // 307 | err = g.compileSource() 308 | if err != nil { 309 | return err 310 | } 311 | 312 | // 313 | // Cleanup our source file? Or leave it alone 314 | // and output the path of the source-file we generated. 315 | // 316 | clean := os.Getenv("CLEANUP") 317 | if clean == "1" { 318 | os.Remove(g.output + ".s") 319 | } else { 320 | fmt.Printf("generated source file at %s\n", g.output+".s") 321 | } 322 | 323 | return nil 324 | } 325 | 326 | // Register our back-end 327 | func init() { 328 | Register("asm", func() Generator { 329 | return &GeneratorASM{} 330 | }) 331 | } 332 | -------------------------------------------------------------------------------- /generators/c.go: -------------------------------------------------------------------------------- 1 | package generators 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | 10 | "github.com/skx/bfcc/lexer" 11 | ) 12 | 13 | // GeneratorC is a generator that will produce an C version of the specified 14 | // input-program. 15 | // 16 | // The C-source will then be compiled by gcc. 17 | type GeneratorC struct { 18 | // input source 19 | input string 20 | 21 | // file to write to 22 | output string 23 | } 24 | 25 | // generateSource produces a version of the program as C source-file 26 | func (c *GeneratorC) generateSource() error { 27 | var buff bytes.Buffer 28 | var programStart = ` 29 | extern int putchar(int); 30 | extern char getchar(); 31 | 32 | char array[30000]; 33 | 34 | int idx = 0; 35 | 36 | int main (int arc, char *argv[]) { 37 | ` 38 | buff.WriteString(programStart) 39 | 40 | // 41 | // Create a lexer for the input program 42 | // 43 | l := lexer.New(c.input) 44 | 45 | // 46 | // Program consists of all tokens 47 | // 48 | program := l.Tokens() 49 | 50 | // 51 | // We'll process the complete program until 52 | // we hit an end of file/input 53 | // 54 | offset := 0 55 | for offset < len(program) { 56 | 57 | // 58 | // The current token 59 | // 60 | tok := program[offset] 61 | 62 | // 63 | // Output different things depending on the token-type 64 | // 65 | switch tok.Type { 66 | 67 | case lexer.INC_PTR: 68 | buff.WriteString(fmt.Sprintf(" idx += %d;\n", tok.Repeat)) 69 | case lexer.DEC_PTR: 70 | buff.WriteString(fmt.Sprintf(" idx -= %d;\n", tok.Repeat)) 71 | case lexer.INC_CELL: 72 | buff.WriteString(fmt.Sprintf(" array[idx] += %d;\n", tok.Repeat)) 73 | case lexer.DEC_CELL: 74 | buff.WriteString(fmt.Sprintf(" array[idx] -= %d;\n", tok.Repeat)) 75 | case lexer.OUTPUT: 76 | buff.WriteString(" putchar(array[idx]);\n") 77 | 78 | case lexer.INPUT: 79 | buff.WriteString(" array[idx] = getchar();\n") 80 | 81 | case lexer.LOOP_OPEN: 82 | 83 | // 84 | // We sneekily optimize "[-]" by converting it 85 | // into an explicit setting of the cell-content 86 | // to zero. 87 | // 88 | // Since this involves looking at future-tokens 89 | // we need to make sure we're not at the end of 90 | // the program. 91 | // 92 | if offset+2 < len(program) { 93 | 94 | // 95 | // Look for the next two tokens "-]", if 96 | // we find them then we're looking at "[-]" 97 | // which is something we can optimize. 98 | // 99 | if program[offset+1].Type == lexer.DEC_CELL && 100 | program[offset+2].Type == lexer.LOOP_CLOSE { 101 | // register == zero 102 | buff.WriteString(" array[idx] = 0;\n") 103 | 104 | // 1. Skip this instruction, 105 | // 2. the next one "-" 106 | // 3. and the final one "]" 107 | offset += 3 108 | 109 | // And continue the loop again. 110 | continue 111 | } 112 | } 113 | 114 | buff.WriteString(" while (array[idx]) {\n") 115 | 116 | case lexer.LOOP_CLOSE: 117 | buff.WriteString("}\n") 118 | 119 | default: 120 | fmt.Printf("token not handled: %v\n", tok) 121 | os.Exit(1) 122 | } 123 | 124 | // 125 | // Keep processing 126 | // 127 | offset++ 128 | } 129 | 130 | // Close the main-function 131 | buff.WriteString("}\n") 132 | 133 | // Output to a file 134 | err := ioutil.WriteFile(c.output+".c", buff.Bytes(), 0644) 135 | return err 136 | } 137 | 138 | // compileSource uses gcc to compile the generated source-code 139 | func (c *GeneratorC) compileSource() error { 140 | 141 | gcc := exec.Command( 142 | "gcc", 143 | "-static", 144 | "-O3", 145 | "-s", 146 | "-o", c.output, 147 | c.output+".c") 148 | 149 | gcc.Stdout = os.Stdout 150 | gcc.Stderr = os.Stderr 151 | 152 | err := gcc.Run() 153 | return err 154 | } 155 | 156 | // Generate takes the specified input-string and writes it as a compiled 157 | // binary to the named output-path. 158 | // 159 | // We generate a temporary file, write our C-source to that and then 160 | // compile via gcc. 161 | func (c *GeneratorC) Generate(input string, output string) error { 162 | 163 | // 164 | // Save the input and output path away. 165 | // 166 | c.input = input 167 | c.output = output 168 | 169 | // 170 | // Generate our output program 171 | // 172 | err := c.generateSource() 173 | if err != nil { 174 | return err 175 | } 176 | 177 | // 178 | // Compile it 179 | // 180 | err = c.compileSource() 181 | if err != nil { 182 | return err 183 | } 184 | 185 | // 186 | // Cleanup our source file? Or leave it alone 187 | // and output the path of the source-file we generated. 188 | // 189 | clean := os.Getenv("CLEANUP") 190 | if clean == "1" { 191 | os.Remove(c.output + ".c") 192 | } else { 193 | 194 | fmt.Printf("generated source file at %s\n", c.output+".c") 195 | } 196 | 197 | return nil 198 | } 199 | 200 | // Register our back-end 201 | func init() { 202 | Register("c", func() Generator { 203 | return &GeneratorC{} 204 | }) 205 | } 206 | -------------------------------------------------------------------------------- /generators/generators.go: -------------------------------------------------------------------------------- 1 | // Package generators contains the back-ends to generate an executable 2 | // from an input Brainfuck source. 3 | // 4 | // We ship with a small number of backends, and allow the user to choose 5 | // which is used, by name, at runtime. 6 | package generators 7 | 8 | import "sync" 9 | 10 | // Generator is the interface which must be implemented by 11 | // a backend to compile our code 12 | type Generator interface { 13 | 14 | // Generate an executable at the path "output", from the 15 | // brainfuck source-code stored in "input". 16 | Generate(input string, output string) error 17 | } 18 | 19 | // 20 | // Everything below here is boilerplate to allow 21 | // class-registration and lookup. 22 | // 23 | 24 | // This is a map of known-backends. 25 | var handlers = struct { 26 | m map[string]NewGenerator 27 | sync.RWMutex 28 | }{m: make(map[string]NewGenerator)} 29 | 30 | // NewGenerator is the signature of a constructor-function to 31 | // instantiate a backend. 32 | type NewGenerator func() Generator 33 | 34 | // Register allows a new backend to self-register itself, with a name. 35 | func Register(id string, newfunc NewGenerator) { 36 | handlers.Lock() 37 | handlers.m[id] = newfunc 38 | handlers.Unlock() 39 | } 40 | 41 | // GetGenerator will retrieve a generator, by name. 42 | func GetGenerator(id string) (a Generator) { 43 | handlers.RLock() 44 | ctor, ok := handlers.m[id] 45 | handlers.RUnlock() 46 | if ok { 47 | a = ctor() 48 | } 49 | return 50 | } 51 | 52 | // Available returns the names of all registered backend handlers. 53 | func Available() []string { 54 | var result []string 55 | 56 | // For each handler save the name 57 | handlers.RLock() 58 | for index := range handlers.m { 59 | result = append(result, index) 60 | } 61 | handlers.RUnlock() 62 | 63 | // And return the result 64 | return result 65 | } 66 | -------------------------------------------------------------------------------- /generators/interpreter.go: -------------------------------------------------------------------------------- 1 | package generators 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/skx/bfcc/lexer" 8 | ) 9 | 10 | // Interpreter is a generator which will actually interpret, or execute, 11 | // the supplied program. It is not a compiler, and it produces no 12 | // output (other than that printed by the program itself). 13 | // 14 | // As our host application is designed to be a compiler this is a little 15 | // atypical, however it demonstrates the speedup possible by compilation. 16 | // 17 | // In my rough tests this interpreter is 200 times slower executing our 18 | // mandelbrot example. 19 | type Interpreter struct { 20 | 21 | // 22 | // Our state 23 | // 24 | 25 | // The lexed tokens of our input program. 26 | tokens []*lexer.Token 27 | 28 | // The offset within the program which we're executing. 29 | offset int 30 | 31 | // 32 | // BF virtual machine 33 | // 34 | 35 | // The index pointer. 36 | ptr int 37 | 38 | // The memory. 39 | memory [3000]int 40 | } 41 | 42 | // Generate takes the specified input-program, and executes it. 43 | func (i *Interpreter) Generate(input string, output string) error { 44 | 45 | // Create a lexer 46 | lex := lexer.New(input) 47 | 48 | // Store the programs' lexed tokens 49 | tok := lex.Next() 50 | for tok.Type != lexer.EOF { 51 | i.tokens = append(i.tokens, tok) 52 | tok = lex.Next() 53 | } 54 | 55 | // Setup our defaults 56 | i.ptr = 0 57 | i.offset = 0 58 | 59 | // 60 | // Repeatedly evaluate a single instruction, until 61 | // we've exhausted our program. 62 | // 63 | for i.offset < len(i.tokens) { 64 | err := i.evaluate() 65 | if err != nil { 66 | return err 67 | } 68 | } 69 | 70 | return nil 71 | } 72 | 73 | // evaluate executes the current BF instruction. 74 | func (i *Interpreter) evaluate() error { 75 | 76 | // 77 | // Get the token we're executing. 78 | // 79 | tok := i.tokens[i.offset] 80 | 81 | // 82 | // Execute it. 83 | // 84 | switch tok.Type { 85 | 86 | case lexer.INC_PTR: 87 | i.ptr += tok.Repeat 88 | 89 | case lexer.DEC_PTR: 90 | i.ptr -= tok.Repeat 91 | 92 | case lexer.INC_CELL: 93 | i.memory[i.ptr] += tok.Repeat 94 | 95 | case lexer.DEC_CELL: 96 | i.memory[i.ptr] -= tok.Repeat 97 | 98 | case lexer.LOOP_OPEN: 99 | // early termination 100 | if i.memory[i.ptr] != 0 { 101 | i.offset++ 102 | return nil 103 | } 104 | 105 | // Otherwise we need to the end of the loop and 106 | // jump to it 107 | depth := 1 108 | for depth != 0 { 109 | i.offset++ 110 | switch i.tokens[i.offset].Type { 111 | case lexer.LOOP_OPEN: 112 | depth++ 113 | case lexer.LOOP_CLOSE: 114 | depth-- 115 | } 116 | } 117 | return nil 118 | 119 | case lexer.LOOP_CLOSE: 120 | 121 | // early termination 122 | if i.memory[i.ptr] == 0 { 123 | i.offset++ 124 | return nil 125 | } 126 | 127 | depth := 1 128 | for depth != 0 { 129 | i.offset-- 130 | switch i.tokens[i.offset].Type { 131 | case lexer.LOOP_CLOSE: 132 | depth++ 133 | case lexer.LOOP_OPEN: 134 | depth-- 135 | } 136 | } 137 | return nil 138 | 139 | case lexer.INPUT: 140 | buf := make([]byte, 1) 141 | l, err := os.Stdin.Read(buf) 142 | if err != nil { 143 | return err 144 | } 145 | if l != 1 { 146 | return fmt.Errorf("read %d bytes of input, not 1", l) 147 | } 148 | i.memory[i.ptr] = int(buf[0]) 149 | 150 | case lexer.OUTPUT: 151 | fmt.Printf("%c", rune(i.memory[i.ptr])) 152 | 153 | } 154 | 155 | // next instruction will be executed next time. 156 | i.offset++ 157 | return nil 158 | } 159 | 160 | // Register our back-end 161 | func init() { 162 | Register("interpreter", func() Generator { 163 | return &Interpreter{} 164 | }) 165 | } 166 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/skx/bfcc 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /lexer/lexer.go: -------------------------------------------------------------------------------- 1 | //lint:file-ignore ST1003 Ignore all-caps because this is fine. 2 | 3 | // Package lexer is a simple lexer for processing BrainFuck programs. 4 | // 5 | // A given program will be parsed into a series of tokens, with unrecognized 6 | // input characters skipped over. 7 | // 8 | // As an optimization we collapse multiple adjacent tokens together, and 9 | // return their type as well as a count of how many times the character 10 | // was repeated. 11 | package lexer 12 | 13 | import "strings" 14 | 15 | // These constants are our token-types 16 | const ( 17 | EOF = "EOF" 18 | 19 | DEC_PTR = "<" 20 | INC_PTR = ">" 21 | 22 | INC_CELL = "+" 23 | DEC_CELL = "-" 24 | 25 | OUTPUT = "." 26 | INPUT = "," 27 | 28 | LOOP_OPEN = "[" 29 | LOOP_CLOSE = "]" 30 | ) 31 | 32 | // Token contains the next token from the input program. 33 | type Token struct { 34 | 35 | // Type contains the token-type (such as "<", "[", etc). 36 | Type string 37 | 38 | // Repeat contains the number of consecutive appearances we've seen 39 | // of this token. 40 | Repeat int 41 | } 42 | 43 | // Lexer holds our lexer state. 44 | type Lexer struct { 45 | 46 | // input is the string we're lexing. 47 | input string 48 | 49 | // position is the current position within the input-string. 50 | position int 51 | 52 | // simple map of single-character tokens to their type 53 | known map[string]string 54 | 55 | // simple map which allows us to determine if a token can 56 | // have repeated occurences collapsed. 57 | repeat map[string]bool 58 | } 59 | 60 | // New creates a new Lexer, which will parse the specified 61 | // input program into a series of tokens. 62 | func New(input string) *Lexer { 63 | 64 | // Create the lexer object. 65 | l := &Lexer{input: input} 66 | 67 | // Strip newlines/spaces from our iput 68 | l.input = strings.ReplaceAll(l.input, "\n", "") 69 | l.input = strings.ReplaceAll(l.input, "\r", "") 70 | l.input = strings.ReplaceAll(l.input, " ", "") 71 | 72 | // Populate the simple token-types in a map for later use. 73 | l.known = make(map[string]string) 74 | l.known["+"] = INC_CELL 75 | l.known["-"] = DEC_CELL 76 | l.known[">"] = INC_PTR 77 | l.known["<"] = DEC_PTR 78 | l.known[","] = INPUT 79 | l.known["."] = OUTPUT 80 | l.known["["] = LOOP_OPEN 81 | l.known["]"] = LOOP_CLOSE 82 | 83 | // Some characters will have their input collapsed 84 | // when multiple consecutive occurrences are found. 85 | l.repeat = make(map[string]bool) 86 | l.repeat["+"] = true 87 | l.repeat["-"] = true 88 | l.repeat[">"] = true 89 | l.repeat["<"] = true 90 | 91 | return l 92 | } 93 | 94 | // Tokens returns ALL tokens from the input-stream. 95 | func (l *Lexer) Tokens() []*Token { 96 | var res []*Token 97 | 98 | tok := l.Next() 99 | 100 | for tok.Type != EOF { 101 | res = append(res, tok) 102 | tok = l.Next() 103 | } 104 | 105 | return res 106 | } 107 | 108 | // Next returns the next token from our input stream. 109 | // 110 | // This is pretty naive lexer because we only have to consider 111 | // single-character tokens. However we do look for tokens which 112 | // are repeated. 113 | func (l *Lexer) Next() *Token { 114 | 115 | // Loop until we've exhausted our input. 116 | for l.position < len(l.input) { 117 | 118 | // Get the next character 119 | char := string(l.input[l.position]) 120 | 121 | // Is this a known character/token? 122 | _, ok := l.known[char] 123 | if ok { 124 | 125 | // 126 | // Can this token be repeated? 127 | // 128 | // If not just return this single instance. 129 | // 130 | repeated := l.repeat[char] 131 | if !repeated { 132 | l.position++ 133 | return &Token{Type: char, Repeat: 1} 134 | } 135 | 136 | // 137 | // OK we've found a character, such as "<", 138 | // which can be repeated multiple times. 139 | // 140 | // We count how many times that repetition 141 | // occurs, swallowing that input as we go. 142 | // 143 | 144 | // Record our starting position 145 | begin := l.position 146 | 147 | // Loop forward to see how many times the character 148 | // is repeated. 149 | for l.position < len(l.input) { 150 | 151 | // If it isn't the same character 152 | // we're done 153 | if string(l.input[l.position]) != char { 154 | break 155 | } 156 | 157 | // Otherwise keep advancing forward 158 | l.position++ 159 | } 160 | 161 | // Return the token and the times it was 162 | // seen in adjacent positions 163 | count := l.position - begin 164 | return &Token{Type: char, Repeat: count} 165 | } 166 | 167 | // 168 | // Here we're ignoring a token which was unknown. 169 | // 170 | l.position++ 171 | } 172 | 173 | // 174 | // If we got here then we're at/after the end of our input 175 | // string. So we just return EOF. 176 | // 177 | return &Token{Type: EOF, Repeat: 1} 178 | } 179 | -------------------------------------------------------------------------------- /lexer/lexer_test.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // TestLexer performs a trivial test of the lexer 8 | func TestLexer(t *testing.T) { 9 | 10 | tests := []struct { 11 | expectedType string 12 | expectedCount int 13 | }{ 14 | {INC_CELL, 1}, 15 | {DEC_CELL, 1}, 16 | {DEC_PTR, 5}, 17 | {INC_PTR, 5}, 18 | {LOOP_OPEN, 1}, 19 | {LOOP_CLOSE, 1}, 20 | {OUTPUT, 1}, 21 | {INPUT, 1}, 22 | {EOF, 1}, 23 | } 24 | 25 | l := New("+-<<<<<#\n`>>>>>[].,") 26 | 27 | for i, tt := range tests { 28 | tok := l.Next() 29 | if tok.Type != tt.expectedType { 30 | t.Fatalf("tests[%d] - tokentype wrong, expected=%q, got=%q", i, tt.expectedType, tok.Type) 31 | } 32 | if tok.Repeat != tt.expectedCount { 33 | t.Fatalf("tests[%d] - count wrong, expected=%d, got=%d", i, tt.expectedCount, tok.Repeat) 34 | } 35 | } 36 | } 37 | 38 | // TestAdjacent is designed to ensure we count adjacent runs of characters 39 | // even when newlines are in the way. 40 | func TestAdjacent(t *testing.T) { 41 | 42 | tests := []struct { 43 | expectedType string 44 | expectedCount int 45 | }{ 46 | {INC_CELL, 5}, 47 | {DEC_CELL, 5}, 48 | {EOF, 1}, 49 | } 50 | 51 | l := New("+\n+\n+\n+\n+- - - - -") 52 | 53 | for i, tt := range tests { 54 | tok := l.Next() 55 | if tok.Type != tt.expectedType { 56 | t.Fatalf("tests[%d] - tokentype wrong, expected=%q, got=%q", i, tt.expectedType, tok.Type) 57 | } 58 | if tok.Repeat != tt.expectedCount { 59 | t.Fatalf("tests[%d] - count wrong, expected=%d, got=%d", i, tt.expectedCount, tok.Repeat) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // bfcc is a trivial compiler for converting BrainFuck programs into 2 | // executables. 3 | // 4 | // The bfcc compiler contains a pair of backends which can be used to generate 5 | // executables. 6 | // 7 | // There is a backend named `asm` which converts the input program into an 8 | // assembly-language file, and then compiles it via `gcc`. 9 | // 10 | // Then there is a second backend named `c` which converts the input-program 11 | // into a C source-file, and then also compiles it via `gcc`. 12 | // 13 | // The end result of either approach should be a working, native, executable 14 | // which can be executed to run the brainfuck program. 15 | package main 16 | 17 | import ( 18 | "flag" 19 | "fmt" 20 | "io/ioutil" 21 | "os" 22 | "os/exec" 23 | 24 | "github.com/skx/bfcc/generators" 25 | ) 26 | 27 | func main() { 28 | 29 | // 30 | // Parse command-line flags 31 | // 32 | backend := flag.String("backend", "asm", "The backend to use for compilation.") 33 | cleanup := flag.Bool("cleanup", true, "Remove the generated files after creation.") 34 | debug := flag.Bool("debug", false, "Insert a debugging-breakpoint in the generated file, if possible.") 35 | run := flag.Bool("run", false, "Run the program after compiling.") 36 | flag.Parse() 37 | 38 | // 39 | // Ensure the backend we have is available 40 | // 41 | helper := generators.GetGenerator(*backend) 42 | if helper == nil { 43 | 44 | fmt.Printf("Unknown backend %s - valid backends are:\n", *backend) 45 | all := generators.Available() 46 | for _, name := range all { 47 | fmt.Printf("\t%s\n", name) 48 | } 49 | return 50 | } 51 | 52 | // 53 | // Ensure we have an input filename 54 | // 55 | if len(flag.Args()) < 1 { 56 | fmt.Printf("Usage: bfcc [flags] input.file.bf [outfile]\n") 57 | return 58 | } 59 | 60 | // 61 | // Input and output files 62 | // 63 | input := flag.Args()[0] 64 | output := "a.out" 65 | if len(flag.Args()) == 2 { 66 | output = flag.Args()[1] 67 | } 68 | 69 | // 70 | // Read the input program 71 | // 72 | prog, err := ioutil.ReadFile(input) 73 | if err != nil { 74 | fmt.Printf("failed to read %s: %s\n", input, err.Error()) 75 | } 76 | 77 | // 78 | // Will we cleanup ? 79 | // 80 | if *cleanup { 81 | os.Setenv("CLEANUP", "1") 82 | } else { 83 | os.Setenv("CLEANUP", "0") 84 | } 85 | 86 | // 87 | // Will we setup a debug-breakpoint? 88 | // 89 | // This only makes sense for the ASM-backend. 90 | // 91 | if *debug { 92 | os.Setenv("DEBUG", "1") 93 | } else { 94 | os.Setenv("DEBUG", "0") 95 | } 96 | 97 | // 98 | // Generate the compiled version 99 | // 100 | err = helper.Generate(string(prog), output) 101 | if err != nil { 102 | fmt.Printf("error generating binary: %s\n", err.Error()) 103 | return 104 | } 105 | 106 | // 107 | // Are we running the program? Then do so. 108 | // 109 | if *run { 110 | exe := exec.Command(output) 111 | exe.Stdin = os.Stdin 112 | exe.Stdout = os.Stdout 113 | exe.Stderr = os.Stderr 114 | err = exe.Run() 115 | if err != nil { 116 | fmt.Printf("Error launching %s: %s\n", output, err) 117 | os.Exit(1) 118 | } 119 | 120 | } 121 | } 122 | --------------------------------------------------------------------------------