├── img ├── fibonacci_x10.png └── braincopter2_x10.png ├── Brainfuck ├── BrainfuckTests │ ├── fibonacci.png │ ├── braincopter1.png │ ├── braincopter2.png │ ├── Info.plist │ └── BrainfuckTests.swift ├── Brainfuck.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ ├── BrainfuckTests.xcscheme │ │ │ └── Brainfuck.xcscheme │ └── project.pbxproj └── Brainfuck │ ├── Braincopter.swift │ ├── main.swift │ ├── Brainfuck.swift │ └── Brainloller.swift ├── LICENSE ├── .gitignore └── README.md /img/fibonacci_x10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/Brainfuck/master/img/fibonacci_x10.png -------------------------------------------------------------------------------- /img/braincopter2_x10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/Brainfuck/master/img/braincopter2_x10.png -------------------------------------------------------------------------------- /Brainfuck/BrainfuckTests/fibonacci.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/Brainfuck/master/Brainfuck/BrainfuckTests/fibonacci.png -------------------------------------------------------------------------------- /Brainfuck/BrainfuckTests/braincopter1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/Brainfuck/master/Brainfuck/BrainfuckTests/braincopter1.png -------------------------------------------------------------------------------- /Brainfuck/BrainfuckTests/braincopter2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/Brainfuck/master/Brainfuck/BrainfuckTests/braincopter2.png -------------------------------------------------------------------------------- /Brainfuck/Brainfuck.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Brainfuck/Brainfuck.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Brainfuck/BrainfuckTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Brainfuck/Brainfuck/Braincopter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Braincopter.swift 3 | // Brainfuck 4 | // 5 | // Created by Nicolas Seriot on 06.05.17. 6 | // Copyright © 2017 Nicolas Seriot. All rights reserved. 7 | // 8 | 9 | // https://esolangs.org/wiki/Braincopter 10 | 11 | import Foundation 12 | 13 | class Braincopter: Brainloller { 14 | 15 | override func readInstruction(r: UInt8, g: UInt8, b:UInt8) -> String? { 16 | 17 | let command = (65536 * Int(r) + 256 * Int(g) + Int(b)) % 11 18 | 19 | switch command { 20 | case 0: 21 | return ">" 22 | case 1: 23 | return "<" 24 | case 2: 25 | return "+" 26 | case 3: 27 | return "-" 28 | case 4: 29 | return "." 30 | case 5: 31 | return "," 32 | case 6: 33 | return "[" 34 | case 7: 35 | return "]" 36 | case 8: 37 | return "CW" 38 | case 9: 39 | return "CCW" 40 | default: 41 | return nil 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nicolas Seriot 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /Brainfuck/Brainfuck/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // Brainfuck 4 | // 5 | // Created by Nicolas Seriot on 01.05.17. 6 | // Copyright © 2017 Nicolas Seriot. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let helloWorld = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." 12 | 13 | func f1() { 14 | let b = try! Brainfuck(helloWorld) 15 | let result = try! b.run() 16 | print(result) 17 | } 18 | 19 | func f2() { 20 | let b = try! Brainfuck(helloWorld, userInput: "", dataSize: 10) 21 | let result = try! b.run() 22 | print(result) 23 | } 24 | 25 | func f3() { 26 | do { 27 | let b = try Brainfuck(helloWorld) 28 | 29 | while b.canRun() { 30 | if let s = try b.step() { 31 | print(s) 32 | } 33 | } 34 | } catch let e { 35 | print(e) 36 | } 37 | } 38 | 39 | func f4() { 40 | do { 41 | let b = try Brainfuck("++++++[>++++++<-]>.") // 6x6 == 0x24 == '$' 42 | 43 | while b.canRun() { 44 | print("------------------------------------------------------------------------------------") 45 | b.printStep() 46 | b.printInstructions() 47 | b.printData(upToIndex: 10) 48 | 49 | if let putByte = try b.step() { 50 | print(" PUT: " + String(format: "%02X", putByte)) 51 | // let s = Character(UnicodeScalar(putByte)) 52 | // print(s, separator: "", terminator: "") 53 | } 54 | } 55 | 56 | print("------------------------------------------------------------------------------------") 57 | b.printExecutionSummary() 58 | 59 | // print(b.outputString()) 60 | 61 | } catch let e { 62 | print(e) 63 | } 64 | } 65 | 66 | func f5() { 67 | let path = "/tmp/x.png" 68 | let bl = try! Brainloller(imagePath: path) 69 | let (coords, s1) = bl.brainfuck() 70 | print(s1) 71 | 72 | let outPath = "/Users/nst/Desktop/out.png" 73 | bl.magnifiedProgramWithTrace(programPath: path, outPath: outPath, coordinates: coords) 74 | 75 | let bf = try! Brainfuck(s1) 76 | let s2 = try! bf.run() 77 | print(coords) 78 | print(s2) 79 | } 80 | 81 | func f6() { 82 | let path = "/Users/nst/Desktop/braincopter1.png" 83 | let bl = try! Braincopter(imagePath: path) 84 | let (_, s1) = bl.brainfuck() 85 | print(s1) 86 | 87 | let bf = try! Brainfuck(s1) 88 | let s2 = try! bf.run() 89 | print(s2) 90 | } 91 | 92 | f1() 93 | f2() 94 | f3() 95 | f4() 96 | 97 | //f5() 98 | //f6() 99 | -------------------------------------------------------------------------------- /Brainfuck/Brainfuck.xcodeproj/xcshareddata/xcschemes/BrainfuckTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 16 | 18 | 24 | 25 | 26 | 27 | 28 | 38 | 40 | 46 | 47 | 48 | 49 | 55 | 56 | 62 | 63 | 64 | 65 | 67 | 68 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /Brainfuck/Brainfuck.xcodeproj/xcshareddata/xcschemes/Brainfuck.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Brainfuck/Brainfuck/Brainfuck.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Brainfuck.swift 3 | // Brainfuck 4 | // 5 | // Created by Nicolas Seriot on 01.05.17. 6 | // Copyright © 2017 Nicolas Seriot. All rights reserved. 7 | // 8 | 9 | // https://esolangs.org/wiki/Brainfuck 10 | 11 | import Foundation 12 | 13 | class Brainfuck: NSObject { 14 | 15 | enum Instruction { 16 | case moveRight 17 | case moveLeft 18 | case increment 19 | case decrement 20 | case put 21 | case get 22 | case loopStart(to:Int) 23 | case loopStop(from:Int) 24 | 25 | init?(rawValue c: Character) { 26 | switch c { 27 | case ">": self = .moveRight 28 | case "<": self = .moveLeft 29 | case "+": self = .increment 30 | case "-": self = .decrement 31 | case ".": self = .put 32 | case ",": self = .get 33 | case "[": self = .loopStart(to:0) 34 | case "]": self = .loopStop(from:0) 35 | default: return nil 36 | } 37 | } 38 | 39 | func description(showLoopMatch:Bool = false) -> String { 40 | switch self { 41 | case .moveRight: return ">" 42 | case .moveLeft: return "<" 43 | case .increment: return "+" 44 | case .decrement: return "-" 45 | case .put: return "." 46 | case .get: return "," 47 | case let .loopStart(to): return showLoopMatch ? "[\(to)" : "[" 48 | case let .loopStop(from): return showLoopMatch ? "\(from)]" : "]" 49 | } 50 | } 51 | } 52 | 53 | var stepCounter : Int = 0 54 | 55 | // program 56 | var instructions : [Instruction] = [] 57 | var ip : Int = 0 // instruction pointer 58 | 59 | // data 60 | var data : [UInt8] = [] 61 | var dp : Int = 0 // data pointer 62 | 63 | // I/O 64 | var input = [UInt8]("".utf8) 65 | var output = [UInt8]("".utf8) 66 | 67 | enum BFError: Error { 68 | case LoopStartUnbalanced(index:Int) 69 | case LoopStopUnbalanced(index:Int) 70 | case DataPointerBelowZero(ip:Int) 71 | case DataPointerBeyondBounds(ip:Int) 72 | case CannotReadEmptyInputBuffer(ip:Int) 73 | } 74 | 75 | init(_ s: String, userInput: String = "", dataSize: Int = 30000) throws { 76 | 77 | // 1. initialize data 78 | self.data = Array(repeating: 0, count: dataSize) 79 | 80 | // 2. sanitize instructions 81 | self.instructions = s.compactMap { Instruction(rawValue:$0) } 82 | 83 | // 3. store user input 84 | self.input = [UInt8](userInput.utf8) 85 | 86 | // 4. associate matching indices to loops start and end 87 | 88 | var loopStartStack : [Int] = [] 89 | 90 | for (i, instruction) in instructions.enumerated() { 91 | switch instruction { 92 | case .loopStart: 93 | loopStartStack.append(i) 94 | case .loopStop: 95 | guard let loopStartIndex = loopStartStack.popLast() else { throw BFError.LoopStopUnbalanced(index:i) } 96 | instructions[loopStartIndex] = .loopStart(to: i) 97 | instructions[i] = .loopStop(from: loopStartIndex) 98 | default: 99 | () 100 | } 101 | } 102 | 103 | // 5. throw if unbalanced brackets 104 | if let unmatchedStartIndex = loopStartStack.first { 105 | throw BFError.LoopStartUnbalanced(index: unmatchedStartIndex) 106 | } 107 | } 108 | 109 | func canRun() -> Bool { 110 | return ip < instructions.count 111 | } 112 | 113 | func run() throws -> String { 114 | while self.canRun() { 115 | _ = try self.step() 116 | } 117 | return self.outputString() 118 | } 119 | 120 | func step() throws -> UInt8? { 121 | 122 | assert(ip < instructions.count) 123 | 124 | stepCounter += 1 125 | 126 | var putByte : UInt8? = nil 127 | 128 | let i = instructions[ip] 129 | 130 | switch i { 131 | case .moveRight: 132 | dp += 1 133 | case .moveLeft: 134 | dp -= 1 135 | case .increment: 136 | data[dp] = data[dp] &+ 1 137 | case .decrement: 138 | data[dp] = data[dp] &- 1 139 | case .put: 140 | let byte = data[dp] 141 | output.append(byte) 142 | putByte = byte 143 | case .get: 144 | guard input.count > 0 else { throw BFError.CannotReadEmptyInputBuffer(ip:ip) } // TODO: be interactive instead? 145 | data[dp] = input.removeFirst() 146 | case let .loopStart(to): 147 | if data[dp] == 0 { 148 | ip = to 149 | } 150 | case let .loopStop(from): 151 | ip = from - 1 152 | } 153 | 154 | ip += 1 155 | 156 | if dp < 0 { 157 | throw BFError.DataPointerBelowZero(ip:ip) 158 | } else if dp >= data.count { 159 | throw BFError.DataPointerBeyondBounds(ip:ip) 160 | } 161 | 162 | return putByte 163 | } 164 | } 165 | 166 | // DEBUG extension 167 | extension Brainfuck { 168 | 169 | func printStep() { 170 | print("STEP:", stepCounter) 171 | } 172 | 173 | func printData(upToIndex: Int? = nil) { 174 | var subData = data 175 | if let upIndex = upToIndex { 176 | subData = Array(data[0...upIndex]) 177 | } 178 | print("DATA:", subData.map { String(format: "%02X", $0) }.joined(separator: " ")) 179 | print("".padding(toLength: 6 + 3*dp, withPad: " ", startingAt: 0) + "^^ \(dp)") 180 | } 181 | 182 | func printInstructions() { 183 | print("PROG:", instructions.map { $0.description(showLoopMatch:false) }.joined()) 184 | print("".padding(toLength: 6 + ip, withPad: " ", startingAt: 0) + "^ \(ip)") 185 | } 186 | 187 | func printExecutionSummary() { 188 | print("SUMMARY: program stopped after \(stepCounter) step(s) with output:") 189 | 190 | let s = output.map { String(format: "%02X", $0) }.joined(separator: " ") 191 | print(" HEX: \(s)") 192 | print(" STR: \(outputString())") 193 | } 194 | 195 | func outputBuffer() -> [UInt8] { 196 | return output 197 | } 198 | 199 | func outputString() -> String { 200 | return output.map { String(UnicodeScalar(Int($0))!) }.joined() 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Brainfuck 2 | 3 | `Brainfuck.swift` is a flexible [Brainfuck](https://esolangs.org/wiki/Brainfuck) interpreter in Swift 3.1. 4 | 5 | It comes with unit tests and tracing / debuging functions. 6 | 7 | It also comes with [Brainloller](https://esolangs.org/wiki/Brainloller) and [Braincopter](https://esolangs.org/wiki/Braincopter) readers. 8 | 9 | Brainfuck commands: 10 | 11 | ``` 12 | > Move pointer right 13 | < Move pointer left 14 | + Increment memory under pointer 15 | - Decrement memory under pointer 16 | . Output value under pointer 17 | , Input value and store it under pointer 18 | [ Jump past matching ] if value under pointer is 0 19 | ] Jump back to matching [ if value under pointer is nonzero 20 | ``` 21 | 22 | Here are several ways to use Brainfuck.swift, given the following program: 23 | 24 | ```let helloWorld = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>."``` 25 | 26 | ## 1. Simple call 27 | 28 | ```Swift 29 | let b = try! Brainfuck(helloWorld) 30 | let result = try! b.run() 31 | ``` 32 | 33 | Result: `Hello World!\n` 34 | 35 | ## 2. Use optional parameters 36 | 37 | ```Swift 38 | let b = try! Brainfuck(helloWorld, userInput: "", dataSize: 32) 39 | let result = try! b.run() 40 | ``` 41 | 42 | Result: `Hello World!\n` 43 | 44 | ## 3. Call step by step 45 | 46 | ```Swift 47 | do { 48 | let b = try Brainfuck(helloWorld) 49 | 50 | while b.canRun() { 51 | if let byte = try b.step() { 52 | print(byte) 53 | } 54 | } 55 | } catch let e { 56 | print(e) 57 | } 58 | ``` 59 | 60 | Output: 61 | 62 | ``` 63 | 72 64 | 101 65 | 108 66 | 108 67 | 111 68 | 32 69 | 87 70 | 111 71 | 114 72 | 108 73 | 100 74 | 33 75 | 10 76 | ``` 77 | 78 | ## 4. Print state at each step 79 | 80 | ```Swift 81 | do { 82 | let b = try Brainfuck("++++++[>++++++<-]>.") // 6x6 == 0x24 == '$' 83 | 84 | while b.canRun() { 85 | print("-------------------------------------------------------------") 86 | b.printStep() 87 | b.printInstructions() 88 | b.printData(upToIndex: 10) 89 | 90 | if let putByte = try b.step() { 91 | print(" PUT: " + String(format: "%02X", putByte)) 92 | } 93 | } 94 | 95 | print("-------------------------------------------------------------") 96 | b.printExecutionSummary() 97 | 98 | } catch let e { 99 | print(e) 100 | } 101 | ``` 102 | 103 | Output: 104 | 105 | ``` 106 | ... 107 | ------------------------------------------------------------- 108 | STEP: 73 109 | PROG: ++++++[>++++++<-]>. 110 | ^ 17 111 | DATA: 00 24 00 00 00 00 00 00 00 00 00 112 | ^^ 0 113 | ------------------------------------------------------------- 114 | STEP: 74 115 | PROG: ++++++[>++++++<-]>. 116 | ^ 18 117 | DATA: 00 24 00 00 00 00 00 00 00 00 00 118 | ^^ 1 119 | PUT: 24 120 | ------------------------------------------------------------- 121 | SUMMARY: program stopped after 75 step(s) with output: 122 | HEX: 24 123 | STR: $ 124 | ``` 125 | 126 | ## 5. Brainloller 127 | 128 | [Brainloller](https://esolangs.org/wiki/Brainloller) stores Brainfuck commands in image pixels. 129 | 130 | Example with this code: 131 | 132 | ```Swift 133 | let bl = try! Brainloller(imagePath: "/tmp/fibonacci.png") 134 | let s1 = bl.brainfuck() 135 | print(s1) 136 | 137 | let bf = try! Brainfuck(s1) 138 | let s2 = try! bf.run() 139 | print(s2) 140 | ``` 141 | 142 | Input (magnified 10x): 143 | 144 | 145 | 146 | Brainloller commands: 147 | 148 | ``` 149 | red (255,0,0) > 150 | darkred (128,0,0) < 151 | green (0,255,0) + 152 | darkgreen (0,128,0) - 153 | blue (0,0,255) . 154 | darkblue (0,0,128) , 155 | yellow (255,255,0) [ 156 | darkyellow (128,128,0) ] 157 | cyan (0,255,255) rotates the IP 90° clockwise 158 | darkcyan (0,128,128) rotates the IP 90° counter-clockwise 159 | others n/a nop 160 | ``` 161 | 162 | Output (s1) is Brainfuck instructions: 163 | 164 | ``` 165 | ++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++>++++++++++++++++>>+<<[>>>>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>[<+>-]>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>+>>]<<<<<]>[-]>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]<[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]<<<++++++++++++++++++++++++++++++++++++++++++++++++.[-]<<<<<<<.>.>>[>>+<<-]>[>+<<+>-]>[<+>-]<<<-]<<++... 166 | ``` 167 | 168 | Output (s2) is Brainfuck code exection output: 169 | 170 | `1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 121, 98, 219, ...` 171 | 172 | ## 6. Braincopter 173 | 174 | [Braincopter](https://esolangs.org/wiki/Braincopter) is a variant of Brainloller in which commands are stored in pixels of any color. 175 | 176 | The commands are retrieved from pixels this way: 177 | 178 | ``` 179 | command = (65536 * R + 256 * G + B) % 11 180 | 181 | 0 > 182 | 1 < 183 | 2 + 184 | 3 - 185 | 4 . 186 | 5 , 187 | 6 [ 188 | 7 ] 189 | 8 rotate IP to the right 190 | 9 rotate IP to the left 191 | 10 nop 192 | ``` 193 | 194 | Example with this code: 195 | 196 | ```Swift 197 | let path = "/tmp/braincopter2.png" 198 | let bl = try! Braincopter(imagePath: path) 199 | let (_, s1) = bl.brainfuck() 200 | print(s1) 201 | 202 | let bf = try! Brainfuck(s1) 203 | let s2 = try! bf.run() 204 | print(s2) 205 | ``` 206 | 207 | Input (magnified 10x): 208 | 209 | 210 | 211 | Output (s1) is Brainfuck instructions: 212 | 213 | ``` 214 | >++++++++++[<++++++++++>-]>>>>>>>>>>>>>>>>++++[>++++<-]>[<<<<<<<++>+++>++++>++++++>+++++++>+++++++>++++>-]<++<+++++<++++++++++<+++++++++<++++++<<<<<<<<<<<<<[>+>+>[-]>>>>[-]>[-]<<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]+>---[<->[-]]<[>>>>>>.>.>..<<<<<<<<<<<<+<<[-]>>>>>>-]<<<<<[>>>>>+>+<<<<<<-]>>>>>[<<<<<+>>>>>-]+>-----[<->[-]]<[>>>>>>>>>>.<.<..<<<<<<<<<<<<+<[-]>>>>>-]<+>[-]>[-]>[-]<<<[>+>+>+<<<-]>[<+>-]+>----------[<->[-]]<[<<+>[-]>-]>[-]>[-]<<<<[>>+>+>+<<<<-]>>[<<+>>-]+>----------[<->[-]]<[<<<+>[-]>>-][-]>[-]<<<<<[>>>>+>+<<<<<-]>>>>[<<<<+>>>>-]+>[<->[-]]<[[-]>[-]<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<[>++++++++[<++++++>-]<.-.[-]][-]>[-]<<<[>>+>+<<<-]>>>[<<<+>>>-]<[>++++++++[<++++++>-]<.[-]][-]>[-]<<[>+>+<<-]>>[<<+>>-]++++++++[<++++++>-]<.[-]]>>>>.<<<<<<<<<<<-] 215 | ``` 216 | 217 | Output (s2) is Brainfuck code exection output: 218 | 219 | ``` 220 | 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buz 221 | ``` 222 | -------------------------------------------------------------------------------- /Brainfuck/BrainfuckTests/BrainfuckTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BrainfuckTests.swift 3 | // BrainfuckTests 4 | // 5 | // Created by Nicolas Seriot on 01.05.17. 6 | // Copyright © 2017 Nicolas Seriot. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | func compileAndRun(instructions: String, userInput: String = "", dataSize: Int = 1000) throws -> Brainfuck { 12 | let b = try Brainfuck(instructions, userInput: userInput, dataSize: dataSize) 13 | let _ = try b.run() 14 | return b 15 | } 16 | 17 | func bfToString(_ instructions: String) -> String { 18 | do { 19 | return try compileAndRun(instructions: instructions).outputString() 20 | } catch let e { 21 | XCTFail("\(e)") 22 | return "" 23 | } 24 | } 25 | 26 | func bfToBuffer(_ instructions: String) -> [UInt8] { 27 | do { 28 | return try compileAndRun(instructions: instructions).outputBuffer() 29 | } catch let e { 30 | XCTFail("\(e)") 31 | return [] 32 | } 33 | } 34 | 35 | class BrainfuckTests: XCTestCase { 36 | 37 | override func setUp() { 38 | super.setUp() 39 | // Put setup code here. This method is called before the invocation of each test method in the class. 40 | } 41 | 42 | override func tearDown() { 43 | // Put teardown code here. This method is called after the invocation of each test method in the class. 44 | super.tearDown() 45 | } 46 | 47 | func testPrograms() { 48 | XCTAssertEqual(bfToBuffer("+ asdasdas +++."), [4]) 49 | 50 | XCTAssertEqual(bfToBuffer("++++."), [4]) 51 | 52 | let helloWorldSingleLoop = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." 53 | XCTAssertEqual(bfToString(helloWorldSingleLoop), "Hello World!\n") 54 | 55 | let helloWorldNestedLoops = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++." 56 | XCTAssertEqual(bfToString(helloWorldNestedLoops), "Hello World!\n") 57 | 58 | let printThreeInputChars = ",.,.,." 59 | let b = try! compileAndRun(instructions: printThreeInputChars, userInput: "asd") 60 | XCTAssertEqual(b.outputString(), "asd") 61 | } 62 | 63 | func testCompilationErrors() { 64 | do { 65 | let _ = try Brainfuck(">[>") 66 | XCTFail() 67 | } catch let e { 68 | switch e { 69 | case Brainfuck.BFError.LoopStartUnbalanced(index: 1): 70 | () 71 | default: 72 | XCTFail() 73 | } 74 | } 75 | 76 | do { 77 | let _ = try Brainfuck("[]]") 78 | XCTFail() 79 | } catch let e { 80 | switch e { 81 | case Brainfuck.BFError.LoopStopUnbalanced(index: 2): 82 | () 83 | default: 84 | XCTFail() 85 | } 86 | } 87 | } 88 | 89 | func testRuntimeErrors() { 90 | do { 91 | let b = try compileAndRun(instructions: "+<+") 92 | let _ = try b.run() 93 | XCTFail() 94 | } catch let e { 95 | switch e { 96 | case Brainfuck.BFError.DataPointerBelowZero(ip: 2): 97 | () 98 | default: 99 | print(e) 100 | XCTFail() 101 | } 102 | } 103 | 104 | do { 105 | let b = try compileAndRun(instructions: "+>+>+", userInput: "", dataSize: 2) 106 | let _ = try b.run() 107 | XCTFail() 108 | } catch let e { 109 | switch e { 110 | case Brainfuck.BFError.DataPointerBeyondBounds(ip: 4): 111 | () 112 | default: 113 | print(e) 114 | XCTFail() 115 | } 116 | } 117 | 118 | do { 119 | let b = try compileAndRun(instructions: "+-,", userInput: "") 120 | let _ = try b.run() 121 | XCTFail() 122 | } catch let e { 123 | switch e { 124 | case Brainfuck.BFError.CannotReadEmptyInputBuffer(ip: 2): 125 | () 126 | default: 127 | print(e) 128 | XCTFail() 129 | } 130 | } 131 | } 132 | 133 | func testBrainloller() { 134 | let bundle = Bundle(for: type(of: self)) 135 | guard let path = bundle.pathForImageResource("fibonacci") else { 136 | XCTFail() 137 | return 138 | } 139 | 140 | do { 141 | let b = try Brainloller(imagePath: path) 142 | let (_, s) = b.brainfuck() 143 | 144 | XCTAssertEqual(s, "++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++>++++++++++++++++>>+<<[>>>>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>[<+>-]>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>+>>]<<<<<]>[-]>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]<[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]<<<++++++++++++++++++++++++++++++++++++++++++++++++.[-]<<<<<<<.>.>>[>>+<<-]>[>+<<+>-]>[<+>-]<<<-]<<++...") 145 | } catch let e { 146 | print(e) 147 | XCTFail() 148 | } 149 | } 150 | 151 | func testBraincopter1() { 152 | let bundle = Bundle(for: type(of: self)) 153 | guard let path = bundle.pathForImageResource("braincopter1") else { 154 | XCTFail() 155 | return 156 | } 157 | 158 | do { 159 | let b = try Braincopter(imagePath: path) 160 | let (_, s) = b.brainfuck() 161 | 162 | XCTAssertEqual(s, "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.") 163 | } catch let e { 164 | print(e) 165 | XCTFail() 166 | } 167 | } 168 | 169 | func testBraincopter2() { 170 | let bundle = Bundle(for: type(of: self)) 171 | guard let path = bundle.pathForImageResource("braincopter2") else { 172 | XCTFail() 173 | return 174 | } 175 | 176 | do { 177 | let b = try Braincopter(imagePath: path) 178 | let (_, s) = b.brainfuck() 179 | 180 | XCTAssertEqual(s, ">++++++++++[<++++++++++>-]>>>>>>>>>>>>>>>>++++[>++++<-]>[<<<<<<<++>+++>++++>++++++>+++++++>+++++++>++++>-]<++<+++++<++++++++++<+++++++++<++++++<<<<<<<<<<<<<[>+>+>[-]>>>>[-]>[-]<<<<<<<[>>>>>>+>+<<<<<<<-]>>>>>>[<<<<<<+>>>>>>-]+>---[<->[-]]<[>>>>>>.>.>..<<<<<<<<<<<<+<<[-]>>>>>>-]<<<<<[>>>>>+>+<<<<<<-]>>>>>[<<<<<+>>>>>-]+>-----[<->[-]]<[>>>>>>>>>>.<.<..<<<<<<<<<<<<+<[-]>>>>>-]<+>[-]>[-]>[-]<<<[>+>+>+<<<-]>[<+>-]+>----------[<->[-]]<[<<+>[-]>-]>[-]>[-]<<<<[>>+>+>+<<<<-]>>[<<+>>-]+>----------[<->[-]]<[<<<+>[-]>>-][-]>[-]<<<<<[>>>>+>+<<<<<-]>>>>[<<<<+>>>>-]+>[<->[-]]<[[-]>[-]<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<[>++++++++[<++++++>-]<.-.[-]][-]>[-]<<<[>>+>+<<<-]>>>[<<<+>>>-]<[>++++++++[<++++++>-]<.[-]][-]>[-]<<[>+>+<<-]>>[<<+>>-]++++++++[<++++++>-]<.[-]]>>>>.<<<<<<<<<<<-]") 181 | } catch let e { 182 | print(e) 183 | XCTFail() 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /Brainfuck/Brainfuck/Brainloller.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Brainloller.swift 3 | // Brainfuck 4 | // 5 | // Created by Nicolas Seriot on 02.05.17. 6 | // Copyright © 2017 Nicolas Seriot. All rights reserved. 7 | // 8 | 9 | // https://esolangs.org/wiki/Brainloller 10 | 11 | import AppKit 12 | 13 | class Brainloller: NSObject { 14 | 15 | enum Direction { 16 | case north 17 | case east 18 | case south 19 | case west 20 | 21 | mutating func rotateClockwise() { 22 | switch self { 23 | case .north: self = .east 24 | case .east: self = .south 25 | case .south: self = .west 26 | case .west: self = .north 27 | } 28 | } 29 | 30 | mutating func rotateCounterClockwise() { 31 | switch self { 32 | case .north: self = .west 33 | case .west: self = .south 34 | case .south: self = .east 35 | case .east: self = .north 36 | } 37 | } 38 | 39 | func pixels() -> (x:Int, y:Int) { 40 | switch self { 41 | case .north: return (0,-1) 42 | case .west: return (-1,0) 43 | case .south: return (0,1) 44 | case .east: return (1,0) 45 | } 46 | } 47 | } 48 | 49 | enum BLError: Error { 50 | case CannotReadPath 51 | case CannotGetImageData 52 | case CannotGetImageBitmap 53 | } 54 | 55 | var bitmap : NSBitmapImageRep 56 | var direction : Direction = .east 57 | 58 | init(imagePath: String) throws { 59 | guard let image = NSImage(byReferencingFile: imagePath) else { throw BLError.CannotReadPath } 60 | guard let tiffData = image.tiffRepresentation else { throw BLError.CannotGetImageData } 61 | guard let bitmapRep = NSBitmapImageRep(data: tiffData) else { throw BLError.CannotGetImageBitmap } 62 | 63 | self.bitmap = bitmapRep 64 | } 65 | 66 | func rgbComponents(color c: NSColor) -> (r: UInt8, g: UInt8, b: UInt8) { 67 | //let c = color.usingColorSpaceName(NSCalibratedRGBColorSpace)! 68 | 69 | let r = UInt8(c.redComponent * 255) 70 | let g = UInt8(c.greenComponent * 255) 71 | let b = UInt8(c.blueComponent * 255) 72 | 73 | return (r,g,b) 74 | } 75 | 76 | func readInstruction(r: UInt8, g: UInt8, b:UInt8) -> String? { 77 | 78 | switch (r,g,b) { 79 | case (255, 0, 0): // red 80 | return ">" 81 | case (128, 0, 0): // darkred 82 | return "<" 83 | case ( 0,255, 0): // green 84 | return "+" 85 | case ( 0,128, 0): // darkgreen 86 | return "-" 87 | case ( 0, 0,255): // blue 88 | return "." 89 | case ( 0, 0,128): // darkblue 90 | return "," 91 | case (255,255, 0): // yellow 92 | return "[" 93 | case (128,128, 0): // darkyellow 94 | return "]" 95 | case ( 0,255,255): // cyan 96 | return "CW" 97 | case ( 0,128,128): // darkcyan 98 | return "CCW" 99 | default: 100 | return nil 101 | } 102 | } 103 | 104 | func brainfuck() -> (coordinates: [(x: Int, y: Int)], brainfuck: String) { 105 | 106 | var x : Int = 0 107 | var y : Int = 0 108 | 109 | var s = "" 110 | 111 | var coords : [(x:Int, y:Int)] = [] 112 | 113 | while let color = bitmap.colorAt(x: x, y: y) { 114 | 115 | coords += [(x: x, y: y)] 116 | 117 | let (r,g,b) = rgbComponents(color: color) 118 | 119 | if let i = readInstruction(r: r, g: g, b: b) { 120 | switch i { 121 | case "CW": 122 | self.direction.rotateClockwise() 123 | case "CCW": 124 | self.direction.rotateCounterClockwise() 125 | default: 126 | s += i 127 | } 128 | } else { 129 | print("-- [\(x),\(y)] ignore (\(r),\(g),\(b))") 130 | } 131 | 132 | x += direction.pixels().x 133 | y += direction.pixels().y 134 | 135 | //print("\(x),\(y)") 136 | } 137 | 138 | return (coords, s) 139 | } 140 | 141 | func magnifiedProgramWithTrace(programPath: String, outPath: String, coordinates: [(x: Int, y: Int)]) { 142 | 143 | let FACTOR = 10 144 | 145 | let WIDTH = Int(self.bitmap.size.width) * FACTOR 146 | let HEIGHT = Int(self.bitmap.size.height) * FACTOR 147 | 148 | let imageRep = NSBitmapImageRep( 149 | bitmapDataPlanes:nil, 150 | pixelsWide: WIDTH, 151 | pixelsHigh: HEIGHT, 152 | bitsPerSample:8, 153 | samplesPerPixel:4, 154 | hasAlpha:true, 155 | isPlanar:false, 156 | colorSpaceName:NSColorSpaceName.deviceRGB, 157 | bytesPerRow: WIDTH * 4, 158 | bitsPerPixel:32)! 159 | 160 | let nsGraphicContext = NSGraphicsContext(bitmapImageRep: imageRep)! 161 | 162 | let c = nsGraphicContext.cgContext 163 | 164 | NSGraphicsContext.current = nsGraphicContext 165 | 166 | c.setAllowsAntialiasing(false) 167 | 168 | // makes coordinates start upper left 169 | c.translateBy(x: 0, y: CGFloat(HEIGHT)) 170 | c.scaleBy(x: 1.0, y: -1.0) 171 | 172 | let strikeColor = NSColor.black 173 | 174 | c.saveGState() 175 | 176 | // align to the pixel grid 177 | c.translateBy(x: 0.5, y: 0.5) 178 | 179 | // copy program but magnified 180 | 181 | for y in 0..