src/index

Author:xlxs4
Version:0.1.0

Description

This is a toy interpreter for the brainfuck programming language written fully in Nim. It doubles as a transpiler of brainfuck into efficient Nim code.


Example:

import brainfuck, streams

interpret("++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.")
# Prints "Hello"

proc mandelbrot = compileFile("examples/mandelbrot.b")
mandelbrot() # Draws a mandelbrot set in ASCII

Introduction

Brainfuck is a very small esoteric programming language. It's one of the most famous esoteric languages and has inspired many esoteric programming language creators.

Brainfuck only consists of eight simple commands. It operates on a tape (array) of memory cells, each initially set to zero:

There's also a pointer, initially pointing to the first cell:

In other words, there's a data pointer and an instruction pointer (program counter). The commands are the following:

  • >: Move the pointer to the right
  • <: Move the pointer to the left
  • +: Increment the memory cell at the pointer
  • -: Decrement the memory cell at the pointer
  • .: Output the character signified by the cell at the pointer
  • ,: Input a character and store it in the cell at the pointer
  • [: Jump past the matching ] if the cell at the pointer is zero
  • ]: Jump back to the matching [ if the cell at the pointer is nonzero

Or, if you want to be more technical:

  • >: Increment the data pointer (to point to the next cell to the right)
  • <: Decrement the data pointer (to point to the next cell to the left)
  • +: Increment the byte at the data pointer
  • -: Decrement the byte at the data pointer
  • .: Output the byte at the data pointer
  • ,: Accept one byte of input, storing its value in the byte at the data pointer
  • [: If the byte at the data pointer is zero, then instead of moving the instruction pointer forward to the next command, jump it forward to the command after the matching ] command
  • ]: If the byte at the data pointer is nonzero, then instead of moving the instruction pointer forward to the next command, jump it back to the command after the matching [ command

Usage

Usage:

brainfuck mandelbrot
brainfuck interpret [<file.b>]
brainfuck (-h | --help)
brainfuck (-v | --version)

Options:

-h --help     Show this screen.
-v --version  Show version.

Building

  1. install nim
  2. git clone https://github.com/xlxs4/nim-brainfuck-interpreter.git
  3. cd nim-brainfuck-interpreter/
  4. nimble build

After that you will get a ready-made binary file in the root directory of the project.

Examples

  • collatz.b: Receives number as input, outputs the number of steps from that number to zero or one, following the rules of the Collatz conjecture.
  • dbf2c.b: Translates brainfuck to C.
  • dbfi.b: Brainfuck interpreter, in brainfuck. ... I know, right? Right !?
  • factorial.b: Computes and outputs the factorials (A000132 OEIS).
  • helloworld.b: Outputs "Hello World!".
  • impeccable.b: Computes and outputs the impeccable sequence (A014221 OEIS).
  • jabh.b: Outputs "Just another brainfuck hacker,". Based on JAPH.
  • life.b: Simulates the Game of Life cellular automaton. It duplicates the interface of the classic program at http://www.linusakesson.net/programming/brainfuck/index.php.
  • mandelbrot.b: Outputs an ASCII interpretation of the Mandelbrot set.
  • numwrap.b: Receives a number as input, outputs that number, well, wrapped?
  • prime.b: Receives a number as input, outputs all the prime numbers up to the input number.
  • random.b: A RNG.
  • rot13.b: Receives input, outputs the input transformed using the ROT13 version of the Ceasar cipher.
  • sierpinski.b: Outputs an ASCII interpretation of the Sierpinski triangle.
  • squares-endless.b: Outputs the square numbers.
  • squares.b: Outputs square numbers from 0 to 10000.
  • thuemorse.b: Outputs the Thue-Morse sequence.
  • tictactoe.b: Plays tic-tac-toe, after the first move which is already given.
  • utm.b: A universal Turing machine (UTM) from Yurii Rogozhin's article "Small universal Turing machines", in Theoretical Computer Science, 168(2):215-240, 20 November 1996.

Tests

The pre-defined test Nimble task is used: see here. There are three test files: tbf.nim, tcompile.nim and tinterpret.nim:

  • tbf.nim tests that the implemented tape is at least 30000 cells long, as per the original standard, and for various obscure problems in brainfuck implementations (thanks http://brainfuck.org/tests.b!).
  • tcompile.nim uses the helloworld.b and rot13.b examples to test the transpiler.
  • tinterpret.nim uses the helloworld.b and rot13.b examples to test the interpreter.

Procs

proc interpret(code, input: string): string {....raises: [IOError, OSError],
    tags: [WriteIOEffect, ReadIOEffect].}

Interpret the brainfuck code string, reading from the input and returning the result directly.

Example:

echo interpret(readFile("examples/rot13.b"), "Hello World!\n")
proc interpret(code: string) {....raises: [IOError, OSError],
                               tags: [WriteIOEffect, ReadIOEffect].}

Interpret the brainfuck code, reading from stdin and writing to stdout.

Example:

interpret(readFile("examples/rot13.b"))
proc interpret(code: string; input, output: Stream) {.
    ...raises: [IOError, OSError], tags: [WriteIOEffect, ReadIOEffect].}

Interpret the brainfuck code string, reading from the input and writing to the output stream.

Example:

var inpStream = newStringStream("Hello World!\n")
var outStream = newFileStream(stdout)
interpret(readFile("examples/rot13.b"), inpStream, outStream)
proc readCharEOF(input: Stream): char {....raises: [IOError, OSError],
                                        tags: [ReadIOEffect].}
Read a character from input stream and return a Unix EOF (-1). This is necessary because brainfuck assumes Unix EOF while streams use 0 for EOF.
proc xdec(c: var char) {....raises: [], tags: [].}
Decrement a character with wrapping instead of underflow checks.
proc xinc(c: var char) {....raises: [], tags: [].}
Increment a character with wrapping instead of overflow checks.

Macros

macro compileFile(filename: string)

Compile the brainfuck code read from filename at compile time into Nim code that reads from stdin and writes to stdout.

Example:

proc mandelbrot = compileFile("examples/mandelbrot.b")
mandelbrot()
macro compileFile(filename: string; input, output: untyped)

Compile the brainfuck code read from filename at compile time into Nim code that reads from the input variable and writes to the output variable, both of which have to be strings.

Example:

proc rot13(input: string): string =
  compileFile("examples/rot13.b", input, result)
echo rot13("Hello World!\n")
macro compileString(code: string)
Compile the brainfuck code string into Nim code that reads from stdin and writes to stdout.
macro compileString(code: string; input, output: untyped)
Compile the brainfuck code read from filename at compile time into Nim code that reads from the input variable and writes to the output variable, both of which have to be strings.