Skip to content

Instantly share code, notes, and snippets.

@jnape
Last active December 15, 2015 20:19
Show Gist options
  • Select an option

  • Save jnape/5317828 to your computer and use it in GitHub Desktop.

Select an option

Save jnape/5317828 to your computer and use it in GitHub Desktop.
A Brainfuck interpreter written in Scala
package com.thoughtworks.futureeither
import scala.collection.mutable
abstract sealed class LoopOutcome {
def endOrElse(fn: => Any) = this match {
case Repeat() => fn
case _ =>
}
def repeatOrElse(fn: => Any) = this match {
case End() => fn
case _ =>
}
}
case class Repeat() extends LoopOutcome {}
case class End() extends LoopOutcome {}
class Runtime(registry: mutable.IndexedSeq[Int] = mutable.ArraySeq.fill[Int](10000000)(0)) {
private var index = 0
def incrementPointer() { updatePointer(_ + 1) }
def decrementPointer() { updatePointer(_ - 1) }
def incrementByte() { updateByte(_ + 1) }
def decrementByte() { updateByte(_ - 1) }
def output() { print(currentByte.toChar) }
def input() {
val input = readLine()
updateByte( _ => input.toInt )
}
def loop = if (currentByte == 0) End() else Repeat()
private def updatePointer(fn: (Int) => Int) {
index = fn(index)
if (index < 0 || index >= registry.size)
throw new IndexOutOfBoundsException(s"Pointer has moved outside of Registry: $index")
}
private def updateByte(fn: (Int) => Int) { registry.update(index, fn(registry(index))) }
private def currentByte = registry(index)
}
class BrainfuckInterpreter(literateSource: String, runtime: Runtime = new Runtime) {
private val TOKENS = "><+-.,[]"
private val source = literateSource filter (TOKENS contains _)
private var index = 0
val run = {
interpret(source) {
case '>' => runtime.incrementPointer()
case '<' => runtime.decrementPointer()
case '+' => runtime.incrementByte()
case '-' => runtime.decrementByte()
case '.' => runtime.output()
case ',' => runtime.input()
case '[' => {
runtime.loop repeatOrElse scanToCorrespondingBrace(']')
}
case ']' => {
runtime.loop endOrElse scanToCorrespondingBrace('[')
}
}
}
private def interpret(source: String)(instructionBindings: PartialFunction[Char, Any]) {
while (index < source.length) {
instructionBindings(currentInstruction)
index += 1
}
}
private def scanToCorrespondingBrace(instruction: Char) {
val operation = instruction match {
case ']' => (x:Int) => x + 1
case '[' => (x:Int) => x - 1
}
var loopLevel = operation(0)
while (loopLevel != 0) {
index = operation(index)
if (currentInstruction == '[')
loopLevel += 1
else if (currentInstruction == ']')
loopLevel -= 1
}
}
private def currentInstruction = source(index)
}
object Example {
def main(args: Array[String]) {
new BrainfuckInterpreter( """
+++++ +++++ initialize counter (cell #0) to 10
[ use loop to set the next four cells to 70/100/30/10
> +++++ ++ add 7 to cell #1
> +++++ +++++ add 10 to cell #2
> +++ add 3 to cell #3
> + add 1 to cell #4
<<<< - decrement counter (cell #0)
]
> ++ . print 'H'
> + . print 'e'
+++++ ++ . print 'l'
. print 'l'
+++ . print 'o'
> ++ . print ' '
<< +++++ +++++ +++++ . print 'W'
> . print 'o'
+++ . print 'r'
----- - . print 'l'
----- --- . print 'd'
> + . print '!'
> . print '\n'
""").run
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment