Skip to content

Instantly share code, notes, and snippets.

@Danappelxx
Created March 15, 2016 17:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Danappelxx/86031868034f6a5306ea to your computer and use it in GitHub Desktop.
Save Danappelxx/86031868034f6a5306ea to your computer and use it in GitHub Desktop.
A simple recursive Brainfuck parser & interpreter (`,` not supported).
extension String {
subscript(safe index: String.CharacterView.Index) -> Character? {
guard index < endIndex else { return nil }
return self[index]
}
}
public final class Brainfuck {
public enum Token {
case IncrementPointer
case DecrementPointer
case IncrementByte
case DecrementByte
case Print
case Loop([Token])
public var description: String {
switch self {
case .IncrementPointer: return ">"
case .DecrementPointer: return "<"
case .IncrementByte: return "+"
case .DecrementByte: return"-"
case .Print: return "."
case .Loop(let tokens): return "[" + tokens.map { $0.description }.reduce("", combine: +) + "]"
}
}
}
public private(set) var pointer = 0
public private(set) var cells = [Int](count: 100, repeatedValue: 0)
let tokens: [Token]
public init(tokens: [Token]) {
self.tokens = tokens
}
public convenience init(input: String) {
self.init(tokens: Brainfuck.parse(input))
}
public func evaluate() -> [Int] {
return self.evaluate(tokens: self.tokens)
}
private func evaluate(tokens tokens: [Token]) -> [Int] {
var output = [Int]()
for token in tokens {
switch token {
case .IncrementPointer: pointer += 1
case .DecrementPointer: pointer -= 1
case .IncrementByte: cells[pointer] += 1
case .DecrementByte: cells[pointer] -= 1
case .Print: output.append(cells[pointer])
case .Loop(let loop):
while cells[pointer] != 0 {
evaluate(tokens: loop)
}
}
}
return output
}
static func parse(input: String) -> [Token] {
var index = input.startIndex
var tokens = [Token]()
while let char = input[safe: index] {
switch char {
case ".": tokens.append(.Print)
case ">": tokens.append(.IncrementPointer)
case "<": tokens.append(.DecrementPointer)
case "+": tokens.append(.IncrementByte)
case "-": tokens.append(.DecrementByte)
case "[":
let startIndex = index.successor()
let endIndex: String.CharacterView.Index = {
var i = startIndex
var opened = 1
while opened > 0 {
i = i.successor()
if input[i] == "[" {
opened += 1
}
if input[i] == "]" {
opened -= 1
}
}
return i
}()
let loop = parse(input[startIndex...endIndex])
tokens.append(.Loop(loop))
index = endIndex
case "]": break // ignore, above case should handle it
default: break // ignore all other characters
}
index = index.successor()
}
return tokens
}
}
let addTwo = "+++>++++<[->+<]>."
let helloWorld = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."
let brainfuck = Brainfuck(input: helloWorld)
let output = brainfuck.evaluate()
let outputString = output.map { String(Character(UnicodeScalar($0))) } // -> Hello World!\n
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment