├── .gitattributes ├── LICENSE ├── README.md ├── awk ├── README.md ├── bf.awk └── patches │ ├── busybox-1.28.4-older.patch │ ├── busybox.patch │ └── plan9.patch ├── bash ├── README.md ├── bf.bash └── bf2.bash ├── bc ├── README.md ├── bc.sh └── bf.bc ├── c ├── README.md └── bf.c ├── crystal ├── README.md └── bf.cr ├── d ├── README.md └── bf.d ├── dart ├── README.md └── bf.dart ├── elvish ├── README.md ├── bf.elv └── bf2.elv ├── examples ├── cent.b ├── fibonacci.b ├── fizzbuzz.b ├── hello.b ├── hello2.b ├── hello3.b ├── isort.b ├── numwarp.b ├── rot13.b └── wc.b ├── go ├── README.md └── bf.go ├── javascript ├── README.md ├── bf.js └── bf2.js ├── lua ├── README.md ├── bf.lua └── bf2.lua ├── nim ├── README.md └── bf.nim ├── perl ├── README.md ├── bf.pl └── bf2.pl ├── powershell ├── README.md ├── bf.ps1 └── bf2.ps1 ├── python ├── README.md └── bf.py ├── rc ├── README.md └── bf.rc ├── ruby ├── README.md ├── bf.rb └── bf2.rb ├── rust ├── README.md └── bf.rs ├── sh ├── README.md ├── bf.sh ├── bf1.sh └── patches │ └── toolbox.patch ├── swift ├── README.md └── bf.swift ├── tcl ├── README.md ├── bf.tcl └── bf2.tcl └── vala ├── README.md └── bf.vala /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rc diff 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Crestwave 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bf 2 | 3 | Brainfuck interpreters written in pure AWK, Bash, Elvish, Lua, Perl, Python, Ruby, Tcl, and `sh` 4 | 5 | ## Implementation Info 6 | 7 | All the interpreters have the following behavior: 8 | 9 | - Cells wrap on 8-bit overflow and underflow 10 | - The cell is left unchanged on EOF 11 | - Input is line buffered 12 | 13 | Any other behavior may vary to suit the languages. 14 | -------------------------------------------------------------------------------- /awk/README.md: -------------------------------------------------------------------------------- 1 | # bf.awk 2 | 3 | A brainfuck interpreter written in pure AWK 4 | 5 | ## Info 6 | 7 | - It is compatible with the one true `awk` 8 | - It is compliant with `gawk`'s linting when portable 9 | - An array of 30,000 cells in the positive direction is initialized 10 | - Referencing uninitialized cells is allowed, though not portable 11 | - The locale's character set is used 12 | -------------------------------------------------------------------------------- /awk/bf.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { 4 | ptr = 0 5 | input = "" 6 | for (i = 0; i < 30000; ++i) 7 | tape[i] = 0 8 | for (i = 0; i < 256; ++i) 9 | ord[sprintf("%c", i)] = i 10 | } 11 | 12 | { 13 | gsub(/[^][><.,+-]/, "") 14 | program = program $0 15 | } 16 | 17 | END { 18 | len = length(program) 19 | for (i = 1; i <= len; ++i) { 20 | c = substr(program, i, 1) 21 | if (c == "[") { 22 | stack[++ptr] = i 23 | } else if (c == "]") { 24 | jumps[stack[ptr]] = i 25 | jumps[i] = stack[ptr] 26 | delete stack[ptr--] 27 | } 28 | } 29 | 30 | for (i = 1; i <= len; ++i) { 31 | c = substr(program, i, 1) 32 | if (c == ">") { 33 | ptr += 1 34 | } else if (c == "<") { 35 | ptr -= 1 36 | } else if (c == "+") { 37 | if (++tape[ptr] == 256) 38 | tape[ptr] = 0 39 | } else if (c == "-") { 40 | if (--tape[ptr] == -1) 41 | tape[ptr] = 255 42 | } else if (c == ".") { 43 | printf("%c", tape[ptr]) 44 | } else if (c == ",") { 45 | if (!input && getline input <"-") 46 | input = input "\n" 47 | if (input) { 48 | tape[ptr] = ord[substr(input, 1, 1)] 49 | sub(/./, "", input) 50 | } 51 | } else if (c == "[") { 52 | if (!tape[ptr]) 53 | i = jumps[i] 54 | } else if (c == "]") { 55 | if (tape[ptr]) 56 | i = jumps[i] 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /awk/patches/busybox-1.28.4-older.patch: -------------------------------------------------------------------------------- 1 | --- a/bf.awk 2 | +++ b/bf.awk 3 | @@ -15,6 +15,7 @@ 4 | } 5 | 6 | END { 7 | + ARGV[ARGC++] = "-" 8 | len = length(program) 9 | for (i = 1; i <= len; ++i) { 10 | c = substr(program, i, 1) 11 | @@ -23,7 +24,8 @@ 12 | } else if (c == "]") { 13 | jumps[stack[ptr]] = i 14 | jumps[i] = stack[ptr] 15 | - delete stack[ptr--] 16 | + delete stack[ptr] 17 | + ptr -= 1 18 | } 19 | } 20 | 21 | @@ -42,7 +44,7 @@ 22 | } else if (c == ".") { 23 | printf("%c", tape[ptr]) 24 | } else if (c == ",") { 25 | - if (!input && getline input <"-") 26 | + if (!input && getline input) 27 | input = input "\n" 28 | if (input) { 29 | tape[ptr] = ord[substr(input, 1, 1)] 30 | -------------------------------------------------------------------------------- /awk/patches/busybox.patch: -------------------------------------------------------------------------------- 1 | --- a/bf.awk 2 | +++ b/bf.awk 3 | @@ -15,6 +15,7 @@ 4 | } 5 | 6 | END { 7 | + ARGV[ARGC++] = "-" 8 | len = length(program) 9 | for (i = 1; i <= len; ++i) { 10 | c = substr(program, i, 1) 11 | @@ -42,7 +43,7 @@ 12 | } else if (c == ".") { 13 | printf("%c", tape[ptr]) 14 | } else if (c == ",") { 15 | - if (! input && getline input <"-") 16 | + if (! input && getline input) 17 | input = input "\n" 18 | if (input) { 19 | tape[ptr] = ord[substr(input, 1, 1)] 20 | -------------------------------------------------------------------------------- /awk/patches/plan9.patch: -------------------------------------------------------------------------------- 1 | --- a/bf.awk 2 | +++ b/bf.awk 3 | @@ -10,7 +10,7 @@ 4 | } 5 | 6 | { 7 | - gsub(/[^][><.,+-]/, "") 8 | + gsub(/[^><+\-.,[\]]/, "") 9 | program = program $0 10 | } 11 | 12 | @@ -46,7 +46,7 @@ 13 | input = input "\n" 14 | if (input) { 15 | tape[ptr] = ord[substr(input, 1, 1)] 16 | - sub(/./, "", input) 17 | + input = substr(input, 2) 18 | } 19 | } else if (c == "[") { 20 | if (! tape[ptr]) 21 | -------------------------------------------------------------------------------- /bash/README.md: -------------------------------------------------------------------------------- 1 | # bf.bash 2 | 3 | A brainfuck interpreter written in pure Bash 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - It only depends on Bash 4.3+ and is portable 10 | - An array with the length of 30,000 cells is created 11 | - Negative pointers count from the back of the array 12 | - Unmatched brackets are detected during runtime 13 | - Output is ASCII-encoded 14 | 15 | 16 | ## bf2.bash Info 17 | 18 | - The program is transpiled into Bash then evaluated 19 | - It reads from files named in all arguments 20 | - Negative pointers are allowed 21 | -------------------------------------------------------------------------------- /bash/bf.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | LC_ALL=C 3 | IFS= 4 | 5 | die() 6 | { 7 | printf 'error: %s\n' "$@" >&2 8 | exit 1 9 | } 10 | 11 | if (( $# )); then 12 | if [[ -e $1 ]]; then 13 | program=$(< "$1") 14 | else 15 | die "file '$1' does not exist" 16 | fi 17 | else 18 | mapfile -t 19 | program=${MAPFILE[*]} 20 | fi 21 | 22 | program=${program//[^'><+-.,[]']} 23 | (( tape[29999] = ptr = 0 )) 24 | 25 | for (( i = 0; i < ${#program}; ++i )); do 26 | case ${program:i:1} in 27 | '>') 28 | (( ++ptr )) 29 | ;; 30 | '<') 31 | (( --ptr )) 32 | ;; 33 | '+') 34 | (( tape[ptr] = tape[ptr]+1 & 255 )) 35 | ;; 36 | '-') 37 | (( tape[ptr] = tape[ptr]-1 & 255 )) 38 | ;; 39 | '.') 40 | printf -v f %x "${tape[ptr]}" 41 | printf %b "\x$f" 42 | ;; 43 | ',') 44 | [[ -z $REPLY ]] && read -r && REPLY+=$'\n' 45 | 46 | [[ $REPLY ]] && { 47 | printf -v 'tape[ptr]' %d "'${REPLY::1}" 48 | REPLY=${REPLY:1} 49 | } 50 | ;; 51 | '[') 52 | if (( tape[ptr] )); then 53 | stack+=("$i") 54 | else 55 | for (( depth = 1; depth > 0 && ++i; )); do 56 | case ${program:i:1} in 57 | '[') (( ++depth )) ;; 58 | ']') (( --depth )) ;; 59 | '') die "unmatched [" ;; 60 | esac 61 | done 62 | fi 63 | ;; 64 | ']') 65 | (( ${#stack[@]} )) || die "unmatched ]" 66 | 67 | if (( tape[ptr] )); then 68 | (( i = stack[-1] )) 69 | else 70 | unset 'stack[-1]' 71 | fi 72 | ;; 73 | esac 74 | done 75 | 76 | (( ! ${#stack[@]} )) || die "unmatched [" 77 | -------------------------------------------------------------------------------- /bash/bf2.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if (( $# )); then 4 | for arg; do 5 | program+=$(< "$arg") || exit 6 | done 7 | else 8 | mapfile -t 9 | program=${MAPFILE[*]} 10 | fi 11 | 12 | program=${program//[^'><+-.,[]']} 13 | IFS= read -d "" -r translation <<'EOF' 14 | declare -A a 15 | LC_ALL=C 16 | IFS= 17 | p=0 18 | 19 | i() 20 | { 21 | [[ -z $REPLY ]] && read -r && REPLY+=$'\n' 22 | 23 | [[ $REPLY ]] && { 24 | printf -v 'a[$p]' %d "'${REPLY::1}" 25 | REPLY=${REPLY:1} 26 | } 27 | } 28 | 29 | o() 30 | { 31 | local hex 32 | printf -v hex %x "${a[$p]}" 33 | printf %b "\x$hex" 34 | } 35 | EOF 36 | 37 | for (( i = 0; i < ${#program}; ++i )); do 38 | case ${program:i:1} in 39 | '>'): '(( ++p ))' ;; 40 | '<'): '(( --p ))' ;; 41 | '+'): '(( a[$p] = a[$p]+1 & 255 ))' ;; 42 | '-'): '(( a[$p] = a[$p]-1 & 255 ))' ;; 43 | '.'): 'o' ;; 44 | ','): 'i' ;; 45 | '['): 'while (( a[$p] )); do :' ;; 46 | ']'): 'done' ;; 47 | esac 48 | translation+=$_$'\n' 49 | done 50 | 51 | eval "$translation"$'\n:' || exit 52 | -------------------------------------------------------------------------------- /bc/README.md: -------------------------------------------------------------------------------- 1 | # bf.bc 2 | 3 | A brainfuck interpreter written in pure `bc` 4 | 5 | ## Info 6 | 7 | This doesn't work like the other interpreters due to `bc`'s limitations. 8 | 9 | - Each command must be translated into its ASCII codepoint 10 | - Each codepoint must occupy a single line 11 | - The program must end with 33 (`!`) 12 | - The program must be stripped of comments 13 | - The program must be redirected into the interpreter's input 14 | - Input must be converted in the same way and given after the program 15 | - It must end with -1 16 | - Output is formatted similarly to the input and would have to externally be converted to ASCII if so desired 17 | 18 | Additionally: 19 | 20 | - Negative pointers are not allowed 21 | - It is POSIX-compliant except for the use of the `read()` extension. 22 | 23 | For your convenience, `bc.sh` is provided, which accepts a program's filename as its first argument and appropriately reads and converts input and output. 24 | -------------------------------------------------------------------------------- /bc/bc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | LC_ALL=C 3 | n=' 4 | ' 5 | 6 | while read -r line; do 7 | prog=$prog$line 8 | done < "$1" || exit 9 | 10 | while [ -n "$prog" ]; do 11 | c=${prog%"${prog#?}"} 12 | prog=${prog#?} 13 | case $c in ["><+-.,[]"]) 14 | program=$program$(printf %d "'$c")$n 15 | esac 16 | done 17 | 18 | program=${program}33$n 19 | dir=${0%/} 20 | dir=${dir%/*} 21 | 22 | { 23 | printf %s "$program" 24 | while input=; do 25 | read -r line 26 | eof=$? 27 | [ "$eof" != 0 ] && [ -z "$line" ] && break 28 | 29 | while [ -n "$line" ]; do 30 | input=$input$(printf %d "'${line%"${line#?}"}")$n 31 | line=${line#?} 32 | done 33 | 34 | printf %s "$input" 35 | if [ "$eof" = 0 ]; then 36 | printf '10\n' 37 | else 38 | break 39 | fi 40 | done 41 | printf -- '-1\n' 42 | } | "$dir"/bf.bc | { 43 | while read -r line; do 44 | printf "\\$(printf %o "$line")" 45 | done 46 | 47 | kill -s 13 0 48 | } 49 | -------------------------------------------------------------------------------- /bc/bf.bc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bc 2 | while (c = read() != 33) 3 | prog[len++] = c 4 | 5 | for (i = 0; i < len; ++i) { 6 | c = prog[i] 7 | if (c == 62) 8 | ptr += 1 9 | if (c == 60) 10 | ptr -= 1 11 | if (c == 43) 12 | if (++tape[ptr] == 256) 13 | tape[ptr] = 0 14 | if (c == 45) 15 | if (--tape[ptr] == -1) 16 | tape[ptr] = 255 17 | if (c == 46) 18 | tape[ptr] 19 | if (c == 44) 20 | if (!eof) { 21 | d = read() 22 | if (d == -1) 23 | eof = 1 24 | if (d != -1) 25 | tape[ptr] = d 26 | } 27 | if (c == 91) { 28 | if (tape[ptr]) 29 | stack[++j] = i 30 | if (!tape[ptr]) 31 | for (depth = 1; depth > 0 && ++i;) { 32 | d = prog[i] 33 | if (d == 91) 34 | depth += 1 35 | if (d == 93) 36 | depth -= 1 37 | } 38 | } 39 | if (c == 93) { 40 | if (tape[ptr]) 41 | i = stack[j] 42 | if (!tape[ptr]) 43 | j -= 1 44 | } 45 | } 46 | 47 | quit 48 | -------------------------------------------------------------------------------- /c/README.md: -------------------------------------------------------------------------------- 1 | # bf.c 2 | 3 | A brainfuck interpreter written in pure C 4 | 5 | ## Info 6 | 7 | - It is written in portable C 8 | - It reads the program from a file named in the first argument 9 | - If there are no arguments, it reads from standard input 10 | - It only reads up to 65,536 bytes 11 | - An array of 65,536 cells is initialized 12 | - The pointer wraps on 16-bit overflow and underflow 13 | - Output is ASCII-encoded 14 | -------------------------------------------------------------------------------- /c/bf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char program[65536]; 5 | uint8_t tape[65536]; 6 | uint16_t ptr; 7 | int jumps[65536]; 8 | int stack[65536]; 9 | int len; 10 | int c; 11 | int i; 12 | 13 | 14 | int main(int argc, char **argv) 15 | { 16 | if (argc > 1) { 17 | FILE *f = fopen(argv[1], "r"); 18 | len = fread(program, 1, 65536, f); 19 | fclose(f); 20 | } else { 21 | len = fread(program, 1, 65536, stdin); 22 | clearerr(stdin); 23 | } 24 | 25 | for (i = 0; i < len; ++i) 26 | switch (program[i]) { 27 | case '[': 28 | stack[++ptr] = i; 29 | break; 30 | case ']': 31 | jumps[stack[ptr]] = i; 32 | jumps[i] = stack[ptr--]; 33 | break; 34 | } 35 | 36 | for (i = 0; i < len; ++i) 37 | switch (program[i]) { 38 | case '>': 39 | ++ptr; 40 | break; 41 | case '<': 42 | --ptr; 43 | break; 44 | case '+': 45 | ++tape[ptr]; 46 | break; 47 | case '-': 48 | --tape[ptr]; 49 | break; 50 | case '.': 51 | putchar(tape[ptr]); 52 | fflush(stdout); 53 | break; 54 | case ',': 55 | c = getchar(); 56 | if (c != EOF) 57 | tape[ptr] = c; 58 | else 59 | clearerr(stdin); 60 | break; 61 | case '[': 62 | if (tape[ptr] == 0) 63 | i = jumps[i]; 64 | break; 65 | case ']': 66 | if (tape[ptr] != 0) 67 | i = jumps[i]; 68 | break; 69 | } 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /crystal/README.md: -------------------------------------------------------------------------------- 1 | # bf.cr 2 | 3 | A brainfuck interpreter written in pure Crystal 4 | 5 | ## Info 6 | 7 | - An array of 300,000 cells is initialized 8 | - Negative pointers refer to cells from the end of the array 9 | - Unmatched brackets are detected before runtime 10 | - Output is UTF-8-encoded 11 | -------------------------------------------------------------------------------- /crystal/bf.cr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env crystal 2 | program = ARGF.gets("") || "" 3 | program = program.gsub(/[^><+\-.,\[\]]/, "") 4 | 5 | stack = [] of Int32 6 | jumps = {} of Int32 => Int32 7 | 8 | program.each_char.with_index do |c, i| 9 | if c == '[' 10 | stack.push(i) 11 | elsif c == ']' 12 | j = stack.pop 13 | jumps[i] = j 14 | jumps[j] = i 15 | end 16 | end 17 | 18 | tape = Array.new(300_000, 0_u8) 19 | len = program.size 20 | ptr = 0 21 | i = 0 22 | 23 | while i < len 24 | case program[i] 25 | when '>' 26 | ptr += 1 27 | when '<' 28 | ptr -= 1 29 | when '+' 30 | tape[ptr] += 1 31 | when '-' 32 | tape[ptr] -= 1 33 | when '.' 34 | print tape[ptr].chr 35 | when ',' 36 | if c = STDIN.read_byte 37 | tape[ptr] = c 38 | end 39 | when '[' 40 | i = jumps[i] if tape[ptr].zero? 41 | when ']' 42 | i = jumps[i] unless tape[ptr].zero? 43 | end 44 | i += 1 45 | end 46 | -------------------------------------------------------------------------------- /d/README.md: -------------------------------------------------------------------------------- 1 | # bf.d 2 | 3 | A brainfuck interpreter written in pure D 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - The pointer is a 16-bit unsigned integer 10 | - Unmatched brackets are detected before runtime 11 | - Output is ASCII-encoded 12 | -------------------------------------------------------------------------------- /d/bf.d: -------------------------------------------------------------------------------- 1 | import std.array : back, empty, popBack; 2 | import std.file : FileException, read; 3 | import std.regex : regex, replaceAll; 4 | import std.stdio : EOF, getchar, putchar, stdin, stdout, writeln; 5 | 6 | int main(in string[] args) 7 | { 8 | char[] program; 9 | if (args.length > 1) 10 | try 11 | { 12 | program = cast(char[]) read(args[1]); 13 | } 14 | catch (FileException e) 15 | { 16 | writeln(e.msg); 17 | return 1; 18 | } 19 | else 20 | stdin.readf("%s", program); 21 | 22 | auto re = regex(r"[^><\+\-.,\[\]]"); 23 | program = program.replaceAll(re, ""); 24 | size_t len = program.length; 25 | 26 | size_t[size_t]jumps; 27 | size_t[] stack; 28 | 29 | foreach (i, c; program) 30 | if (c == '[') 31 | { 32 | stack ~= i; 33 | } 34 | else if (c == ']') 35 | { 36 | if (stack.empty) 37 | throw new Exception("unmatched ']'"); 38 | 39 | size_t j = stack.back; 40 | stack.popBack(); 41 | jumps[i] = j; 42 | jumps[j] = i; 43 | } 44 | 45 | if (!stack.empty) 46 | throw new Exception("unmatched '['"); 47 | 48 | char[65536] tape = '\0'; 49 | ushort ptr; 50 | 51 | for (size_t i = 0; i < len; ++i) 52 | switch (program[i]) 53 | { 54 | case '>': 55 | ++ptr; 56 | break; 57 | case '<': 58 | --ptr; 59 | break; 60 | case '+': 61 | ++tape[ptr]; 62 | break; 63 | case '-': 64 | --tape[ptr]; 65 | break; 66 | case '.': 67 | putchar(tape[ptr]); 68 | stdout.flush(); 69 | break; 70 | case ',': 71 | int c = getchar(); 72 | if (c != EOF) { 73 | tape[ptr] = cast(char)c; 74 | } else { 75 | stdin.clearerr(); 76 | } 77 | break; 78 | case '[': 79 | if (!tape[ptr]) 80 | i = jumps[i]; 81 | break; 82 | case ']': 83 | if (tape[ptr]) 84 | i = jumps[i]; 85 | break; 86 | default: 87 | break; 88 | } 89 | 90 | return 0; 91 | } 92 | -------------------------------------------------------------------------------- /dart/README.md: -------------------------------------------------------------------------------- 1 | # bf.dart 2 | 3 | A brainfuck interpreter written in pure Dart 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - The pointer wraps on 16-bit overflow and underflow 10 | - Output is UTF-8-encoded 11 | -------------------------------------------------------------------------------- /dart/bf.dart: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env dart 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | void main(List arguments) { 6 | var program; 7 | if (arguments.isEmpty) { 8 | var buffer = new StringBuffer(); 9 | var line; 10 | while ((line = stdin.readLineSync()) != null) { 11 | buffer.write(line); 12 | } 13 | program = buffer.toString(); 14 | } else { 15 | program = new File(arguments[0]).readAsStringSync(); 16 | } 17 | 18 | program = program.replaceAll(RegExp(r'[^><+\-.,[\]]'), ''); 19 | var len = program.length; 20 | var jumps = new List(len); 21 | var stack = []; 22 | 23 | for (var i = 0; i < len; ++i) { 24 | switch (program[i]) { 25 | case '[': 26 | stack.add(i); 27 | break; 28 | case ']': 29 | var j = stack.removeLast(); 30 | jumps[j] = i; 31 | jumps[i] = j; 32 | break; 33 | } 34 | } 35 | 36 | var tape = new Uint8List(65536); 37 | var ptr = 0; 38 | 39 | for (var i = 0; i < len; ++i) { 40 | switch (program[i]) { 41 | case '>': 42 | ptr = ptr + 1 & 65535; 43 | break; 44 | case '<': 45 | ptr = ptr - 1 & 65535; 46 | break; 47 | case '+': 48 | ++tape[ptr]; 49 | break; 50 | case '-': 51 | --tape[ptr]; 52 | break; 53 | case '.': 54 | stdout.writeCharCode(tape[ptr]); 55 | break; 56 | case ',': 57 | var c = stdin.readByteSync(); 58 | if (c != -1) { 59 | tape[ptr] = c; 60 | } 61 | break; 62 | case '[': 63 | if (tape[ptr] == 0) { 64 | i = jumps[i]; 65 | } 66 | break; 67 | case ']': 68 | if (tape[ptr] != 0) { 69 | i = jumps[i]; 70 | } 71 | break; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /elvish/README.md: -------------------------------------------------------------------------------- 1 | # bf.elv 2 | 3 | A brainfuck interpreter written in pure Elvish 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - A list of 30,000 cells is initialized 10 | - Negative pointers count from the back of the list 11 | - Unmatched brackets are detected before runtime 12 | - Output is UTF-8-encoded 13 | 14 | # bf2.elv Info 15 | 16 | - The program is transpiled into Elvish then evaluated 17 | -------------------------------------------------------------------------------- /elvish/bf.elv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elvish 2 | use re 3 | use str 4 | 5 | program input = "" "" 6 | if (> (count $args) 0) { 7 | up:program = (slurp < $args[0]) 8 | } else { 9 | up:program = (slurp) 10 | } 11 | 12 | program = (re:replace '[^><+\-.,[\]]' "" $program) 13 | len = (count $program) 14 | tape = [(repeat 30000 0)] 15 | jumps = [(repeat $len 0)] 16 | i ptr = 0 0 17 | stack = [] 18 | 19 | while (< $i $len) { 20 | c = $program[$i] 21 | if (eq $c "[") { 22 | stack = [$@stack $i] 23 | } elif (eq $c "]") { 24 | if (== (count $stack) 0) { 25 | fail "unmatched ']'" 26 | } 27 | j = $stack[-1] 28 | stack = $stack[:-1] 29 | jumps[$j] = $i 30 | jumps[$i] = $j 31 | } 32 | i = (+ $i 1) 33 | } 34 | if (> (count $stack) 0) { 35 | fail "unmatched '['" 36 | } 37 | 38 | i = 0 39 | while (< $i $len) { 40 | c = $program[$i] 41 | if (eq $c ">") { 42 | ptr = (+ $ptr 1) 43 | } elif (eq $c "<") { 44 | ptr = (- $ptr 1) 45 | } elif (eq $c "+") { 46 | tape[$ptr] = (+ $tape[$ptr] 1) 47 | if (== $tape[$ptr] 256) { 48 | tape[$ptr] = 0 49 | } 50 | } elif (eq $c "-") { 51 | tape[$ptr] = (- $tape[$ptr] 1) 52 | if (== $tape[$ptr] -1) { 53 | tape[$ptr] = 255 54 | } 55 | } elif (eq $c ".") { 56 | print (str:from-codepoints $tape[$ptr]) 57 | } elif (eq $c ",") { 58 | if (== (count $input) 0) { 59 | input = (read-upto "\n") 60 | } 61 | if (> (count $input) 0) { 62 | tape[$ptr] = (str:to-codepoints $input[0]) 63 | input = $input[1:] 64 | } 65 | } elif (eq $c "[") { 66 | if (== $tape[$ptr] 0) { 67 | i = $jumps[$i] 68 | } 69 | } elif (eq $c "]") { 70 | if (!= $tape[$ptr] 0) { 71 | i = $jumps[$i] 72 | } 73 | } 74 | i = (+ $i 1) 75 | } 76 | -------------------------------------------------------------------------------- /elvish/bf2.elv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env elvish 2 | use re 3 | 4 | prog = "" 5 | if (> (count $args) 0) { 6 | up:prog = (slurp < $args[0]) 7 | } else { 8 | up:prog = (slurp) 9 | } 10 | 11 | init = ' 12 | use str 13 | 14 | input = "" 15 | ptr = 0 16 | tape = [(repeat 30000 0)] 17 | ' 18 | 19 | tr = [&'>'='ptr = (+ $ptr 1)' 20 | &'<'='ptr = (- $ptr 1)' 21 | &'+'='if (== $tape[$ptr] 255) { 22 | tape[$ptr] = 0 23 | } else { 24 | tape[$ptr] = (+ $tape[$ptr] 1) 25 | }' 26 | &'-'='if (== $tape[$ptr] 0) { 27 | tape[$ptr] = 255 28 | } else { 29 | tape[$ptr] = (- $tape[$ptr] 1) 30 | }' 31 | &'.'='print (str:from-codepoints $tape[$ptr])' 32 | &','='if (== (count $input) 0) { 33 | input = (read-upto "\n") 34 | } 35 | 36 | if (> (count $input) 0) { 37 | tape[$ptr] = (str:to-codepoints $input[0]) 38 | input = $input[1:] 39 | }' 40 | &'['='while (!= $tape[$ptr] 0) {' 41 | &']'='}'] 42 | 43 | prog = $init( 44 | re:replace '.?' [c]{ 45 | if (has-key $tr $c) { 46 | put $tr[$c]"\n" 47 | } else { 48 | put "" 49 | } 50 | } $prog 51 | ) 52 | 53 | { eval $prog } 54 | -------------------------------------------------------------------------------- /examples/cent.b: -------------------------------------------------------------------------------- 1 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++. 2 | [-] 3 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++. 4 | -------------------------------------------------------------------------------- /examples/fibonacci.b: -------------------------------------------------------------------------------- 1 | >++++++++++>+>+[ 2 | [+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[ 3 | [-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 4 | [>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>> 5 | ]<<< 6 | ] 7 | This program doesn't terminate; you will have to kill it. 8 | Daniel B Cristofani (cristofdathevanetdotcom) 9 | http://www.hevanet.com/cristofd/brainfuck/ -------------------------------------------------------------------------------- /examples/fizzbuzz.b: -------------------------------------------------------------------------------- 1 | ++++++++[>+++++++++>+++++++++++++>++++++++>+++++++++++++++>+++++++++++++++>++++++++++++ 2 | +<<<<<<-]>-->+>++>--->++>---->>>+++>+++++<<<<[->+>>>>>+<<<-[<<->>[-<+>]]<<[-<<[<]>.>.>> 3 | >..>>>+++>>>>[-]<<<<<]+>[->+<]>>-[[-<<+>>]<<<->>>]<<<[<<<<.>.>..>>->+++++>>>>[-]<<<<<]> 4 | [->>+<<]>>>+>[-<[->+>>+<<<]>[-<+>]>>>+[[-]<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+<[->+ 5 | <[->[-]>>+>+<<<]]]]]]]]]<]>>[>]++++++[-<++++++++>]>>]<<<[.[-]<<<]]++++++++++.[-]<<<<<<] 6 | -------------------------------------------------------------------------------- /examples/hello.b: -------------------------------------------------------------------------------- 1 | ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. 2 | -------------------------------------------------------------------------------- /examples/hello2.b: -------------------------------------------------------------------------------- 1 | [ Shorter "Hello, World!" program that uses negative pointers. ] 2 | +[+[<<<+>>>>]+<-<-<<<+<++]<<.<++.<++..+++.<<++.<---.>>.>.+++.------.>-.>>--. 3 | -------------------------------------------------------------------------------- /examples/hello3.b: -------------------------------------------------------------------------------- 1 | [ 2 | Shortest known "Hello, World!" program in brainfuck. 3 | Uses negative pointers and 8-bit cell wrapping. 4 | ] 5 | +[-->-[>>+>-----<<]<--<---]>-.>>>+.>>..+++[.>]<<<<.+++.------.<<-.>>>>+. 6 | -------------------------------------------------------------------------------- /examples/isort.b: -------------------------------------------------------------------------------- 1 | [isort.b -- insertion sort 2 | (c) 2016 Daniel B. Cristofani 3 | http://brainfuck.org/] 4 | 5 | >>+>,[ 6 | <[ 7 | [>>+<<-]>[<<+<[->>+[<]]>>>[>]<<-]<<< 8 | ]>>[<<+>>-]<[>+<-]>[>>]<, 9 | ]<<<[<+<]>[>.>] 10 | 11 | [This program sorts bytes of input using insertion sort.] 12 | -------------------------------------------------------------------------------- /examples/numwarp.b: -------------------------------------------------------------------------------- 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 | [Enter a number using ()-./0123456789abcdef and space, and hit return. 33 | Daniel B Cristofani (cristofdathevanetdotcom) 34 | http://www.hevanet.com/cristofd/brainfuck/] 35 | -------------------------------------------------------------------------------- /examples/rot13.b: -------------------------------------------------------------------------------- 1 | , 2 | [>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 3 | [>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 4 | [>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 5 | [>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 6 | [>++++++++++++++<- 7 | [>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 8 | [>>+++++[<----->-]<<- 9 | [>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 10 | [>++++++++++++++<- 11 | [>+<-[>+<-[>+<-[>+<-[>+<- 12 | [>++++++++++++++<- 13 | [>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 14 | [>>+++++[<----->-]<<- 15 | [>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- 16 | [>++++++++++++++<- 17 | [>+<-]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] 18 | ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]>.[-]<,] 19 | 20 | of course any function char f(char) can be made easily on the same principle 21 | 22 | [Daniel B Cristofani (cristofdathevanetdotcom) 23 | http://www.hevanet.com/cristofd/brainfuck/] 24 | -------------------------------------------------------------------------------- /examples/wc.b: -------------------------------------------------------------------------------- 1 | >>>+>>>>>+>>+>>+[<<],[ 2 | -[-[-[-[-[-[-[-[<+>-[>+<-[>-<-[-[-[<++[<++++++>-]< 3 | [>>[-<]<[>]<-]>>[<+>-[<->[-]]]]]]]]]]]]]]]] 4 | <[-<<[-]+>]<<[>>>>>>+<<<<<<-]>[>]>>>>>>>+>[ 5 | <+[ 6 | >+++++++++<-[>-<-]++>[<+++++++>-[<->-]+[+>>>>>>]] 7 | <[>+<-]>[>>>>>++>[-]]+< 8 | ]>[-<<<<<<]>>>> 9 | ], 10 | ]+<++>>>[[+++++>>>>>>]<+>+[[<++++++++>-]<.<<<<<]>>>>>>>>] 11 | [Counts lines, words, bytes. Assumes no-change-on-EOF or EOF->0. 12 | Daniel B Cristofani (cristofdathevanetdotcom) 13 | http://www.hevanet.com/cristofd/brainfuck/] 14 | -------------------------------------------------------------------------------- /go/README.md: -------------------------------------------------------------------------------- 1 | # bf.go 2 | 3 | A brainfuck interpreter written in pure Go 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - The pointer is a 16-bit unsigned integer 10 | - An array with 8-bit unsigned cells is initialized 11 | - Unmatched brackets are detected before runtime 12 | - Output is UTF-8-encoded 13 | -------------------------------------------------------------------------------- /go/bf.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | "regexp" 10 | ) 11 | 12 | func main() { 13 | var program []byte 14 | var err error 15 | if len(os.Args) > 1 { 16 | program, err = ioutil.ReadFile(os.Args[1]) 17 | if err != nil { 18 | fmt.Fprintf(os.Stderr, "%s\n", err) 19 | os.Exit(1) 20 | } 21 | } else { 22 | program, err = ioutil.ReadAll(os.Stdin) 23 | if err != nil { 24 | panic(err) 25 | } 26 | } 27 | 28 | re := regexp.MustCompile(`[^><+\-.,[\]]`) 29 | program = re.ReplaceAll(program, []byte{}) 30 | 31 | jumps := make(map[int]int) 32 | var stack []int 33 | 34 | for i, c := range program { 35 | switch c { 36 | case '[': 37 | stack = append(stack, i) 38 | case ']': 39 | l := len(stack) 40 | if l < 1 { 41 | panic("syntax error: expecting ]") 42 | } 43 | 44 | j := stack[l-1] 45 | stack = stack[:l-1] 46 | jumps[i] = j 47 | jumps[j] = i 48 | } 49 | } 50 | 51 | if len(stack) > 0 { 52 | panic("syntax error: unexpected ]") 53 | } 54 | 55 | reader := bufio.NewReader(os.Stdin) 56 | l := len(program) 57 | 58 | var tape [65536]uint8 59 | var ptr uint16 60 | 61 | for i := 0; i < l; i++ { 62 | switch program[i] { 63 | case '>': 64 | ptr++ 65 | case '<': 66 | ptr-- 67 | case '+': 68 | tape[ptr]++ 69 | case '-': 70 | tape[ptr]-- 71 | case ',': 72 | c, err := reader.ReadByte() 73 | if err == nil { 74 | tape[ptr] = c 75 | } else if err != io.EOF { 76 | panic(err) 77 | } 78 | case '.': 79 | fmt.Printf("%c", tape[ptr]) 80 | case '[': 81 | if tape[ptr] == 0 { 82 | i = jumps[i] 83 | } 84 | case ']': 85 | if tape[ptr] != 0 { 86 | i = jumps[i] 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /javascript/README.md: -------------------------------------------------------------------------------- 1 | # bf.js 2 | 3 | A brainfuck interpreter written in JavaScript using pure Node.js 4 | 5 | ## Info 6 | - It reads the program from a file named in the first argument 7 | - If there are no arguments, it reads from standard input 8 | - An array with 65536 cells is initialized 9 | - Unmatched brackets are detected before runtime 10 | - Output is UTF-8-encoded 11 | 12 | ## bf2.js Info 13 | 14 | - The program is transpiled into JavaScript then evaluated 15 | - The tape array is extended dynamically 16 | -------------------------------------------------------------------------------- /javascript/bf.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const fs = require('fs'); 3 | const program = fs 4 | .readFileSync(process.argv[2] || 0, 'ascii') 5 | .replace(/[^><+\-.,[\]]/g, ''); 6 | 7 | const stack = []; 8 | const jumps = []; 9 | 10 | for (let i = 0; i < program.length; ++i) { 11 | switch (program[i]) { 12 | case '[': 13 | stack.push(i); 14 | break; 15 | case ']': 16 | if (!stack.length) 17 | throw new Error('SyntaxError: Unexpected token ]'); 18 | 19 | let j = stack.pop(); 20 | jumps[j] = i; 21 | jumps[i] = j; 22 | break; 23 | } 24 | } 25 | 26 | if (stack.length) 27 | throw new Error('SyntaxError: Expecting token ]'); 28 | 29 | const tape = new Uint8Array(65536); 30 | const ptr = new Uint16Array(1); 31 | 32 | for (let i = 0; i < program.length; ++i) 33 | switch (program[i]) { 34 | case '>': 35 | ++ptr[0]; 36 | break; 37 | case '<': 38 | --ptr[0]; 39 | break; 40 | case '+': 41 | ++tape[ptr[0]]; 42 | break; 43 | case '-': 44 | --tape[ptr[0]]; 45 | break; 46 | case '.': 47 | process.stdout.write(String.fromCharCode(tape[ptr[0]])); 48 | break; 49 | case ',': 50 | let buf = new Int8Array(1) 51 | if (fs.readSync(0, buf, 0, 1)) 52 | tape[ptr[0]] = buf[0]; 53 | break; 54 | case '[': 55 | if (!tape[ptr[0]]) 56 | i = jumps[i]; 57 | break; 58 | case ']': 59 | if (tape[ptr[0]]) 60 | i = jumps[i]; 61 | break; 62 | } 63 | -------------------------------------------------------------------------------- /javascript/bf2.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | let program; 3 | const fs = require('fs'); 4 | if (process.argv.length > 2) 5 | for (let i = 2; i < process.argv.length; ++i) { 6 | program += fs.readFileSync(process.argv[i], 'ascii'); 7 | } 8 | else 9 | program = fs.readFileSync(0, 'ascii'); 10 | 11 | program = program.replace(/[^><+\-.,[\]]/g, ''); 12 | const a = []; 13 | let p = 0; 14 | const buf = new Int8Array(1); 15 | 16 | const translation = { 17 | '>': '++p;', 18 | '<': '--p;', 19 | '+': 'a[p] = (a[p]||0)+1 & 255;', 20 | '-': 'a[p] = (a[p]||0)-1 & 255;', 21 | '.': 'process.stdout.write(String.fromCharCode(a[p]));', 22 | ',': 'if (fs.readSync(0, buf, 0, 1)) a[p] = buf[0];', 23 | '[': 'while (a[p]) {', 24 | ']': '}' 25 | } 26 | program = program.replace(/./g, c => translation[c]); 27 | eval(program); 28 | -------------------------------------------------------------------------------- /lua/README.md: -------------------------------------------------------------------------------- 1 | # bf.lua 2 | 3 | A brainfuck interpreter written in pure Lua 4 | 5 | ## Info 6 | 7 | - It is compatible with Lua 5.1+ and LuaJIT 8 | - It reads the program from a file named in the first argument 9 | - If there are no arguments, it reads from standard input 10 | - The program is transpiled into Lua then evaluated 11 | - An array of 30,000 cells to the right and 200 to the left is initialized 12 | - Output is ASCII-encoded 13 | 14 | ## bf2.lua Info 15 | 16 | - `load`/`loadstring` is not used 17 | - Unmatched brackets are detected before runtime 18 | - It reads from files named in all arguments 19 | - Array cells are initialized when they are used 20 | -------------------------------------------------------------------------------- /lua/bf.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local program 4 | if #arg > 0 then 5 | local f = assert(io.open(arg[1], "r")) 6 | program = f:read("*all") 7 | f:close() 8 | else 9 | program = io.read("*all") 10 | end 11 | 12 | local load = load 13 | if not pcall(load, "") then 14 | load = loadstring 15 | end 16 | 17 | assert(load( 18 | "local a = {} " .. 19 | "for i = -199, 30001 do a[i] = 0 end " .. 20 | "local p = 1 " .. 21 | program:gsub(".", setmetatable({ 22 | [">"] = "p = p + 1 ", 23 | ["<"] = "p = p - 1 ", 24 | ["+"] = "a[p] = (a[p]+1) % 256 ", 25 | ["-"] = "a[p] = (a[p]-1) % 256 ", 26 | ["."] = "io.write(string.char(a[p])) io.flush() ", 27 | [","] = "if io.read(0) then a[p] = io.read(1):byte() end ", 28 | ["["] = "while a[p] ~= 0 do ", 29 | ["]"] = "end " 30 | }, {__index = function() return "" end})) 31 | ))() 32 | -------------------------------------------------------------------------------- /lua/bf2.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local program = "" 4 | if #arg > 0 then 5 | for i = 1, #arg do 6 | local f = assert(io.open(arg[1], "r")) 7 | program = program .. f:read("*all") 8 | f:close() 9 | end 10 | else 11 | program = io.read("*all") 12 | end 13 | 14 | program = program:gsub("[^><+%-.,[%]]", "") 15 | local tape = setmetatable({}, {__index=function() return 0 end}) 16 | local stack = {} 17 | local jumps = {} 18 | local ptr = 0 19 | 20 | for i = 1, #program do 21 | local c = program:byte(i) 22 | if c == 91 then 23 | ptr = ptr + 1 24 | stack[ptr] = i 25 | elseif c == 93 then 26 | if ptr < 1 then 27 | error "unmatched ']'" 28 | end 29 | jumps[stack[ptr]] = i 30 | jumps[i] = stack[ptr] 31 | ptr = ptr - 1 32 | end 33 | end 34 | 35 | if ptr > 0 then 36 | error "unmatched '['" 37 | end 38 | 39 | local i = 1 40 | while i <= #program do 41 | local c = program:byte(i) 42 | if c == 62 then 43 | ptr = ptr + 1 44 | elseif c == 60 then 45 | ptr = ptr - 1 46 | elseif c == 43 then 47 | tape[ptr] = (tape[ptr]+1) % 256 48 | elseif c == 45 then 49 | tape[ptr] = (tape[ptr]-1) % 256 50 | elseif c == 46 then 51 | io.write(string.char(tape[ptr])) 52 | io.flush() 53 | elseif c == 44 then 54 | if io.read(0) then 55 | tape[ptr] = io.read(1):byte() 56 | end 57 | elseif c == 91 then 58 | if (tape[ptr] == 0) then 59 | i = jumps[i] 60 | end 61 | elseif c == 93 then 62 | if (tape[ptr] ~= 0) then 63 | i = jumps[i] 64 | end 65 | end 66 | i = i + 1 67 | end 68 | -------------------------------------------------------------------------------- /nim/README.md: -------------------------------------------------------------------------------- 1 | # bf.nim 2 | 3 | A brainfuck interpreter written in pure Nim 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - An array with cells from -200 to 299999 is initialized 10 | - Negative pointers are allowed 11 | - Unmatched brackets are detected before runtime 12 | - Output is ASCII-encoded 13 | -------------------------------------------------------------------------------- /nim/bf.nim: -------------------------------------------------------------------------------- 1 | import tables 2 | 3 | proc parse_jumps(program: string): object = 4 | var 5 | jumps = initTable[int, int]() 6 | stack = newSeq[int]() 7 | 8 | for i, c in pairs(program): 9 | if c == '[': 10 | stack.add(i) 11 | elif c == ']': 12 | if len(stack) == 0: 13 | quit("] without a corresponding [") 14 | 15 | var j = pop(stack) 16 | jumps[i] = j 17 | jumps[j] = i 18 | 19 | if len(stack) > 0: 20 | quit("[ without a corresponding ]") 21 | 22 | result = jumps 23 | 24 | {.push overflowChecks: off.} 25 | proc execute_bf(program: string) = 26 | let 27 | jumps = parse_jumps(program) 28 | programLen = len(program) 29 | 30 | var 31 | tape: array[-200..299999, char] 32 | i, pos = 0 33 | 34 | while i < programLen: 35 | case program[i] 36 | of '>': inc(pos) 37 | of '<': dec(pos) 38 | of '+': inc(tape[pos]) 39 | of '-': dec(tape[pos]) 40 | of '.': 41 | stdout.write(tape[pos]) 42 | flushFile(stdout) 43 | of ',': 44 | if not endOfFile(stdin): 45 | tape[pos] = readChar(stdin) 46 | of '[': 47 | if tape[pos] == '\0': 48 | i = jumps[i] 49 | of ']': 50 | if tape[pos] != '\0': 51 | i = jumps[i] 52 | else: discard 53 | inc i 54 | {.pop.} 55 | 56 | when isMainModule: 57 | import os 58 | var program: string 59 | 60 | try: 61 | program = if paramCount() > 0: readFile(paramStr(1)) 62 | else: readAll(stdin) 63 | except IOError: 64 | quit("I/O error: " & getCurrentExceptionMsg()) 65 | 66 | execute_bf(program) 67 | -------------------------------------------------------------------------------- /perl/README.md: -------------------------------------------------------------------------------- 1 | # bf.pl 2 | 3 | A brainfuck interpreter written in pure Perl 5 4 | 5 | ## Info 6 | 7 | - It is compliant with the `strict` and `warnings` pragmas 8 | - The program is transpiled into Perl 5 then evaluated 9 | - An array of 30,000 cells is initialized 10 | - Cells are initialized when modified 11 | - Printing uninitialized cells causes a warning 12 | - Negative pointers refer to cells from the end of the array 13 | - Output is ASCII-encoded 14 | 15 | ## bf2.pl Info 16 | 17 | - `eval` is not used 18 | - It reads from files named in all arguments 19 | - End-of-files are cleared from standard input 20 | - Negative pointers are allowed 21 | - Unmatched brackets are detected before runtime 22 | -------------------------------------------------------------------------------- /perl/bf.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | local $| = 1; 6 | my @a = (0) x 30000; 7 | my $p = 0; 8 | my %translation = ( 9 | '>' => '++$p', 10 | '<' => '--$p', 11 | '+' => '++$a[$p] == 256 and $a[$p] = 0', 12 | '-' => '--$a[$p] == -1 and $a[$p] = 255', 13 | '.' => 'print chr($a[$p])', 14 | ',' => '$a[$p] = ord(getc) unless eof(STDIN)', 15 | '[' => 'while ($a[$p]) {', 16 | ']' => '}', 17 | ); 18 | 19 | my $program = do { 20 | local $/ = undef; 21 | <>; 22 | }; 23 | die $! unless defined($program); 24 | 25 | $program =~ s/[^><+\-.,[\]]//g; 26 | $program =~ s/./$translation{$&};/g; 27 | eval $program; 28 | die $@ if $@; 29 | -------------------------------------------------------------------------------- /perl/bf2.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | use IO::Handle; 5 | 6 | my $program; 7 | { 8 | local $/ = undef; 9 | do { $program .= <> } while (@ARGV); 10 | IO::Handle::clearerr(*STDIN); 11 | die $! unless defined($program); 12 | } 13 | 14 | $program =~ s/[^><+\-.,[\]]//g; 15 | my $len = length($program); 16 | my @stack; 17 | my @jumps; 18 | 19 | for (my $i = 0; $i < $len; ++$i) { 20 | my $c = substr($program, $i, 1); 21 | if ($c eq '[') { push @stack, $i } 22 | elsif ($c eq ']') { 23 | die "Unmatched right square bracket" unless (@stack); 24 | my $j = pop(@stack); 25 | $jumps[$j] = $i; 26 | $jumps[$i] = $j; 27 | } 28 | } 29 | 30 | die "Missing right square bracket" if (@stack); 31 | local $| = 1; 32 | my %tape; 33 | my $ptr = 0; 34 | 35 | for (my $i = 0; $i < $len; ++$i) { 36 | my $c = substr($program, $i, 1); 37 | if ($c eq '>') { ++$ptr } 38 | elsif ($c eq '<') { --$ptr } 39 | elsif ($c eq '+') { ++$tape{$ptr} == 256 and $tape{$ptr} = 0 } 40 | elsif ($c eq '-') { --$tape{$ptr} == -1 and $tape{$ptr} = 255 } 41 | elsif ($c eq '.') { print chr($tape{$ptr}) } 42 | elsif ($c eq ',') { 43 | unless (eof(STDIN)) { $tape{$ptr} = ord(getc) } 44 | else { IO::Handle::clearerr(*STDIN) } 45 | } 46 | elsif ($c eq '[') { $i = $jumps[$i] unless ($tape{$ptr}) } 47 | elsif ($c eq ']') { $i = $jumps[$i] if ($tape{$ptr}) } 48 | } 49 | -------------------------------------------------------------------------------- /powershell/README.md: -------------------------------------------------------------------------------- 1 | # bf.bash 2 | 3 | A brainfuck interpreter written in pure PowerShell 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it read from standard input 9 | - An array with the length of 30,000 cells is created 10 | - Negative pointers count from the back of the array 11 | - Unmatched brackets are detected before runtime 12 | - Output is UTF-8-encoded 13 | 14 | ## bf2.ps1 Info 15 | 16 | - The program is transpiled into PowerShell then evaluated 17 | -------------------------------------------------------------------------------- /powershell/bf.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | param ( 3 | $filename 4 | ) 5 | 6 | if ($filename -ne $null) { 7 | $program = Get-Content -Raw $filename 8 | } else { 9 | do { 10 | $line = [console]::ReadLine() 11 | $program += $line 12 | } while ($line -ne $null) 13 | } 14 | 15 | $jumps = @{} 16 | $stack = [System.Collections.ArrayList]@() 17 | 18 | for ($i = 0; $i -lt $program.length; ++$i) { 19 | switch ($program[$i]) { 20 | "[" { $null = $stack.Add($i) } 21 | "]" { 22 | if ($stack.count -eq 0) { 23 | throw "Unexpected token ']'." 24 | } 25 | 26 | $j = $stack[-1] 27 | $stack.RemoveAt($stack.count - 1) 28 | $jumps[$i] = $j 29 | $jumps[$j] = $i 30 | } 31 | } 32 | } 33 | 34 | if ($stack.length -gt 0) { 35 | throw "Unmatched token '['." 36 | } 37 | 38 | $tape = @(0) * 30000 39 | $ptr = 0 40 | 41 | for ($i = 0; $i -lt $program.length; ++$i) { 42 | switch ($program[$i]) { 43 | ">" { ++$ptr } 44 | "<" { --$ptr } 45 | "+" { 46 | if (++$tape[$ptr] -eq 256) { 47 | $tape[$ptr] = 0 48 | } 49 | } 50 | "-" { 51 | if (--$tape[$ptr] -eq -1) { 52 | $tape[$ptr] = 255 53 | } 54 | } 55 | "." { Write-Host -NoNewline ([char]$tape[$ptr]) } 56 | "," { 57 | $c = [console]::Read() 58 | if ($c -ne -1) { 59 | $tape[$ptr] = $c 60 | } 61 | } 62 | "[" { 63 | if ($tape[$ptr] -eq 0) { 64 | $i = $jumps[$i] 65 | } 66 | } 67 | "]" { 68 | if ($tape[$ptr] -ne 0) { 69 | $i = $jumps[$i] 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /powershell/bf2.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | param ( 3 | $filename 4 | ) 5 | 6 | if ($filename -ne $null) { 7 | $program = Get-Content -Raw $filename 8 | } else { 9 | do { 10 | $line = [console]::ReadLine() 11 | $program += $line 12 | } while ($line -ne $null) 13 | } 14 | 15 | $a = @(0) * 30000 16 | $p = 0 17 | $t = @{ 18 | ">" = '++$p;'; 19 | "<" = '--$p;'; 20 | "+" = 'if (++$a[$p] -eq 256) { $a[$p] = 0 }'; 21 | "-" = 'if (--$a[$p] -eq -1) { $a[$p] = 255 }'; 22 | "." = 'Write-Host -NoNewline ([char]$a[$p]);'; 23 | "," = '$c = [console]::Read(); if ($c -ne -1) { $a[$p] = $c }'; 24 | "[" = 'while ($a[$p]) {'; 25 | "]" = '}'; 26 | } 27 | 28 | foreach ($c in [char[]]$program) { 29 | $eval += $t[[string]$c] 30 | } 31 | 32 | Invoke-Expression $eval 33 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | # bf.py 2 | 3 | A brainfuck interpreter written in pure Python 4 | 5 | ## Info 6 | 7 | - It is compatible with both Python 2 and 3 8 | - It is compliant with PEP 8 and `pylint` 9 | - It reads the program from a file named in the first argument 10 | - If there are no arguments, it reads from standard input 11 | - A list of 30,000 cells is initialized 12 | - Negative pointers count from the back of the list 13 | - Unmatched brackets are detected before runtime 14 | - The Python version's default character encoding is used for output 15 | -------------------------------------------------------------------------------- /python/bf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """A brainfuck interpreter written in pure Python""" 3 | import re 4 | import sys 5 | 6 | 7 | def parse_jumps(program): 8 | """Returns a dictionary matching bracket locations""" 9 | index = 0 10 | stack = [] 11 | jumps = {} 12 | 13 | for index, char in enumerate(program): 14 | if char == '[': 15 | stack.append(index) 16 | elif char == ']': 17 | if not stack: 18 | raise SyntaxError("unmatched closing bracket") 19 | match = stack.pop() 20 | jumps[match] = index 21 | jumps[index] = match 22 | 23 | if stack: 24 | raise SyntaxError("unmatched opening bracket") 25 | 26 | return jumps 27 | 28 | 29 | def execute_bf(program): 30 | """Executes a brainfuck program""" 31 | jumps = parse_jumps(program) 32 | length = len(program) 33 | tape = [0] * 30000 34 | ptr = 0 35 | index = 0 36 | 37 | while index < length: 38 | char = program[index] 39 | 40 | if char == '>': 41 | ptr += 1 42 | elif char == '<': 43 | ptr -= 1 44 | elif char == '+': 45 | tape[ptr] = tape[ptr]+1 & 255 46 | elif char == '-': 47 | tape[ptr] = tape[ptr]-1 & 255 48 | elif char == '.': 49 | sys.stdout.write(chr(tape[ptr])) 50 | sys.stdout.flush() 51 | elif char == ',': 52 | char = sys.stdin.read(1) 53 | if char: 54 | tape[ptr] = ord(char) 55 | elif char == '[': 56 | if tape[ptr] == 0: 57 | index = jumps[index] 58 | elif char == ']': 59 | if tape[ptr] != 0: 60 | index = jumps[index] 61 | 62 | index += 1 63 | 64 | 65 | def main(): 66 | """Reads the program, strips comments from it, then runs execute_bf""" 67 | if sys.argv[1:]: 68 | with open(sys.argv[1], 'r') as program_file: 69 | program = program_file.read() 70 | else: 71 | program = sys.stdin.read() 72 | 73 | program = re.sub(r"[^><+\-.,[\]]", '', program) 74 | execute_bf(program) 75 | 76 | 77 | if __name__ == "__main__": 78 | main() 79 | -------------------------------------------------------------------------------- /rc/README.md: -------------------------------------------------------------------------------- 1 | # bf.rc 2 | 3 | A brainfuck interpreter written in pure rc 4 | 5 | ## Info 6 | 7 | - "Pure" rc here means only using the Plan 9 base utilities, excluding `awk`, as you can't really do anything with just rc builtins (and `awk` is more powerful than rc) 8 | - I also tried to use as few external utilities as few times as possible; the current list is `ascii`, `bc`, `echo`, and `read`. 9 | - It reads the program from a file named in the first argument 10 | - If there are no arguments, it reads from standard input 11 | - An array with around 30,000 cells to the right and 2,000 to the left is created 12 | - Output is ASCII-encoded 13 | -------------------------------------------------------------------------------- /rc/bf.rc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rc 2 | if(~ $#* 0) 3 | prog=`{ascii -d `{ascii -n -d -- `{read -m}}} 4 | if not 5 | prog=`{ascii -d `{ascii -n -d -- `{read -m <$1}}} 6 | 7 | tape=`{echo 'for(i=0;i<32002;++i) print 0' | bc} 8 | ptr=(2001 2002 2003) 9 | nl=' 10 | ' 11 | i=0 12 | j=0 13 | 14 | fn pop{ 15 | shift 16 | stack=$* 17 | } 18 | 19 | fn write{ 20 | tape=($tape(1-$ptr(1)) $1 $tape($ptr(3)^-)) 21 | } 22 | 23 | while(! ~ $i $#prog){ 24 | i=`{echo $i+1 | bc} 25 | switch($prog($i)){ 26 | case '>' 27 | ptr=`{echo $ptr(2)';'$ptr(2)+1';'$ptr(2)+2 | bc} 28 | case '<' 29 | ptr=`{echo $ptr(2)-2';'$ptr(2)-1';'$ptr(2) | bc} 30 | case '+' 31 | write `{echo $tape($ptr(2))+1 | bc} 32 | if(~ $tape($ptr(2)) 256) 33 | write 0 34 | case '-' 35 | write `{echo $tape($ptr(2))-1 | bc} 36 | if(~ $tape($ptr(2)) -1) 37 | write 255 38 | case '.' 39 | switch($tape($ptr(2))){ 40 | case 0;v='';case 1;v='';case 2;v='';case 3;v='';case 4;v='';case 5;v='';case 6;v='';case 7;v='';case 8;v='';case 9;v=' ';case 10;v=$nl;case 11;v=' ';case 12;v=' ';case 13;v=' ';case 14;v='';case 15;v='';case 16;v='';case 17;v='';case 18;v='';case 19;v='';case 20;v='';case 21;v='';case 22;v='';case 23;v='';case 24;v='';case 25;v='';case 26;v='';case 27;v='';case 28;v='';case 29;v='';case 30;v='';case 31;v='';case 32;v=' ';case 33;v='!';case 34;v='"';case 35;v='#';case 36;v='$';case 37;v='%';case 38;v='&';case 39;v='''';case 40;v='(';case 41;v=')';case 42;v='*';case 43;v='+';case 44;v=',';case 45;v='-';case 46;v='.';case 47;v='/';case 48;v='0';case 49;v='1';case 50;v='2';case 51;v='3';case 52;v='4';case 53;v='5';case 54;v='6';case 55;v='7';case 56;v='8';case 57;v='9';case 58;v=':';case 59;v=';';case 60;v='<';case 61;v='=';case 62;v='>';case 63;v='?';case 64;v='@';case 65;v='A';case 66;v='B';case 67;v='C';case 68;v='D';case 69;v='E';case 70;v='F';case 71;v='G';case 72;v='H';case 73;v='I';case 74;v='J';case 75;v='K';case 76;v='L';case 77;v='M';case 78;v='N';case 79;v='O';case 80;v='P';case 81;v='Q';case 82;v='R';case 83;v='S';case 84;v='T';case 85;v='U';case 86;v='V';case 87;v='W';case 88;v='X';case 89;v='Y';case 90;v='Z';case 91;v='[';case 92;v='\';case 93;v=']';case 94;v='^';case 95;v='_';case 96;v='`';case 97;v='a';case 98;v='b';case 99;v='c';case 100;v='d';case 101;v='e';case 102;v='f';case 103;v='g';case 104;v='h';case 105;v='i';case 106;v='j';case 107;v='k';case 108;v='l';case 109;v='m';case 110;v='n';case 111;v='o';case 112;v='p';case 113;v='q';case 114;v='r';case 115;v='s';case 116;v='t';case 117;v='u';case 118;v='v';case 119;v='w';case 120;v='x';case 121;v='y';case 122;v='z';case 123;v='{';case 124;v='|';case 125;v='}';case 126;v='~';case *;v=`{ascii -d $tape($ptr(2))} 41 | } 42 | echo -n $v 43 | case ',' 44 | if(~ $j $#input){ 45 | if(~ $eof 1){ 46 | eof=0 47 | } 48 | if not{ 49 | ifs='' 50 | line=`{read} 51 | if (! ~ $line *$nl) 52 | eof=1 53 | 54 | ifs=' '$nl 55 | if(~ $line ?*) 56 | input=`{ascii -n -d -- $line | bc} 57 | if not 58 | input=() 59 | j=0 60 | } 61 | } 62 | if(! ~ $j $#input){ 63 | j=`{echo $j+1 | bc} 64 | write $input($j) 65 | } 66 | case '[' 67 | if(! ~ $tape($ptr(2)) 0) 68 | stack=($i $stack) 69 | if not{ 70 | depth=1 71 | while(! ~ $depth 0){ 72 | i=`{echo $i+1 | bc} 73 | switch($prog($i)){ 74 | case '[' 75 | depth=`{echo $depth+1 | bc} 76 | case ']' 77 | depth=`{echo $depth-1 | bc} 78 | } 79 | } 80 | } 81 | case ']' 82 | if(! ~ $tape($ptr(2)) 0) 83 | i=$stack(1) 84 | if not 85 | pop $stack 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ruby/README.md: -------------------------------------------------------------------------------- 1 | # bf.rb 2 | 3 | A brainfuck interpreter written in pure Ruby 4 | 5 | ## Info 6 | 7 | - The program is transpiled into Ruby then evaluated 8 | - An array of 30,000 cells is initialized 9 | - Negative pointers refer to cells from the end of the array 10 | - Output is ASCII-encoded 11 | 12 | ## bf2.rb Info 13 | 14 | - `eval` is not used 15 | - Negative pointers are allowed 16 | -------------------------------------------------------------------------------- /ruby/bf.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | a = Array.new(30_000, 0) 3 | p = 0 4 | translation = { 5 | '>' => 'p += 1;', 6 | '<' => 'p -= 1;', 7 | '+' => 'a[p] = a[p]+1 & 255;', 8 | '-' => 'a[p] = a[p]-1 & 255;', 9 | '.' => 'putc a[p].chr;', 10 | ',' => 'a[p] = $stdin.getbyte unless $stdin.eof?;', 11 | '[' => 'while a[p].nonzero? do ', 12 | ']' => 'end;' 13 | } 14 | 15 | eval ARGF.read.gsub(/./, translation) 16 | -------------------------------------------------------------------------------- /ruby/bf2.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | program = ARGF.read 3 | program.gsub!(/[^><+\-.,\[\]]/, '') 4 | 5 | stack = [] 6 | jumps = [] 7 | 8 | program.each_char.with_index do |c, i| 9 | if c == '[' 10 | stack.push(i) 11 | elsif c == ']' 12 | j = stack.pop 13 | jumps[i] = j 14 | jumps[j] = i 15 | end 16 | end 17 | 18 | len = program.length 19 | tape = Hash.new(0) 20 | ptr = 0 21 | i = 0 22 | 23 | while i < len 24 | case program[i] 25 | when '>' 26 | ptr += 1 27 | when '<' 28 | ptr -= 1 29 | when '+' 30 | tape[ptr] = (tape[ptr] + 1) & 255 31 | when '-' 32 | tape[ptr] = (tape[ptr] - 1) & 255 33 | when '.' 34 | putc tape[ptr].chr 35 | when ',' 36 | tape[ptr] = $stdin.getbyte unless $stdin.eof? 37 | when '[' 38 | i = jumps[i] if tape[ptr].zero? 39 | when ']' 40 | i = jumps[i] if tape[ptr].nonzero? 41 | end 42 | i += 1 43 | end 44 | -------------------------------------------------------------------------------- /rust/README.md: -------------------------------------------------------------------------------- 1 | # bf.rs 2 | 3 | A brainfuck interpreter written in pure Rust 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - The pointer is a 16-bit unsigned integer 10 | - Cells are 8-bit unsigned integers and don't have special handling 11 | - This means that it will panic on overflow and underflow in debug mode 12 | - Unmatched brackets are detected before runtime 13 | - Output is ASCII-encoded 14 | -------------------------------------------------------------------------------- /rust/bf.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::env; 3 | use std::fs::File; 4 | use std::io::prelude::*; 5 | use std::io; 6 | 7 | fn main() -> io::Result<()> { 8 | let program: Vec = { 9 | let mut buf = String::new(); 10 | if let Some(filename) = env::args().nth(1) { 11 | let mut f = File::open(filename)?; 12 | f.read_to_string(&mut buf)?; 13 | } else { 14 | let stdin = io::stdin(); 15 | let mut handle = stdin.lock(); 16 | handle.read_to_string(&mut buf)?; 17 | } 18 | buf.chars().filter(|&c| "><+-,.[]".contains(c)).collect() 19 | }; 20 | 21 | let len = program.len(); 22 | let jumps = { 23 | let mut builder = HashMap::new(); 24 | let mut stack = Vec::new(); 25 | for (i, c) in program.iter().enumerate() { 26 | if c == &'[' { 27 | stack.push(i); 28 | } else if c == &']' { 29 | if stack.is_empty() { 30 | panic!("unexpected ']'"); 31 | } 32 | 33 | let j = stack.pop().unwrap(); 34 | builder.insert(i, j); 35 | builder.insert(j, i); 36 | } 37 | } 38 | 39 | if !stack.is_empty() { 40 | panic!("un-closed '['"); 41 | } 42 | builder 43 | }; 44 | 45 | let mut tape: [u8; 65536] = [0; 65536]; 46 | let mut ptr: u16 = 0; 47 | let mut i = 0; 48 | while i < len { 49 | let j = ptr as usize; 50 | match program[i] { 51 | '>' => ptr += 1, 52 | '<' => ptr -= 1, 53 | '+' => tape[j] += 1, 54 | '-' => tape[j] -= 1, 55 | ',' => { 56 | match io::stdin().bytes().next() { 57 | Some(Ok(c)) => tape[j] = c, 58 | Some(Err(e)) => panic!("{}", e), 59 | None => (), 60 | } 61 | } 62 | '.' => { 63 | print!("{}", tape[j] as char); 64 | io::stdout().flush()?; 65 | } 66 | '[' if tape[j] == 0 => i = jumps[&i], 67 | ']' if tape[j] != 0 => i = jumps[&i], 68 | _ => (), 69 | } 70 | i += 1; 71 | } 72 | Ok(()) 73 | } 74 | -------------------------------------------------------------------------------- /sh/README.md: -------------------------------------------------------------------------------- 1 | # bf.sh 2 | 3 | A brainfuck interpreter written in pure `sh` 4 | 5 | ## bf.sh Info 6 | 7 | - It is POSIX-compliant 8 | - It reads the program from a file named in the first argument 9 | - If no arguments are passed, it reads from standard input 10 | - Negative pointers are allowed 11 | - Output is ASCII-encoded 12 | 13 | # bf1.sh Info 14 | 15 | - Same as bf.sh but optimized at the cost of readability 16 | -------------------------------------------------------------------------------- /sh/bf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | LC_ALL=C 3 | IFS=' ' 4 | n=' 5 | ' 6 | val=0 7 | 8 | if [ "$#" -gt 0 ]; then 9 | while read -r line || [ -n "$line" ]; do 10 | prog=$prog$line 11 | done < "$1" || exit 12 | else 13 | while read -r line || [ -n "$line" ]; do 14 | prog=$prog$line 15 | done 16 | fi 17 | 18 | while :; do 19 | case $prog in 20 | '>'*) 21 | set -- ${tape_right:-0} 22 | tape_left="$val $tape_left" 23 | val=$1 24 | shift 25 | tape_right=$* 26 | ;; 27 | '<'*) 28 | set -- ${tape_left:-0} 29 | tape_right="$val $tape_right" 30 | val=$1 31 | shift 32 | tape_left=$* 33 | ;; 34 | '+'*) 35 | val=$(( val + 1 )) 36 | [ "$val" -eq 256 ] && val=0 37 | ;; 38 | '-'*) 39 | val=$(( val - 1 )) 40 | [ "$val" -eq -1 ] && val=255 41 | ;; 42 | '.'*) 43 | printf "\\$(printf %o "$val")" 44 | ;; 45 | ','*) 46 | [ -z "$input" ] && read -r input && input=$input$n 47 | 48 | if [ -n "$input" ]; then 49 | val=$(printf %d "'${input%"${input#?}"}") 50 | input=${input#?} 51 | fi 52 | ;; 53 | '['*) 54 | if [ "$val" -eq 0 ]; then 55 | depth=1 56 | while [ "$depth" -gt 0 ]; do 57 | bak=${prog%"${prog#["><+-.,[]"]}"}$bak 58 | prog=${prog#?} 59 | case $prog in 60 | '['*) depth=$(( depth + 1 )) ;; 61 | ']'*) depth=$(( depth - 1 )) ;; 62 | esac 63 | done 64 | fi 65 | ;; 66 | ']'*) 67 | if [ "$val" -ne 0 ]; then 68 | depth=1 69 | while [ "$depth" -gt 0 ]; do 70 | case $bak in 71 | '['*) depth=$(( depth - 1 )) ;; 72 | ']'*) depth=$(( depth + 1 )) ;; 73 | esac 74 | prog=${bak%"${bak#?}"}$prog 75 | bak=${bak#?} 76 | done 77 | fi 78 | ;; 79 | '') 80 | break 81 | ;; 82 | *) 83 | prog=${prog#?} 84 | continue 85 | ;; 86 | esac 87 | bak=${prog%"${prog#?}"}$bak 88 | prog=${prog#?} 89 | done 90 | -------------------------------------------------------------------------------- /sh/bf1.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crestwave/bf/8bd0858590cb1679a27d1be820dfdc297a08b875/sh/bf1.sh -------------------------------------------------------------------------------- /sh/patches/toolbox.patch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crestwave/bf/8bd0858590cb1679a27d1be820dfdc297a08b875/sh/patches/toolbox.patch -------------------------------------------------------------------------------- /swift/README.md: -------------------------------------------------------------------------------- 1 | # bf.swift 2 | 3 | A brainfuck interpreter written in pure Swift 5 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - An array of 65,536 cells is initialized 10 | - The pointer wraps around when it goes out of range 11 | - Unmatched brackets are detected before runtime 12 | - Output is UTF-8-encoded 13 | -------------------------------------------------------------------------------- /swift/bf.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env swift 2 | import Foundation 3 | 4 | var program = "" 5 | 6 | if CommandLine.argc > 1 { 7 | do { 8 | program = try String(contentsOfFile: CommandLine.arguments[1]) 9 | } catch let error as NSError { 10 | let msg = "\(CommandLine.arguments[1]): \(error.localizedDescription)\n" 11 | 12 | FileHandle.standardError 13 | .write(msg.data(using: .utf8)!) 14 | 15 | exit(1) 16 | } 17 | } else { 18 | while let line = readLine() { 19 | program.append(line) 20 | } 21 | } 22 | 23 | program = program.filter("><+-.,[]".contains) 24 | 25 | var jumps = Array(repeating: 0, count: program.count) 26 | var stack: [Int] = [] 27 | 28 | for (i, c) in program.enumerated() { 29 | if c == "[" { 30 | stack.append(i) 31 | } else if c == "]" { 32 | if stack.count == 0 { 33 | FileHandle.standardError 34 | .write("error: extraneous ']'\n".data(using: .utf8)!) 35 | 36 | exit(1) 37 | } 38 | 39 | let j = stack.removeLast() 40 | 41 | jumps[i] = j 42 | jumps[j] = i 43 | } 44 | } 45 | 46 | if stack.count > 0 { 47 | FileHandle.standardError 48 | .write("error: expected ']'\n".data(using: .utf8)!) 49 | 50 | exit(1) 51 | } 52 | 53 | let len = program.count 54 | 55 | var i = 0 56 | var line = "" 57 | var ptr = 0 58 | var tape = Array(repeating: 0, count: 65536) 59 | 60 | while (i < len) { 61 | let c = program[program.index(program.startIndex, offsetBy: i)] 62 | 63 | if c == ">" { 64 | ptr = (ptr + 1) & 65535 65 | } else if c == "<" { 66 | ptr = (ptr - 1) & 65535 67 | } else if c == "+" { 68 | tape[ptr] = (tape[ptr] + 1) & 255 69 | } else if c == "-" { 70 | tape[ptr] = (tape[ptr] - 1) & 255 71 | } else if c == "." { 72 | print(Character(UnicodeScalar(tape[ptr])!), terminator: "") 73 | } else if c == "," { 74 | if line.count == 0 { 75 | let input = readLine(strippingNewline: false) 76 | 77 | if input != nil { 78 | line = input! 79 | } 80 | } 81 | 82 | if line.count > 0 { 83 | tape[ptr] = Int(UnicodeScalar(String(line 84 | .remove(at: line.startIndex)))!.value) 85 | } 86 | } else if c == "[" { 87 | if tape[ptr] == 0 { 88 | i = jumps[i] 89 | } 90 | } else if c == "]" { 91 | if tape[ptr] != 0 { 92 | i = jumps[i] 93 | } 94 | } 95 | 96 | 97 | i += 1 98 | } 99 | -------------------------------------------------------------------------------- /tcl/README.md: -------------------------------------------------------------------------------- 1 | # bf.tcl 2 | 3 | A brainfuck interpreter written in pure Tcl 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - Unmatched brackets are detected during runtime 10 | - An array of 30,000 cells to the right and 200 to the left is initialized 11 | - The locale's character set is used 12 | 13 | ## bf2.tcl Info 14 | 15 | - The program is transpiled into Tcl then evaluated 16 | - It reads from files named in all arguments 17 | - Negative pointers are allowed 18 | -------------------------------------------------------------------------------- /tcl/bf.tcl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tclsh 2 | 3 | if {$argc > 0} { 4 | set f [open [lindex $argv 0] r] 5 | set program [read $f] 6 | close $f 7 | } else { 8 | set program [read stdin] 9 | } 10 | 11 | fconfigure stdout -buffering none 12 | 13 | regsub -all {[^><+\-.,[\]]} $program "" program 14 | set program [split $program ""] 15 | set len [llength $program] 16 | set stack "" 17 | set ptr 0 18 | 19 | for {set i -200} {$i < 30000} {incr i} { 20 | set tape($i) 0 21 | } 22 | 23 | for {set i 0} {$i < $len} {incr i} { 24 | switch -- [lindex $program $i] { 25 | > { 26 | incr ptr 1 27 | } 28 | < { 29 | incr ptr -1 30 | } 31 | + { 32 | if {[incr tape($ptr) 1] == 256} { 33 | set tape($ptr) 0 34 | } 35 | } 36 | - { 37 | if {[incr tape($ptr) -1] == -1} { 38 | set tape($ptr) 255 39 | } 40 | } 41 | . { 42 | puts -nonewline [format %c $tape($ptr)] 43 | } 44 | , { 45 | set c [read stdin 1] 46 | if {$c ne ""} { 47 | set tape($ptr) [scan $c %c] 48 | } 49 | } 50 | [ { 51 | if {$tape($ptr)} { 52 | lappend stack $i 53 | } else { 54 | for {set depth 1} {$depth > 0} {incr i} { 55 | switch -- [lindex $program $i+1] { 56 | \[ { 57 | incr depth 1 58 | } 59 | \] { 60 | incr depth -1 61 | } 62 | "" { 63 | error "missing close-bracket" 64 | } 65 | } 66 | } 67 | } 68 | } 69 | ] { 70 | if {$stack eq ""} { 71 | error "missing open-bracket" 72 | } 73 | 74 | if {$tape($ptr)} { 75 | set i [lindex $stack end] 76 | } else { 77 | set stack [lrange $stack 0 end-1] 78 | } 79 | } 80 | } 81 | } 82 | 83 | if {$stack ne ""} { 84 | error "missing close-bracket" 85 | } 86 | -------------------------------------------------------------------------------- /tcl/bf2.tcl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tclsh 2 | 3 | if {$argc > 0} { 4 | foreach arg $argv { 5 | set f [open $arg r] 6 | append program [read $f] 7 | close $f 8 | } 9 | } else { 10 | set program [read stdin] 11 | } 12 | 13 | regsub -all {[^><+\-.,[\]]} $program "" program 14 | set program [split $program ""] 15 | set len [llength $program] 16 | set trans "set p 0" 17 | set open_brace "{" 18 | set close_brace "}" 19 | 20 | for {set i 0} {$i < $len} {incr i} { 21 | switch -- [lindex $program $i] { 22 | > { append trans { 23 | incr p 1 24 | }} 25 | < { append trans { 26 | incr p -1 27 | }} 28 | + { append trans { 29 | if {[incr a($p) 1] == 256} { 30 | set a($p) 0 31 | } 32 | }} 33 | - { append trans { 34 | if {[incr a($p) -1] == -1} { 35 | set a($p) 255 36 | } 37 | }} 38 | . { append trans { 39 | puts -nonewline [format %c [incr a($p) 0]] 40 | }} 41 | , { append trans { 42 | if {[set c [read stdin 1]] ne ""} { 43 | set a($p) [scan $c %c] 44 | } 45 | }} 46 | [ { 47 | append trans "\n " 48 | append trans {while {[incr a($p) 0]}} 49 | append trans " $open_brace" 50 | } 51 | ] { 52 | append trans $close_brace 53 | } 54 | } 55 | } 56 | 57 | fconfigure stdout -buffering none 58 | eval $trans 59 | -------------------------------------------------------------------------------- /vala/README.md: -------------------------------------------------------------------------------- 1 | # bf.vala 2 | 3 | A brainfuck interpreter written in pure Vala 4 | 5 | ## Info 6 | 7 | - It reads the program from a file named in the first argument 8 | - If there are no arguments, it reads from standard input 9 | - The pointer is a 16-bit unsigned integer 10 | - Unmatched brackets are detected before runtime 11 | - Output is ASCII-encoded 12 | -------------------------------------------------------------------------------- /vala/bf.vala: -------------------------------------------------------------------------------- 1 | int main (string[] args) { 2 | string program; 3 | 4 | if (args.length > 1) { 5 | try { 6 | FileUtils.get_contents (args[1], out program); 7 | } catch (FileError e) { 8 | error (e.message); 9 | } 10 | } else { 11 | var builder = new StringBuilder (); 12 | string line; 13 | while ((line = stdin.read_line ()) != null) { 14 | builder.append (line); 15 | } 16 | program = builder.str; 17 | stdin.clearerr (); 18 | } 19 | 20 | try { 21 | Regex r = new Regex ("""[^><+\-.,[\]]"""); 22 | program = r.replace (program, program.length, 0, ""); 23 | } catch (RegexError e) { 24 | error (e.message); 25 | } 26 | 27 | size_t length = program.length; 28 | int[] stack = new int[length]; 29 | int[] jumps = new int[length]; 30 | uint16 ptr = 0; 31 | 32 | for (int i = 0; i < length; ++i) { 33 | switch (program[i]) { 34 | case '[': 35 | stack[++ptr] = i; 36 | break; 37 | case ']': 38 | if (ptr < 1) { 39 | error ("expected `]'"); 40 | } 41 | jumps[stack[ptr]] = i; 42 | jumps[i] = stack[ptr--]; 43 | break; 44 | } 45 | } 46 | 47 | if (ptr > 0) { 48 | error ("unexpected `['"); 49 | } 50 | 51 | char tape[uint16.MAX+1]; 52 | int c; 53 | 54 | for (int i = 0; i < length; ++i) { 55 | switch (program[i]) { 56 | case '<': 57 | --ptr; 58 | break; 59 | case '>': 60 | ++ptr; 61 | break; 62 | case '+': 63 | tape[ptr] += 1; 64 | break; 65 | case '-': 66 | tape[ptr] -= 1; 67 | break; 68 | case '.': 69 | stdout.putc (tape[ptr]); 70 | stdout.flush (); 71 | break; 72 | case ',': 73 | c = stdin.getc (); 74 | if (stdin.eof () == false) { 75 | tape[ptr] = (char) c; 76 | } else { 77 | stdin.clearerr (); 78 | } 79 | break; 80 | case '[': 81 | if (tape[ptr] == 0) { 82 | i = jumps[i]; 83 | } 84 | break; 85 | case ']': 86 | if (tape[ptr] != 0) { 87 | i = jumps[i]; 88 | } 89 | break; 90 | } 91 | } 92 | 93 | return 0; 94 | } 95 | --------------------------------------------------------------------------------