Skip to content

Instantly share code, notes, and snippets.

@fpg1503
Last active February 21, 2018 19:25
Show Gist options
  • Select an option

  • Save fpg1503/953fa4c6661fd8196c61705cebc13c4b to your computer and use it in GitHub Desktop.

Select an option

Save fpg1503/953fa4c6661fd8196c61705cebc13c4b to your computer and use it in GitHub Desktop.
BoboLang - A programming language consisting of 4 commands that translates to Brainfuck

BoboLang

A programming language consisting of 4 commands that translates to Brainfuck

Commands:

  • bo: move up
  • bO: move right
  • Bo: move down
  • BO: move right

Usage

The idea of the language is: you start in the center of a 5x5 where the Brainfuck commands are layed out. Every time you step on one of them it's added to the output source. Walking past borders generates a compile time error. Characters other than b and o are ignored from the source. The Brainfuck commands are layed out in the following way:

    0 1 2 3 4
0   [   +   ]
1
2   <   S   >
3
4   ,   -   .
func fromBrainfuck(brainfuck: String) -> String {
let commands = brainfuck.map { Command(rawValue: String($0)) }.flatMap { $0 }
guard let firstCommand = commands.first else {
return ""
}
var path = initialPath(to: firstCommand)
var last = firstCommand
for command in commands.dropFirst() {
let shortest = shortestPath(from: last, to: command)
path.append(contentsOf: shortest)
last = command
}
let program = path.map { $0.rawValue }.reduce("", +)
return program
}
func initialPath(to: Command) -> [Direction] {
switch to {
case .rightCell: return [.right, .right]
case .leftCell: return [.left, .left]
case .increment: return [.up, .up]
case .decrement: return [.down, .down]
case .output: return [.right, .down, .right, .down]
case .input: return [.left, .down, .left, .down]
case .loopStart: return [.left, .up, .left, .up]
case .loopEnd: return [.right, .up, .right, .up]
}
}
func shortestPath(from: Command, to: Command) -> [Direction] {
switch from {
case .rightCell:
switch to {
case .rightCell: return [.left, .right]
case .leftCell: return [.left, .left, .left, .left]
case .increment: return [.left, .up, .left, .up]
case .decrement: return [.left, .down, .left, .down]
case .output: return [.down, .down]
case .input: return [.left, .down, .left, .left, .down, .left]
case .loopStart: return [.left, .up, .left, .left, .up, .left]
case .loopEnd: return [.up, .up]
}
case .leftCell:
switch to {
case .rightCell: return [.right, .right, .right, .right]
case .leftCell: return [.right, .left]
case .increment: return [.right, .up, .right, .up]
case .decrement: return [.right, .down, .right, .down]
case .output: return [.right, .down, .right, .right, .down, .right]
case .input: return [.down, .down]
case .loopStart: return [.up, .up]
case .loopEnd: return [.right, .up, .right, .right, .up, .right]
}
case .increment:
switch to {
case .rightCell: return [.right, .down, .right, .down]
case .leftCell: return [.left, .down, .left, .down]
case .increment: return [.down, .up]
case .decrement: return [.down, .down, .down, .down]
case .output: return [.down, .right, .down, .down, .right, .down]
case .input: return [.down, .left, .down, .down, .left, .down]
case .loopStart: return [.left, .left]
case .loopEnd: return [.right, .right]
}
case .decrement:
switch to {
case .rightCell: return [.up, .right, .up, .right]
case .leftCell: return [.up, .left, .up, .left]
case .increment: return [.up, .up, .up, .up]
case .decrement: return [.up, .down]
case .output: return [.right, .right]
case .input: return [.left, .left]
case .loopStart: return [.up, .left, .up, .up, .left, .up]
case .loopEnd: return [.up, .right, .up, .up, .right, .up]
}
case .output:
switch to {
case .rightCell: return [.up, .up]
case .leftCell: return [.up, .left, .left, .up, .left]
case .increment: return [.up, .left, .up, .up, .left, .up]
case .decrement: return [.left, .left]
case .output: return [.left, .right]
case .input: return [.up, .left, .left, .left, .left, .down]
case .loopStart: return [.up, .left, .up, .up, .left, .left, .up, .left]
case .loopEnd: return [.up, .left, .up, .up, .right, .up]
}
case .input:
switch to {
case .rightCell: return [.up, .right, .right, .up, .right]
case .leftCell: return [.up, .up]
case .increment: return [.up, .right, .up, .up, .right, .up]
case .decrement: return [.right, .right]
case .output: return [.up, .right, .right, .right, .right, .down]
case .input: return [.right, .left]
case .loopStart: return [.up, .right, .up, .up, .left, .up]
case .loopEnd: return [.up, .right, .up, .up, .right, .right, .up, .right]
}
case .loopStart:
switch to {
case .rightCell: return [.down, .right, .right, .right, .down, .right]
case .leftCell: return [.down, .down]
case .increment: return [.right, .right]
case .decrement: return [.down, .right, .down, .down, .right, .down]
case .output: return [.down, .right, .down, .down, .right, .right, .down, .right]
case .input: return [.right, .down, .down, .down, .down, .left]
case .loopStart: return [.right, .left]
case .loopEnd: return [.down, .right, .right, .right, .right, .up]
}
case .loopEnd:
switch to {
case .rightCell: return [.down, .down]
case .leftCell: return [.down, .left, .left, .left, .down, .left]
case .increment: return [.left, .left]
case .decrement: return [.down, .left, .down, .down, .left, .down]
case .output: return [.left, .down, .down, .down, .down, .right]
case .input: return [.down, .left, .down, .down, .left, .left, .down, .left]
case .loopStart: return [.down, .left, .left, .left, .left, .up]
case .loopEnd: return [.left, .right]
}
}
}
extension Collection {
func chunk(withDistance distance: IndexDistance) -> [[SubSequence.Iterator.Element]] {
var index = startIndex
let iterator: AnyIterator<Array<SubSequence.Iterator.Element>> = AnyIterator {
defer {
index = self.index(index, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex
}
let newIndex = self.index(index, offsetBy: distance, limitedBy: self.endIndex) ?? self.endIndex
let range = index ..< newIndex
return index != self.endIndex ? Array(self[range]) : nil
}
return Array(iterator)
}
}
enum Command: String {
case rightCell = ">"
case leftCell = "<"
case increment = "+"
case decrement = "-"
case output = "."
case input = ","
case loopStart = "["
case loopEnd = "]"
static func at(point: Point) -> Command? {
switch (point.x.value, point.y.value) {
case (0, 0): return .loopStart
case (0, 2): return .leftCell
case (0, 4): return .input
case (2, 0): return .increment
case (2, 4): return .decrement
case (4, 0): return .loopEnd
case (4, 2): return .rightCell
case (4, 4): return .output
default: return nil
}
}
}
enum ProgramError: Error {
case walkPastBorders
}
func compile(program: String) throws -> String {
let cleared = clear(program)
let tokens = try tokenize(cleared)
let moves = try translate(tokens)
var currentPoint = Point.starting
var program = ""
for move in moves {
guard let newPoint = currentPoint + move else {
throw ProgramError.walkPastBorders
}
currentPoint = newPoint
if let command = Command.at(point: currentPoint) {
program += command.rawValue
}
}
return program
}
// MAP
/*
0 1 2 3 4
0 [ + ]
1
2 < S >
3
4 , - .
*/
struct Coordinate {
let value: Int
init?(value: Int) {
guard value >= 0,
value <= 4 else {
return nil
}
self.value = value
}
}
struct Point {
let x: Coordinate
let y: Coordinate
static var starting: Point {
guard let two = Coordinate(value: 2) else {
fatalError("Something is really wrong!")
}
return Point(x: two, y: two)
}
}
enum Direction: String {
case up = "bo"
case right = "bO"
case down = "Bo"
case left = "BO"
var xOffset: Int {
switch self {
case .left: return -1
case .right: return 1
case .up, .down: return 0
}
}
var yOffset: Int {
switch self {
case .up: return -1
case .down: return 1
case .left, .right: return 0
}
}
}
func +(lhs: Coordinate, rhs: Int) -> Coordinate? {
let newValue = lhs.value + rhs
return Coordinate(value: newValue)
}
func +(lhs: Point, rhs: Direction) -> Point? {
guard let newX = lhs.x + rhs.xOffset,
let newY = lhs.y + rhs.yOffset else {
return nil
}
return Point(x: newX, y: newY)
}
let brainfuck = "-[------->+<]>-.-[->+++++<]>++.+++++++..+++.[--->+<]>-----.+[->++<]>.-[--->+<]>++++.-------------.+++++++++++++.[->+++<]>-.[------>+<]>-.+++++++++++++.-------."
let program = fromBrainfuck(brainfuck: brainfuck)
do {
let result = try compile(program: program)
print(brainfuck)
print(result)
brainfuck == result
} catch let error {
print(error)
}
enum TokenizeError: Error {
case invalidLength
}
func clear(_ string: String) -> String {
return string.map { String($0) }
.filter { ["b", "o"].contains($0.lowercased()) }
.reduce("", +)
}
func tokenize(_ string: String) throws -> [String] {
let characters = string.map { String($0) }
guard characters.count % 2 == 0 else {
throw TokenizeError.invalidLength
}
let tokens = characters.chunk(withDistance: 2).map { $0.joined() }
return tokens
}
enum TranslationError: Error {
case invalidSequence
}
func translate(_ tokens: [String]) throws -> [Direction] {
let directions = tokens.map(Direction.init)
guard directions.first(where: { $0 == nil }) == nil else {
throw TranslationError.invalidSequence
}
return directions.flatMap { $0 }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment