Skip to content

Instantly share code, notes, and snippets.

@yoh2
Last active December 6, 2019 14:37
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 yoh2/06c7537f45c5e2ed04aa6f51fecd4310 to your computer and use it in GitHub Desktop.
Save yoh2/06c7537f45c5e2ed04aa6f51fecd4310 to your computer and use it in GitHub Desktop.
Kotlin で Brainf*ck
import java.io.IOException
class Bf(private val program: BfProgram, memorySize: Int) {
private val memory = Array<Byte>(memorySize, {0})
private var pointer = 0
private var data
get() = memory[pointer]
set(value) { memory[pointer] = value }
fun execute(): BfResult<Unit> {
memory.fill(0)
pointer = 0
return executeInner(program)
}
private fun executeInner(program: List<BfInstruction>): BfResult<Unit> {
for (instruction in program) {
when (instruction) {
is BfInstruction.PAdd -> pointer += instruction.operand
is BfInstruction.DAdd -> data = (data.toInt() + instruction.operand).toByte()
is BfInstruction.Output -> output().unwrapOr { return BfResult.Failure(it) }
is BfInstruction.Input -> data = input().unwrapOr { return BfResult.Failure(it) }
is BfInstruction.While -> while (data != 0.toByte()) {
executeInner(instruction.sub).unwrapOr { return BfResult.Failure(it) }
}
}
}
return BfResult.Success(Unit)
}
private fun output(): BfResult<Unit> = try {
BfResult.Success(System.out.write(byteArrayOf(data)))
} catch (e: IOException) {
ioError(e)
}
private fun input(): BfResult<Byte> = try {
BfResult.Success(System.`in`.read().toByte())
} catch (e: IOException) {
ioError(e)
}
private fun <T> ioError(e: IOException): BfResult<T> = BfResult.Failure(BfError.IoError(e))
companion object {
fun execute(src: String, memorySize: Int = 30000): BfResult<Unit> = parse(src).flatMap { Bf(it, memorySize).execute() }
fun parse(src: String): BfResult<BfProgram> = parseInner(src, 0, false).map { it.first }
// return value: (program, next-index)
private fun parseInner(src: String, first: Int, nested: Boolean): BfResult<Pair<BfProgram, Int>> {
val program = mutableListOf<BfInstruction>()
var index = first
while (index < src.length) {
val (instruction, next) = when (src[index]) {
'>' -> parsePAdd(src, index + 1, 1)
'<' -> parsePAdd(src, index + 1, -1)
'+' -> parseDAdd(src, index + 1, 1)
'-' -> parseDAdd(src, index + 1, -1)
'.' -> Pair(BfInstruction.Output, index + 1)
',' -> Pair(BfInstruction.Input, index + 1)
'[' -> parseInner(src, index + 1, true).map { Pair(BfInstruction.While(it.first), it.second) }.unwrapOr { return BfResult.Failure(it) }
']' -> return if (nested) {
BfResult.Success(Pair(program, index + 1))
} else {
BfResult.Failure(BfError.UnmatchedParenthesis(index))
}
else -> Pair(null, index + 1)
}
instruction?.let { program.add(it) }
index = next
}
return if (nested) {
BfResult.Failure(BfError.UnmatchedParenthesis(index))
} else {
BfResult.Success(Pair(program, index + 1))
}
}
private fun parsePAdd(src: String, first: Int, init: Int): Pair<BfInstruction?, Int> = parseXAdd(src, first, init, '>', '<') { BfInstruction.PAdd(it) }
private fun parseDAdd(src: String, first: Int, init: Int): Pair<BfInstruction?, Int> = parseXAdd(src, first, init, '+', '-') { BfInstruction.DAdd(it) }
private fun parseXAdd(src: String, first: Int, init: Int, inc: Char, dec: Char, gen: (Int) -> BfInstruction): Pair<BfInstruction?, Int> {
var index = first
var operand = init
loop@while (index < src.length) {
when (src[index]) {
inc -> operand++
dec -> operand--
in INSTRUCTION_CHARS -> break@loop
}
index++
}
return Pair(if (operand == 0) null else gen(operand), index)
}
val INSTRUCTION_CHARS = setOf('>', '<', '+', '-', '.', ',', '[', ']')
}
}
sealed class BfInstruction {
data class PAdd(val operand: Int) : BfInstruction()
data class DAdd(val operand: Int) : BfInstruction()
object Output : BfInstruction()
object Input : BfInstruction()
data class While(val sub: BfProgram) : BfInstruction()
}
typealias BfProgram = List<BfInstruction>
sealed class BfResult<T> {
data class Success<T>(val value: T) : BfResult<T>() {
override fun <U> map(f: (T) -> U): BfResult<U> = Success(f(value))
override fun <U> flatMap(f: (T) -> BfResult<U>): BfResult<U> = f(value)
}
data class Failure<T>(val error: BfError) : BfResult<T>() {
override fun <U> map(f: (T) -> U): BfResult<U> = Failure(error)
override fun <U> flatMap(f: (T) -> BfResult<U>): BfResult<U> = Failure(error)
}
abstract fun <U> map(f: (T) -> U): BfResult<U>;
abstract fun <U> flatMap(f: (T) -> BfResult<U>): BfResult<U>;
inline fun unwrapOr(f: (BfError) -> T): T = when (this) {
is Success -> value
is Failure -> f(error)
}
}
sealed class BfError {
data class UnmatchedParenthesis(val position: Int) : BfError()
data class IoError(val exception: IOException) : BfError()
}
fun main() {
// Hello, world
val src = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
Bf.execute(src).unwrapOr { println("error: $it") }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment