Last active
August 29, 2015 14:17
-
-
Save JadenGeller/15a05124671f9130df48 to your computer and use it in GitHub Desktop.
Brainfuck in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension Dictionary { | |
init(_ elements: [Element]){ | |
self.init() | |
for (k, v) in elements { | |
self[k] = v | |
} | |
} | |
func map<U>(transform: Value -> U) -> [Key : U] { | |
return Dictionary<Key, U>(Swift.map(self, { (key, value) in (key, transform(value)) })) | |
} | |
} | |
struct InfiniteArray<T : Equatable> : Printable, SequenceType { | |
private var backing = [Int : T]() | |
private let defaultValue: T | |
init(defaultValue: T){ | |
self.defaultValue = defaultValue | |
} | |
private init(defaultValue: T, backing: [Int : T]){ | |
self.defaultValue = defaultValue | |
self.backing = backing | |
} | |
mutating func removeAll(keepCapacity: Bool = false) { | |
backing.removeAll(keepCapacity: keepCapacity) | |
} | |
func map<U>(transform: T -> U) -> InfiniteArray<U> { | |
return InfiniteArray<U>(defaultValue: transform(defaultValue), backing: backing.map(transform)) | |
} | |
subscript (index: Int) -> T { | |
get { | |
return backing[index] ?? defaultValue | |
} | |
set { | |
if newValue == defaultValue { | |
backing.removeValueForKey(index) | |
} | |
else { | |
backing[index] = newValue | |
} | |
} | |
} | |
subscript(subRange: Range<Int>) -> [T] { | |
var arr = [T]() | |
for i in subRange { | |
arr.append(self[i]) | |
} | |
return arr | |
} | |
var isEmpty: Bool { | |
get { | |
return backing.isEmpty | |
} | |
} | |
var description: String { | |
get { | |
var str = "[" | |
for (i, value) in backing { | |
str += "\(i): \(value), " | |
} | |
str += "deafult: \(defaultValue)]}" | |
return str | |
} | |
} | |
func generate() -> InfiniteArrayGenerator<T> { | |
return InfiniteArrayGenerator(array: self) | |
} | |
} | |
struct InfiniteArrayGenerator<T : Equatable> : GeneratorType { | |
private var array: InfiniteArray<T> | |
init(array: InfiniteArray<T>){ | |
self.array = array | |
} | |
private var index: Int = 0 | |
mutating func next() -> T? { | |
return array[index++] | |
} | |
} | |
typealias Byte = UInt8 | |
class InfiniteMemory : Printable { | |
internal var backing = InfiniteArray<Byte>(defaultValue: 0) | |
var startPointer: Pointer { | |
get { | |
return Pointer(index: 0, memory: self) | |
} | |
} | |
var description: String { | |
get { | |
return backing.description | |
} | |
} | |
struct Pointer { | |
var index: Int | |
let memory: InfiniteMemory | |
init(index: Int, memory: InfiniteMemory) { | |
self.index = index | |
self.memory = memory | |
} | |
var value: Byte { | |
get { | |
return memory.backing[index] | |
} | |
set { | |
memory.backing[index] = newValue | |
} | |
} | |
var isZero : Bool { | |
get { | |
return value == 0 | |
} | |
} | |
mutating func incrementPointer() { | |
index++ | |
} | |
mutating func decrementPointer() { | |
index-- | |
} | |
mutating func incrementValue() { | |
value = value &+ 1 | |
} | |
mutating func decrementValue() { | |
value = value &- 1 | |
} | |
func printValue(ascii: Bool = true) { | |
if ascii { | |
print(Character(UnicodeScalar(Int(value)))) | |
} | |
else { | |
println(value) | |
} | |
} | |
mutating func readValue() { | |
let byte = malloc(sizeof(Byte)) | |
let readLength = UnsafeMutablePointer<Int>.alloc(1) | |
readLength.initialize(0) | |
value = Byte(NSString(data:NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)!.intValue) | |
byte.dealloc(sizeof(Byte)) | |
readLength.dealloc(1) | |
} | |
} | |
} | |
extension Array { | |
func appended(value: T) -> [T] { | |
var copy = self | |
copy.append(value) | |
return copy | |
} | |
} | |
postfix operator ^ { } | |
postfix operator ~ { } | |
postfix operator + { } | |
postfix operator - { } | |
postfix operator % { } | |
postfix operator & { } | |
postfix operator / { } | |
postfix operator | { } | |
enum BrainfuckCommand : Printable { | |
case IncrementPointer // ^ | |
case DecrementPointer // ~ | |
case IncrementValue // + | |
case DecrementValue // - | |
case Print // % | |
case Read // & | |
case StartLoop // / | |
case EndLoop // | | |
var description: String { | |
get { | |
switch self { | |
case IncrementPointer: return "^" | |
case DecrementPointer: return "~" | |
case IncrementValue: return "+" | |
case DecrementValue: return "-" | |
case Print: return "%" | |
case Read: return "&" | |
case StartLoop: return "/" | |
case EndLoop: return "|" | |
} | |
} | |
} | |
} | |
class BrainfuckProgram { | |
// Set true to print all computation steps | |
let debug = false | |
private var complementaryBracket: [Int : Int]! = nil | |
private let commands: [BrainfuckCommand] | |
init?(commands: [BrainfuckCommand]){ | |
self.commands = commands | |
if let found = findComplementaryBrackets(commands) { | |
self.complementaryBracket = found | |
} | |
else { | |
return nil | |
} | |
} | |
func run(ascii: Bool = true) -> InfiniteMemory { | |
var memory = InfiniteMemory() | |
var instructionIndex = 0 | |
var pointer = memory.startPointer | |
while instructionIndex < commands.count { | |
let commandInstructionIndex = instructionIndex | |
let command = commands[instructionIndex++] | |
if debug { println("(index: \(instructionIndex), instruction: \(command)), (index: \(pointer.index), value: \(pointer.value))") } | |
switch command { | |
case .IncrementPointer: pointer.incrementPointer() | |
case .DecrementPointer: pointer.decrementPointer() | |
case .IncrementValue: pointer.incrementValue() | |
case .DecrementValue: pointer.decrementValue() | |
case .Print: pointer.printValue(ascii: ascii) | |
case .Read: pointer.readValue() | |
case .StartLoop: | |
if pointer.isZero { | |
instructionIndex = complementaryBracket[commandInstructionIndex]! + 1 | |
} | |
case .EndLoop: | |
if !pointer.isZero { | |
instructionIndex = complementaryBracket[commandInstructionIndex]! + 1 | |
} | |
} | |
} | |
return memory | |
} | |
func findComplementaryBrackets(allcommands: [BrainfuckCommand]) -> [Int : Int]? { | |
var found = [Int : Int]() | |
var stack = [Int]() | |
for (index, command) in enumerate(allcommands) { | |
switch command { | |
case .StartLoop: | |
stack.append(index) | |
case .EndLoop: | |
if stack.last != nil { | |
let openingIndex = stack.removeLast() | |
found[openingIndex] = index | |
found[index] = openingIndex | |
} | |
else { | |
return nil // Bad syntax, unopened close bracket | |
} | |
default: continue | |
} | |
} | |
if stack.count != 0 { | |
return nil // Bad syntax, unclosed open bracket | |
} | |
return found | |
} | |
} | |
postfix func ^(var lhs: [BrainfuckCommand]) -> [BrainfuckCommand] { return lhs.appended(.IncrementPointer) } | |
postfix func ~(var lhs: [BrainfuckCommand]) -> [BrainfuckCommand] { return lhs.appended(.DecrementPointer) } | |
postfix func +(var lhs: [BrainfuckCommand]) -> [BrainfuckCommand] { return lhs.appended(.IncrementValue) } | |
postfix func -(var lhs: [BrainfuckCommand]) -> [BrainfuckCommand] { return lhs.appended(.DecrementValue) } | |
postfix func %(var lhs: [BrainfuckCommand]) -> [BrainfuckCommand] { return lhs.appended(.Print) } | |
postfix func &(var lhs: [BrainfuckCommand]) -> [BrainfuckCommand] { return lhs.appended(.Read) } | |
postfix func /(var lhs: [BrainfuckCommand]) -> [BrainfuckCommand] { return lhs.appended(.StartLoop) } | |
postfix func |(var lhs: [BrainfuckCommand]) -> [BrainfuckCommand] { return lhs.appended(.EndLoop) } | |
let fuck = [BrainfuckCommand]() | |
let Brain = { x in BrainfuckProgram(commands: x) } | |
// Examples | |
// Count down from 3 | |
Brain((((((((fuck+)+)+)+)/)-)%)|).run(ascii: false) | |
// -> 3 -> 2 -> 1 -> 0 | |
// Multiply 6 and 7 | |
Brain((((((((((((((((((((fuck+)+)+)+)+)+)/)^)+)+)+)+)+)+)+)~)-)|)^)%).run(ascii: false) | |
// -> 42 | |
// Print uppercase 'A' | |
Brain((((((((((((((((((((((((((((fuck+)+)+)+)+)+)/)^)+)+)+)+)+)+)+)+)+)+)~)-)|)^)+)+)+)+)+)%).run(ascii: true) | |
// -> A | |
// Prints my bf's name | |
Brain(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((fuck+)+)+)+)+)+)/)^)+)+)+)+)+)/)^)+)+)^)+)+)+)+)^)+)+)+)^)+)+)+)+)^)+)+)+)^)+)+)+)+)^)+)+)+)+)^)+)+)^)+)+)^)~)~)~)~)~)~)~)~)~)~)-)|)^)+)^)-)^)+)^)-)-)^)+)^)-)-)^)-)-)^)^)-)-)^)+)+)~)~)~)~)~)~)~)~)~)~)~)-)|)^)^)%)^)%)^)+)%)^)+)+)%)^)+)+)+)+)%)^)+)+)+)%)^)+)+)%)^)%)^)+)+)+)%)^)-)-)%)?.run(ascii: true) | |
// -> Brandon<3 | |
// Print the frist 10 fibonacci numbers | |
Brain(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((fuck+)+)+)+)+)+)+)+)+)+)^)+)^)+)~)~)/)^)^)/)-)^)+)^)+)~)~)|)~)/)-)^)^)^)+)~)~)~)|)^)^)^)/)-)~)~)+)^)^)|)~)/)-)~)~)+)^)^)|)~)~)%)~)-)|)?.run(ascii: false) | |
// 1 -> 2 -> 3 -> 5 -> 8 -> 13 -> 21 -> 34 -> 55 -> 89 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Encodes a string so you no longer have to deal with the weird prefix sytax | |
func unpack<T, S : SequenceType where S.Generator.Element == Optional<T>>(source: S) -> SequenceOf<T> { | |
return SequenceOf(lazy(source).filter{ x in x != nil }.map{ x in x! }) | |
} | |
func encode(code: String) -> [BrainfuckCommand] { | |
return Array(unpack(lazy(code).map{ c in | |
switch c { | |
case ">": return .IncrementPointer | |
case "<": return .DecrementPointer | |
case "+": return .IncrementValue | |
case "-": return .DecrementValue | |
case ".": return .Print | |
case ",": return .Read | |
case "[": return .StartLoop | |
case "]": return .EndLoop | |
default: return nil | |
} | |
})) | |
} | |
if let x = BrainfuckProgram(commands: encode("++++++[>+++++[>++>++++>+++>++++>+++>++++>++++>++>++><<<<<<<<<<-]>+>->+>-->+>-->-->>-->++<<<<<<<<<<<-]>>.>.>+.>++.>++++.>+++.>++.>.>+++.>--.")) { | |
x.run(ascii: true) // -> Brandon<3 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment