├── 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..