Compare commits
12 Commits
master
...
interprete
| Author | SHA1 | Date | |
|---|---|---|---|
| 660a54eda5 | |||
| 14eb1a87f7 | |||
| 81750a920d | |||
| 2e03a031b0 | |||
| 8d6da62be1 | |||
| 626094320d | |||
| dad81ffd40 | |||
| ffab11f785 | |||
| dd6076441c | |||
| 53c7b87354 | |||
| fcb017e6df | |||
| bda23d7e9d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -24,7 +24,6 @@ buildNumber.properties
|
|||||||
.mtj.tmp/
|
.mtj.tmp/
|
||||||
|
|
||||||
# Package Files #
|
# Package Files #
|
||||||
*.jar
|
|
||||||
*.war
|
*.war
|
||||||
*.nar
|
*.nar
|
||||||
*.ear
|
*.ear
|
||||||
@@ -94,3 +93,4 @@ fabric.properties
|
|||||||
# Mobile Tools for Java (J2ME)
|
# Mobile Tools for Java (J2ME)
|
||||||
# Package Files #
|
# Package Files #
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
.idea/statistic.xml
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package befide.befunge.b93
|
|
||||||
|
|
||||||
import befide.befunge.core.*
|
|
||||||
import befide.befunge.state.*
|
|
||||||
|
|
||||||
fun <T> List<T>.padEnd(size: Int, factory: (Int) -> (T)): List<T> = this + (this.size until size).map { factory(it) }
|
|
||||||
fun <T> List<T>.padEnd(size: Int, value: T): List<T> = this.padEnd(size) { value }
|
|
||||||
|
|
||||||
class B93Funge : Funge {
|
|
||||||
override val width = 80
|
|
||||||
override val height = 25
|
|
||||||
|
|
||||||
val bounds = Vec(width, height)
|
|
||||||
|
|
||||||
private var cars = Array(height) { Array(width) { Value(' ') } }
|
|
||||||
|
|
||||||
override var values
|
|
||||||
get() = cars.map { it.toList() }
|
|
||||||
set(data) {
|
|
||||||
for (y in 0 until cars.size)
|
|
||||||
for (x in 0 until cars[y].size)
|
|
||||||
cars[y][x] = data[y][x]
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun get(vec: Vec): Value {
|
|
||||||
return cars[vec.y][vec.x]
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun set(vec: Vec, value: Value) {
|
|
||||||
cars[vec.y][vec.x] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun nextVec(vec: Vec, delta: Vec): Vec {
|
|
||||||
return (vec + delta) mod bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setString(data: String) {
|
|
||||||
cars = data.split("\n").map {
|
|
||||||
it.map(::Value).padEnd(width, Value(' ')).toTypedArray()
|
|
||||||
}.padEnd(height) {
|
|
||||||
Array(width) { Value(' ') }
|
|
||||||
}.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return cars.joinToString("\n") { row ->
|
|
||||||
row.joinToString("") { value ->
|
|
||||||
(value.asChar ?: '?').toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,359 +0,0 @@
|
|||||||
package befide.befunge.b93
|
|
||||||
|
|
||||||
import befide.befunge.core.Interpreter
|
|
||||||
import befide.befunge.events.*
|
|
||||||
import befide.befunge.state.IpMode
|
|
||||||
import befide.befunge.state.Value
|
|
||||||
import befide.befunge.state.Vec
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.BlockingQueue
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
|
||||||
|
|
||||||
class B93Interpreter : Interpreter {
|
|
||||||
private companion object {
|
|
||||||
val RIGHT = Vec(1, 0)
|
|
||||||
val LEFT = Vec(-1, 0)
|
|
||||||
val UP = Vec(0, -1)
|
|
||||||
val DOWN = Vec(0,1)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val funge = B93Funge()
|
|
||||||
override val stack = Stack<Value>()
|
|
||||||
override val ip = B93Pointer(Vec(0, 0), Vec(1, 0), IpMode.Normal)
|
|
||||||
|
|
||||||
override val fungeChanged: Event<FungeEvent> = Event()
|
|
||||||
override val stackChanged: Event<StackEvent> = Event()
|
|
||||||
override val ipChanged: Event<IpEvent> = Event()
|
|
||||||
override val outputChanged: Event<OutputEvent> = Event()
|
|
||||||
|
|
||||||
override val stdInput = LinkedList<Char>()
|
|
||||||
override val stdOutput = LinkedList<String>()
|
|
||||||
private var outBuf: StringBuffer = StringBuffer()
|
|
||||||
private var bufLimit = 0
|
|
||||||
|
|
||||||
private val fungeMods = HashMap<Vec,Value>()
|
|
||||||
|
|
||||||
private fun _pop(): Value? {
|
|
||||||
if (!stack.empty())
|
|
||||||
return stack.pop()
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun pop(): Value {
|
|
||||||
val v = _pop()
|
|
||||||
val ret = v?.let {
|
|
||||||
stackChanged(StackEvent(StackAction.Pop, listOf(it)))
|
|
||||||
it
|
|
||||||
} ?: Value(0)
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun pop(num: Int): List<Value> {
|
|
||||||
val vs = List(num) {_pop()}
|
|
||||||
stackChanged(StackEvent(StackAction.Pop, vs.filterNotNull()))
|
|
||||||
return vs.map { it ?: Value(0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun _push(v: Value) {
|
|
||||||
stack.push(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun push(v: Value) {
|
|
||||||
_push(v)
|
|
||||||
stackChanged.invoke(StackEvent(StackAction.Push, listOf(v)))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun push(v: Value, vararg vs: Value) {
|
|
||||||
_push(v)
|
|
||||||
vs.forEach { _push(it) }
|
|
||||||
stackChanged.invoke(StackEvent(StackAction.Push, listOf(v) + vs.toList()))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun peek(): Value {
|
|
||||||
if (!stack.empty()) {
|
|
||||||
return stack.peek()
|
|
||||||
}
|
|
||||||
return Value(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun binop(bop: Char): Boolean {
|
|
||||||
val (vb, va) = pop(2)
|
|
||||||
val a = va.value
|
|
||||||
val b = vb.value
|
|
||||||
val res = when(bop) {
|
|
||||||
'+' -> a + b
|
|
||||||
'-' -> a - b
|
|
||||||
'*' -> a * b
|
|
||||||
'/' -> a / b
|
|
||||||
'%' -> a % b
|
|
||||||
'`' -> if (a > b) 1L else 0L
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
return res?.let {
|
|
||||||
val vres = Value(res)
|
|
||||||
push(vres)
|
|
||||||
} != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unop(uop: Char): Boolean {
|
|
||||||
val vv = pop()
|
|
||||||
val v = vv.value
|
|
||||||
val res = when(uop) {
|
|
||||||
'!' -> if (v == 0L) 1L else 0L
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
return res?.let {
|
|
||||||
val vres = Value(it)
|
|
||||||
push(vres)
|
|
||||||
} != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun changeDir(dir: Char): Boolean {
|
|
||||||
val newDelta = when(dir) {
|
|
||||||
'>' -> RIGHT
|
|
||||||
'<' -> LEFT
|
|
||||||
'^' -> UP
|
|
||||||
'v' -> DOWN
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
return newDelta?.let {
|
|
||||||
ip.delta = it
|
|
||||||
} != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun randomDir(): Boolean {
|
|
||||||
val dirs = listOf('<', '>', '^', 'v')
|
|
||||||
val ind = Random().nextInt(4)
|
|
||||||
changeDir(dirs[ind])
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun conditional(cop: Char): Boolean {
|
|
||||||
val vcond = pop()
|
|
||||||
val cond = vcond.value == 0L
|
|
||||||
val newDelta = when(cop) {
|
|
||||||
'|' -> if (cond) DOWN else UP
|
|
||||||
'_' -> if (cond) RIGHT else LEFT
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
return newDelta?.let {
|
|
||||||
ip.delta = it
|
|
||||||
} != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun toggleStrmode(): Boolean {
|
|
||||||
val newMode = when (ip.mode) {
|
|
||||||
IpMode.String -> IpMode.Normal
|
|
||||||
IpMode.Normal -> IpMode.String
|
|
||||||
IpMode.Inactive -> IpMode.Inactive
|
|
||||||
}
|
|
||||||
ip.mode = newMode
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun stackop(sop: Char): Boolean {
|
|
||||||
var ret = true
|
|
||||||
when (sop) {
|
|
||||||
':' -> {
|
|
||||||
val vc = peek().copy()
|
|
||||||
push(vc)
|
|
||||||
}
|
|
||||||
'\\' -> {
|
|
||||||
val (v2, v1) = pop(2)
|
|
||||||
push(v2, v1)
|
|
||||||
}
|
|
||||||
'$' -> {
|
|
||||||
pop()
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
ret = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun output(type: Char): Boolean {
|
|
||||||
val vv = pop()
|
|
||||||
val v = vv.value
|
|
||||||
val out = when (type) {
|
|
||||||
'.' -> v.toString() + ' '
|
|
||||||
',' -> v.toChar().toString()
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
return out?.let{
|
|
||||||
outBuf.append(it)
|
|
||||||
if (outBuf.length > bufLimit) {
|
|
||||||
stdOutput.add(outBuf.toString())
|
|
||||||
outBuf.delete(0, outBuf.length)
|
|
||||||
outputChanged.invoke(OutputEvent())
|
|
||||||
}
|
|
||||||
} != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun stepIP(): Boolean {
|
|
||||||
ip.pos = funge.nextVec(ip.pos, ip.delta)
|
|
||||||
return true
|
|
||||||
// No ipChanged here, shown in execInstr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun input(type: Char): Boolean {
|
|
||||||
fun <T> Queue<T>.takeWhile(predicate: (T) -> Boolean): Sequence<T?> {
|
|
||||||
return sequence {
|
|
||||||
if (peek() == null) {
|
|
||||||
yield(null)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
while (peek()?.let(predicate) == true) {
|
|
||||||
yield(remove())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yield(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val zer = '0'.toLong()
|
|
||||||
val inp = when (type) {
|
|
||||||
'&' -> {
|
|
||||||
val nums = stdInput.takeWhile { it in '0' until '9' }
|
|
||||||
val first = nums.first()
|
|
||||||
first?.let {
|
|
||||||
nums.filterNotNull()
|
|
||||||
.map {c -> c.toLong() - zer}
|
|
||||||
.fold(it.toLong()-zer) { curr: Long, next: Long ->
|
|
||||||
curr * 10 + next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'~' -> stdInput.poll()?.toLong()
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
return inp?.let {
|
|
||||||
push(Value(it))
|
|
||||||
} != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fget(): Boolean {
|
|
||||||
val (vy, vx) = pop(2)
|
|
||||||
val x = vx.value.toInt()
|
|
||||||
val y = vy.value.toInt()
|
|
||||||
if (0 <= x && x < funge.width && 0 <= y && y <= funge.height) {
|
|
||||||
val vv = funge[Vec(x, y)]
|
|
||||||
push(vv)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fput(): Boolean {
|
|
||||||
val (vy, vx, vv) = pop(3)
|
|
||||||
val x = vx.value.toInt()
|
|
||||||
val y = vy.value.toInt()
|
|
||||||
if (0 <= x && x < funge.width && 0 <= y && y <= funge.height) {
|
|
||||||
val loc = Vec(x, y)
|
|
||||||
val old = funge[loc]
|
|
||||||
funge[loc] = vv
|
|
||||||
if (!fungeMods.contains(loc)) {
|
|
||||||
fungeMods[loc] = old
|
|
||||||
}
|
|
||||||
fungeChanged(FungeEvent(listOf(FungeChange(loc,old,vv))))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun terminate(): Boolean {
|
|
||||||
stdOutput.add(outBuf.toString())
|
|
||||||
outBuf.delete(0, outBuf.length)
|
|
||||||
outputChanged.invoke(OutputEvent())
|
|
||||||
ip.mode = IpMode.Inactive
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun pushDig(dig: Char): Boolean {
|
|
||||||
val num = dig.toLong() - '0'.toLong()
|
|
||||||
push(Value(num))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun noOp(): Boolean = false
|
|
||||||
|
|
||||||
private fun execInstr(instr: Value): Boolean {
|
|
||||||
val car = instr.asChar
|
|
||||||
return when (car) {
|
|
||||||
null -> noOp()
|
|
||||||
'+', '-', '*', '/', '%', '`' -> binop(car)
|
|
||||||
'!' -> unop(car)
|
|
||||||
'>', '<', '^', 'v' -> changeDir(car)
|
|
||||||
'?' -> randomDir()
|
|
||||||
'_', '|' -> conditional(car)
|
|
||||||
'"' -> toggleStrmode()
|
|
||||||
':', '\\', '$' -> stackop(car)
|
|
||||||
'.', ',' -> output(car)
|
|
||||||
'#' -> stepIP()
|
|
||||||
'g' -> fget()
|
|
||||||
'p' -> fput()
|
|
||||||
'&', '~' -> input(car)
|
|
||||||
'@' -> terminate()
|
|
||||||
in '0'..'9' -> pushDig(car)
|
|
||||||
' ' -> true
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun strMode(instr: Value): Boolean {
|
|
||||||
if (instr.asChar == '"') {
|
|
||||||
toggleStrmode()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
push(instr)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun step(): Boolean {
|
|
||||||
val instr = funge[ip.pos]
|
|
||||||
val currIP = ip.copy()
|
|
||||||
val processed = when (ip.mode) {
|
|
||||||
IpMode.Inactive -> noOp()
|
|
||||||
IpMode.Normal -> execInstr(instr)
|
|
||||||
IpMode.String -> strMode(instr)
|
|
||||||
}
|
|
||||||
if (processed) {
|
|
||||||
stepIP()
|
|
||||||
if (ip.mode != IpMode.String) {
|
|
||||||
var numsteps = 0
|
|
||||||
val maxsteps = if (funge.height > funge.width) funge.height else funge.width
|
|
||||||
while (funge[ip.pos].asChar == ' ' && numsteps++ < maxsteps) {
|
|
||||||
stepIP()
|
|
||||||
}
|
|
||||||
if (numsteps == maxsteps) {
|
|
||||||
ip.mode = IpMode.Inactive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val newIP = ip.copy()
|
|
||||||
ipChanged(IpEvent(currIP, newIP))
|
|
||||||
}
|
|
||||||
return ip.mode != IpMode.Inactive
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun reset() {
|
|
||||||
val oldIP = ip.copy()
|
|
||||||
ip.pos = Vec(0,0)
|
|
||||||
ip.delta = RIGHT
|
|
||||||
ip.mode = IpMode.Normal
|
|
||||||
val newIP = ip.copy()
|
|
||||||
ipChanged.invoke(IpEvent(oldIP, newIP))
|
|
||||||
|
|
||||||
stack.clear()
|
|
||||||
stackChanged(StackEvent(StackAction.Clear, listOf()))
|
|
||||||
|
|
||||||
stdInput.clear()
|
|
||||||
stdOutput.clear()
|
|
||||||
|
|
||||||
val changes = fungeMods.map { FungeChange(it.key, funge[it.key], it.value) }
|
|
||||||
for ((k,v) in fungeMods) {
|
|
||||||
funge[k] = v
|
|
||||||
}
|
|
||||||
fungeMods.clear()
|
|
||||||
fungeChanged(FungeEvent(changes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package befide.befunge.b93
|
|
||||||
|
|
||||||
import befide.befunge.core.Pointer
|
|
||||||
import befide.befunge.state.IpMode
|
|
||||||
import befide.befunge.state.Vec
|
|
||||||
|
|
||||||
data class B93Pointer(override var pos: Vec, override var delta: Vec, override var mode: IpMode) : Pointer
|
|
||||||
97
befunge/src/main/kotlin/befide/befunge/b93/Instructions93.kt
Normal file
97
befunge/src/main/kotlin/befide/befunge/b93/Instructions93.kt
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package befide.befunge.b93
|
||||||
|
|
||||||
|
import befide.befunge.b93.state.Data93
|
||||||
|
import befide.befunge.b93.state.PointerMode
|
||||||
|
import befide.befunge.b93.state.Vec2
|
||||||
|
import befide.befunge.core.InstructionSet
|
||||||
|
import befide.befunge.core.MutableInterpreter
|
||||||
|
import befide.befunge.core.util.chooseOne
|
||||||
|
|
||||||
|
class B93Instructions : InstructionSet<Vec2, Data93, PointerMode> {
|
||||||
|
override fun MutableInterpreter<Vec2, Data93, PointerMode>.handle(): Boolean {
|
||||||
|
when (mode) {
|
||||||
|
PointerMode.Terminated -> return true
|
||||||
|
PointerMode.String -> when (instr.char) {
|
||||||
|
null -> return false
|
||||||
|
'"' -> mode = PointerMode.Normal
|
||||||
|
else -> push(instr)
|
||||||
|
}
|
||||||
|
PointerMode.Normal -> when (instr.char) {
|
||||||
|
null -> return false
|
||||||
|
|
||||||
|
in "0123456789" -> push(Data93(instr.char.toString().toLong(16)))
|
||||||
|
|
||||||
|
'+' -> pop(2).let { (b, a) -> push(a + b) }
|
||||||
|
'-' -> pop(2).let { (b, a) -> push(a - b) }
|
||||||
|
'*' -> pop(2).let { (b, a) -> push(a * b) }
|
||||||
|
'/' -> pop(2).let { (b, a) -> push(a / b) }
|
||||||
|
'%' -> pop(2).let { (b, a) -> push(a mod b) }
|
||||||
|
'!' -> pop(1).let { (n) -> push(Data93(if (n.data == 0L) 1L else 0L)) }
|
||||||
|
'`' -> pop(2).let { (b, a) -> push(Data93(if (a.data > b.data) 1L else 0L)) }
|
||||||
|
|
||||||
|
'>' -> delta = Vec2.RIGHT
|
||||||
|
'<' -> delta = Vec2.LEFT
|
||||||
|
'v' -> delta = Vec2.DOWN
|
||||||
|
'^' -> delta = Vec2.UP
|
||||||
|
'?' -> delta = Vec2.DIRS.chooseOne()
|
||||||
|
'#' -> move()
|
||||||
|
|
||||||
|
'_' -> pop(1).let { (n) -> delta = if (n.data == 0L) Vec2.RIGHT else Vec2.LEFT }
|
||||||
|
'|' -> pop(1).let { (n) -> delta = if (n.data == 0L) Vec2.DOWN else Vec2.UP }
|
||||||
|
|
||||||
|
'"' -> mode = PointerMode.String
|
||||||
|
|
||||||
|
':' -> pop(1).let { (n) -> push(n, n) }
|
||||||
|
'\\' -> pop(2).let { (b, a) -> push(b, a) }
|
||||||
|
'$' -> pop(1)
|
||||||
|
|
||||||
|
'.' -> pop(1).let { (n) -> stdout?.write("${n.data} ") }
|
||||||
|
',' -> pop(1).let { (ch) -> stdout?.write(ch.data.toChar().toString()) }
|
||||||
|
|
||||||
|
'p' -> pop(3).let { (y, x, n) -> funge[Vec2(x.data.toInt(), y.data.toInt())] = n }
|
||||||
|
'g' -> pop(2).let { (y, x) -> push(funge[Vec2(x.data.toInt(), y.data.toInt())]) }
|
||||||
|
|
||||||
|
'~' -> push(Data93(stdin?.read()?.toLong() ?: 0L))
|
||||||
|
'&' -> {
|
||||||
|
val chars = generateSequence {
|
||||||
|
stdin?.read()?.toChar()?.takeIf { it.isDigit() }
|
||||||
|
}
|
||||||
|
val long = chars.joinToString("") {
|
||||||
|
it.toString()
|
||||||
|
}.ifEmpty { "0" }.toLong()
|
||||||
|
push(Data93(long))
|
||||||
|
}
|
||||||
|
|
||||||
|
'@' -> mode = PointerMode.Terminated
|
||||||
|
|
||||||
|
' ' -> do move() while (next.char == ' ')
|
||||||
|
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class B93Extras : InstructionSet<Vec2, Data93, PointerMode> {
|
||||||
|
override fun MutableInterpreter<Vec2, Data93, PointerMode>.handle(): Boolean {
|
||||||
|
when (mode) {
|
||||||
|
PointerMode.Terminated -> return true
|
||||||
|
PointerMode.String -> return false
|
||||||
|
PointerMode.Normal -> when (instr.char) {
|
||||||
|
null -> return false
|
||||||
|
|
||||||
|
in "0123456789abcdef" -> push(Data93(instr.char.toString().toLong(16)))
|
||||||
|
|
||||||
|
'\'' -> {
|
||||||
|
move()
|
||||||
|
push(instr)
|
||||||
|
}
|
||||||
|
|
||||||
|
';' -> do move() while (instr.char != ';')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
31
befunge/src/main/kotlin/befide/befunge/b93/Interpreter93.kt
Normal file
31
befunge/src/main/kotlin/befide/befunge/b93/Interpreter93.kt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package befide.befunge.b93
|
||||||
|
|
||||||
|
import befide.befunge.b93.state.*
|
||||||
|
import befide.befunge.core.*
|
||||||
|
import befide.befunge.core.util.readAll
|
||||||
|
import com.sun.imageio.spi.OutputStreamImageOutputStreamSpi
|
||||||
|
import java.io.*
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.concurrent.timer
|
||||||
|
|
||||||
|
|
||||||
|
class Interpreter93(override var stdin: Reader? = null, override var stdout: Writer? = null)
|
||||||
|
: MutableInterpreter<Vec2, Data93, PointerMode>() {
|
||||||
|
|
||||||
|
override val ip = Pointer93()
|
||||||
|
override val funge = Funge93()
|
||||||
|
override val stack = Stack<Data93>()
|
||||||
|
|
||||||
|
override fun stackDefault() = Data93.ZERO
|
||||||
|
|
||||||
|
override val instructionSet = B93Instructions() + B93Extras()
|
||||||
|
|
||||||
|
override fun run(afterEach: () -> Unit) {
|
||||||
|
while (mode != PointerMode.Terminated) {
|
||||||
|
step()
|
||||||
|
afterEach()
|
||||||
|
stdout?.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
befunge/src/main/kotlin/befide/befunge/b93/Main.kt
Normal file
67
befunge/src/main/kotlin/befide/befunge/b93/Main.kt
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package befide.befunge.b93
|
||||||
|
|
||||||
|
import befide.befunge.core.util.readAll
|
||||||
|
import java.io.PipedReader
|
||||||
|
import java.io.PipedWriter
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.concurrent.timer
|
||||||
|
|
||||||
|
|
||||||
|
fun runSync() {
|
||||||
|
val int = Interpreter93(System.`in`.reader(), System.out.writer())
|
||||||
|
|
||||||
|
// interactive calculator
|
||||||
|
int.funge.src = "\" :b ]%/*-+[ a\">:#,_&~~\$a3*0p& .@"
|
||||||
|
|
||||||
|
int.run {}
|
||||||
|
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runThreaded() {
|
||||||
|
val stdout = PipedReader()
|
||||||
|
val stdin = PipedWriter()
|
||||||
|
|
||||||
|
val int = Interpreter93(PipedReader(stdin), PipedWriter(stdout))
|
||||||
|
|
||||||
|
// incrementer
|
||||||
|
int.funge.src = ":.&+"
|
||||||
|
|
||||||
|
val input = timer("input", period = 50L) {
|
||||||
|
generateSequence {
|
||||||
|
System.`in`.read().takeUnless { it == -1 }
|
||||||
|
}.forEach(stdin::write)
|
||||||
|
}
|
||||||
|
|
||||||
|
val output = timer("output", period = 500L) {
|
||||||
|
print(stdout.readAll())
|
||||||
|
}
|
||||||
|
|
||||||
|
val stepper = thread {
|
||||||
|
int.run { Thread.sleep(50L) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// input, output, and computation happen concurrently at this point.
|
||||||
|
|
||||||
|
stepper.join()
|
||||||
|
|
||||||
|
input.cancel()
|
||||||
|
output.cancel()
|
||||||
|
|
||||||
|
println(stdout.readAll())
|
||||||
|
|
||||||
|
int.stdout?.close()
|
||||||
|
stdout.close()
|
||||||
|
int.stdin?.close()
|
||||||
|
stdin.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
println("synchronous:")
|
||||||
|
runSync()
|
||||||
|
|
||||||
|
println()
|
||||||
|
|
||||||
|
println("threaded:")
|
||||||
|
runThreaded()
|
||||||
|
}
|
||||||
26
befunge/src/main/kotlin/befide/befunge/b93/state/Data93.kt
Normal file
26
befunge/src/main/kotlin/befide/befunge/b93/state/Data93.kt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package befide.befunge.b93.state
|
||||||
|
|
||||||
|
import befide.befunge.core.state.Data
|
||||||
|
import befide.befunge.core.util.mod
|
||||||
|
|
||||||
|
data class Data93(val data: Long)
|
||||||
|
: Data {
|
||||||
|
companion object {
|
||||||
|
val SPACE = Data93(' ')
|
||||||
|
val ZERO = Data93(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val char = data.toChar().takeUnless { it.isISOControl() }
|
||||||
|
|
||||||
|
constructor(char: Char) : this(char.toLong())
|
||||||
|
|
||||||
|
operator fun plus(o: Data93) = Data93(data + o.data)
|
||||||
|
operator fun minus(o: Data93) = Data93(data - o.data)
|
||||||
|
operator fun times(o: Data93) = Data93(data * o.data)
|
||||||
|
operator fun div(o: Data93) = Data93(data / o.data)
|
||||||
|
operator fun unaryMinus() = Data93(-data)
|
||||||
|
|
||||||
|
infix fun mod(o: Data93) = Data93(data mod o.data)
|
||||||
|
|
||||||
|
override fun toString() = "${char ?: '\u2022'} ($data)"
|
||||||
|
}
|
||||||
61
befunge/src/main/kotlin/befide/befunge/b93/state/Funge93.kt
Normal file
61
befunge/src/main/kotlin/befide/befunge/b93/state/Funge93.kt
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package befide.befunge.b93.state
|
||||||
|
|
||||||
|
import befide.befunge.core.state.MutableFunge
|
||||||
|
|
||||||
|
class Funge93
|
||||||
|
: MutableFunge<Vec2, Data93>() {
|
||||||
|
companion object {
|
||||||
|
val DEFAULT_DATA = Data93.SPACE
|
||||||
|
const val DEFAULT_CHAR = '\u2022' // bullet
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size = Vec2(80, 25)
|
||||||
|
|
||||||
|
override fun next(pos: Vec2, delta: Vec2): Vec2 = (pos + delta) mod size
|
||||||
|
|
||||||
|
val contents = hashMapOf<Vec2, Data93>()
|
||||||
|
|
||||||
|
override fun get(pos: Vec2): Data93 = contents[pos] ?: DEFAULT_DATA
|
||||||
|
override fun set(pos: Vec2, data: Data93) {
|
||||||
|
if (pos in Vec2.ZERO until size) notifyChanged(pos) {
|
||||||
|
contents[pos] = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var data: Map<Vec2, Data93>
|
||||||
|
get() {
|
||||||
|
return contents.toMap()
|
||||||
|
}
|
||||||
|
set(data) {
|
||||||
|
clear()
|
||||||
|
for ((v, d) in data.entries)
|
||||||
|
this[v] = d
|
||||||
|
}
|
||||||
|
|
||||||
|
override var src: String
|
||||||
|
get() {
|
||||||
|
val lines = mutableListOf<String>()
|
||||||
|
for ((y, row) in data.entries.groupBy { it.key.y }.toSortedMap()) {
|
||||||
|
lines += List(y - lines.size) { "" }
|
||||||
|
|
||||||
|
val chars = mutableListOf<Char>()
|
||||||
|
for ((v, ch) in row.sortedBy { it.key.x }) {
|
||||||
|
chars += List(v.x - chars.size) { ' ' }
|
||||||
|
chars += ch.char ?: DEFAULT_CHAR
|
||||||
|
}
|
||||||
|
lines += chars.joinToString("")
|
||||||
|
}
|
||||||
|
return lines.joinToString("\n")
|
||||||
|
}
|
||||||
|
set(src) {
|
||||||
|
data = src.lines().mapIndexed { y, line ->
|
||||||
|
line.mapIndexed { x, ch ->
|
||||||
|
Pair(Vec2(x, y), Data93(ch))
|
||||||
|
}
|
||||||
|
}.flatten().toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
contents.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package befide.befunge.b93.state
|
||||||
|
|
||||||
|
import befide.befunge.core.state.MutablePointer
|
||||||
|
import befide.befunge.core.state.Pointer
|
||||||
|
|
||||||
|
data class Pointer93(override var pos: Vec2 = Vec2.ZERO,
|
||||||
|
override var delta: Vec2 = Vec2.RIGHT,
|
||||||
|
override var mode: PointerMode = PointerMode.Normal)
|
||||||
|
: MutablePointer<Vec2, PointerMode> {
|
||||||
|
|
||||||
|
override fun copy(): Pointer<Vec2, PointerMode> = Pointer93(pos, delta, mode)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package befide.befunge.b93.state
|
||||||
|
|
||||||
|
enum class PointerMode {
|
||||||
|
Normal, String, Terminated
|
||||||
|
}
|
||||||
39
befunge/src/main/kotlin/befide/befunge/b93/state/Vec93.kt
Normal file
39
befunge/src/main/kotlin/befide/befunge/b93/state/Vec93.kt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package befide.befunge.b93.state
|
||||||
|
|
||||||
|
import befide.befunge.core.util.mod
|
||||||
|
|
||||||
|
data class Vec2(val x: Int, val y: Int) {
|
||||||
|
companion object {
|
||||||
|
val ZERO = Vec2(0, 0)
|
||||||
|
val ONE = Vec2(0, 0)
|
||||||
|
|
||||||
|
val RIGHT = Vec2(1, 0)
|
||||||
|
val DOWN = Vec2(0, 1)
|
||||||
|
val LEFT = Vec2(-1, 0)
|
||||||
|
val UP = Vec2(0, -1)
|
||||||
|
|
||||||
|
val DIRS = listOf(RIGHT, LEFT, DOWN, UP)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "($x, $y)"
|
||||||
|
|
||||||
|
val ccw by lazy { Vec2(-y, x) }
|
||||||
|
val cw by lazy { Vec2(y, -x) }
|
||||||
|
|
||||||
|
operator fun plus(o: Vec2) = Vec2(x + o.x, y + o.y)
|
||||||
|
operator fun minus(o: Vec2) = Vec2(x - o.x, y - o.y)
|
||||||
|
operator fun times(o: Int) = Vec2(x * o, y * o)
|
||||||
|
operator fun div(o: Int) = Vec2(x / o, y / o)
|
||||||
|
operator fun unaryMinus() = Vec2(-x, -y)
|
||||||
|
|
||||||
|
infix fun mod(o: Vec2) = Vec2(x mod o.x, y mod o.y)
|
||||||
|
|
||||||
|
operator fun rangeTo(o: Vec2) = Bounds2(x..o.x, y..o.y)
|
||||||
|
infix fun until(o: Vec2) = Bounds2(x until o.x, y until o.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Bounds2(val x: IntRange, val y: IntRange) {
|
||||||
|
override fun toString(): String = "($x, $y)"
|
||||||
|
|
||||||
|
operator fun contains(o: Vec2): Boolean = o.x in x && o.y in y
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package befide.befunge.core
|
|
||||||
|
|
||||||
import befide.befunge.events.Event
|
|
||||||
import befide.befunge.events.FungeEvent
|
|
||||||
import befide.befunge.state.Value
|
|
||||||
import befide.befunge.state.Vec
|
|
||||||
|
|
||||||
interface Funge {
|
|
||||||
val width: Int
|
|
||||||
val height: Int
|
|
||||||
|
|
||||||
operator fun get(vec: Vec): Value
|
|
||||||
operator fun set(vec: Vec, value: Value)
|
|
||||||
|
|
||||||
var values: List<List<Value>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the next position to be executed starting at [vec] and stepping by [delta]
|
|
||||||
*/
|
|
||||||
fun nextVec(vec: Vec, delta: Vec): Vec
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the contents of the funge based on a string, handling newlines appropriately
|
|
||||||
*/
|
|
||||||
fun setString(data: String)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return An iterable of executable positions, starting at [vec] and stepping by [delta]
|
|
||||||
*/
|
|
||||||
fun nextVecs(vec: Vec, delta: Vec): Iterable<Vec> {
|
|
||||||
return generateSequence(vec) {
|
|
||||||
nextVec(it, delta)
|
|
||||||
}.asIterable()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starting at [vec] and stepping by [delta], fill those positions with values in [data]
|
|
||||||
*/
|
|
||||||
fun setMany(vec: Vec, delta: Vec, data: List<Value>) {
|
|
||||||
for ((e, v) in data.zip(nextVecs(vec, delta)))
|
|
||||||
set(v, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return An iterable of those values at the executable positions starting at [vec] and stepping by [delta].
|
|
||||||
*/
|
|
||||||
fun getMany(vec: Vec, delta: Vec, count: Int): Iterable<Value> {
|
|
||||||
return nextVecs(vec, delta).take(count).map(this::get).asIterable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package befide.befunge.core
|
||||||
|
|
||||||
|
import befide.befunge.core.state.Data
|
||||||
|
|
||||||
|
interface InstructionSet<V, D : Data, M : Enum<M>> {
|
||||||
|
/**
|
||||||
|
* @return whether this instruction set handled the instruction. If true, the step will be completed
|
||||||
|
*/
|
||||||
|
fun MutableInterpreter<V, D, M>.handle(): Boolean
|
||||||
|
|
||||||
|
fun step(inter: MutableInterpreter<V, D, M>): Boolean = inter.handle()
|
||||||
|
|
||||||
|
operator fun plus(o: InstructionSet<V, D, M>): MultiInstructionSet<V, D, M> =
|
||||||
|
MultiInstructionSet(this, o)
|
||||||
|
|
||||||
|
operator fun plus(o: MultiInstructionSet<V, D, M>): MultiInstructionSet<V, D, M> =
|
||||||
|
MultiInstructionSet(listOf(this) + o.sets)
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultiInstructionSet<V, D : Data, M : Enum<M>>
|
||||||
|
(vararg val sets: InstructionSet<V, D, M>)
|
||||||
|
: InstructionSet<V, D, M> {
|
||||||
|
|
||||||
|
constructor(sets: List<InstructionSet<V, D, M>>) : this(*sets.toTypedArray())
|
||||||
|
|
||||||
|
override fun MutableInterpreter<V, D, M>.handle(): Boolean {
|
||||||
|
for (set in sets) if (set.step(this)) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override operator fun plus(o: InstructionSet<V, D, M>): MultiInstructionSet<V, D, M> =
|
||||||
|
MultiInstructionSet(sets.toList() + listOf(o))
|
||||||
|
}
|
||||||
@@ -1,38 +1,36 @@
|
|||||||
package befide.befunge.core
|
package befide.befunge.core
|
||||||
|
|
||||||
import befide.befunge.events.*
|
import befide.befunge.core.events.IpChange
|
||||||
import befide.befunge.state.Value
|
import befide.befunge.core.events.StackChange
|
||||||
import java.util.*
|
import befide.befunge.core.state.Data
|
||||||
import java.util.concurrent.BlockingQueue
|
import befide.befunge.core.state.Funge
|
||||||
|
import befide.befunge.core.state.Pointer
|
||||||
|
import befide.befunge.core.util.Event
|
||||||
|
import java.io.PipedReader
|
||||||
|
import java.io.PipedWriter
|
||||||
|
import java.io.Reader
|
||||||
|
import java.io.Writer
|
||||||
|
|
||||||
/**
|
interface Interpreter<V, D : Data, M : Enum<M>> {
|
||||||
* Interface for a Befunge interpreter
|
val ip: Pointer<V, M>
|
||||||
*
|
val funge: Funge<V, D>
|
||||||
* @property fungeChanged Invoked whenever the [funge] is modified
|
val stack: List<D>
|
||||||
* @property stackChanged Invoked whenever the [stack] is modified
|
|
||||||
* @property ipChanged Invoked whenever the [ip] is modified
|
|
||||||
*/
|
|
||||||
interface Interpreter {
|
|
||||||
val funge: Funge
|
|
||||||
val stack: Stack<Value>
|
|
||||||
val ip: Pointer
|
|
||||||
|
|
||||||
val fungeChanged: Event<FungeEvent>
|
fun stackDefault(): D
|
||||||
val stackChanged: Event<StackEvent>
|
|
||||||
val ipChanged: Event<IpEvent>
|
|
||||||
val outputChanged: Event<OutputEvent>
|
|
||||||
|
|
||||||
val stdInput: Queue<Char>
|
val mode: M get() = ip.mode
|
||||||
val stdOutput: Queue<String>
|
val delta: V get() = ip.delta
|
||||||
|
val pos: V get() = ip.pos
|
||||||
|
val instr: D get() = funge[ip.pos]
|
||||||
|
val next: D get() = funge[funge.next(pos, delta)]
|
||||||
|
|
||||||
/**
|
val onIpChange: Event<IpChange<V, M>>
|
||||||
* @return If [ip] is inactive after this step, indicating execution has halted, then return `false`
|
val onStackChange: Event<StackChange<D>>
|
||||||
*/
|
|
||||||
fun step(): Boolean
|
|
||||||
|
|
||||||
/**
|
var stdin: Reader?
|
||||||
* Reset the state of the interpreter to the last state before [step] was executed - that is, reset [funge] and [ip] to the state which was manually set, before any interpretation via [step]
|
val stdout: Writer?
|
||||||
*/
|
|
||||||
fun reset()
|
fun step()
|
||||||
|
fun run(afterEach: () -> Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package befide.befunge.core
|
||||||
|
|
||||||
|
import befide.befunge.core.events.IpChange
|
||||||
|
import befide.befunge.core.events.StackChange
|
||||||
|
import befide.befunge.core.events.StackOp
|
||||||
|
import befide.befunge.core.state.Data
|
||||||
|
import befide.befunge.core.state.MutableFunge
|
||||||
|
import befide.befunge.core.state.MutablePointer
|
||||||
|
import befide.befunge.core.util.Event
|
||||||
|
import befide.befunge.core.util.readAll
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
abstract class MutableInterpreter<V, D : Data, M : Enum<M>>
|
||||||
|
: Interpreter<V, D, M> {
|
||||||
|
abstract override val ip: MutablePointer<V, M>
|
||||||
|
abstract override val funge: MutableFunge<V, D>
|
||||||
|
abstract override val stack: Stack<D>
|
||||||
|
|
||||||
|
abstract val instructionSet: InstructionSet<V, D, M>
|
||||||
|
|
||||||
|
override val onIpChange: Event<IpChange<V, M>> = Event()
|
||||||
|
override val onStackChange: Event<StackChange<D>> = Event()
|
||||||
|
|
||||||
|
override fun step() {
|
||||||
|
instructionSet.step(this)
|
||||||
|
move()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyIp(op: () -> Unit) {
|
||||||
|
val from = ip.copy()
|
||||||
|
op()
|
||||||
|
val to = ip.copy()
|
||||||
|
onIpChange(IpChange(from, to))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyStack(op: StackOp, block: () -> D): D {
|
||||||
|
val res = block()
|
||||||
|
onStackChange(StackChange(op, res))
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
override var mode: M
|
||||||
|
get() = ip.mode
|
||||||
|
set(mode) = notifyIp { ip.mode = mode }
|
||||||
|
|
||||||
|
override var delta: V
|
||||||
|
get() = ip.delta
|
||||||
|
set(delta) = notifyIp { ip.delta = delta }
|
||||||
|
|
||||||
|
override var pos: V
|
||||||
|
get() = ip.pos
|
||||||
|
set(pos) = notifyIp { ip.pos = pos }
|
||||||
|
|
||||||
|
fun move(delta: V? = null) {
|
||||||
|
pos = funge.next(pos, delta ?: this.delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pop(): D = notifyStack(StackOp.Pop) {
|
||||||
|
return@notifyStack if (stack.empty()) stackDefault() else stack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pop(n: Int): List<D> {
|
||||||
|
return (0 until n).map { pop() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun push(vararg data: D) {
|
||||||
|
for (datum in data) {
|
||||||
|
notifyStack(StackOp.Push) { stack.push(datum) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package befide.befunge.core
|
|
||||||
|
|
||||||
import befide.befunge.events.Event
|
|
||||||
import befide.befunge.events.IpEvent
|
|
||||||
import befide.befunge.state.IpMode
|
|
||||||
import befide.befunge.state.Vec
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a Befunge Instruction Pointer
|
|
||||||
*
|
|
||||||
* Note that [Vec] need not be a concrete type, but is simply to be easier to implement 2d Befunge
|
|
||||||
*/
|
|
||||||
interface Pointer {
|
|
||||||
val pos: Vec
|
|
||||||
val delta: Vec
|
|
||||||
val mode: IpMode
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package befide.befunge.core.events
|
||||||
|
|
||||||
|
import befide.befunge.core.state.Data
|
||||||
|
import befide.befunge.core.state.Funge
|
||||||
|
|
||||||
|
data class FungeChange<V, D : Data>
|
||||||
|
(val pos: V,
|
||||||
|
val from: D,
|
||||||
|
val to: D)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package befide.befunge.core.events
|
||||||
|
|
||||||
|
import befide.befunge.core.state.Pointer
|
||||||
|
|
||||||
|
data class IpChange<V, M : Enum<M>>
|
||||||
|
(val from: Pointer<V, M>, val to: Pointer<V, M>)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package befide.befunge.core.events
|
||||||
|
|
||||||
|
import befide.befunge.core.state.Data
|
||||||
|
|
||||||
|
enum class StackOp { Push, Pop }
|
||||||
|
|
||||||
|
data class StackChange<D : Data>
|
||||||
|
(val op: StackOp, val data: D)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package befide.befunge.core.state
|
||||||
|
|
||||||
|
interface Data {
|
||||||
|
val char: Char?
|
||||||
|
}
|
||||||
17
befunge/src/main/kotlin/befide/befunge/core/state/Funge.kt
Normal file
17
befunge/src/main/kotlin/befide/befunge/core/state/Funge.kt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package befide.befunge.core.state
|
||||||
|
|
||||||
|
import befide.befunge.core.events.FungeChange
|
||||||
|
import befide.befunge.core.util.Event
|
||||||
|
|
||||||
|
interface Funge<V, D : Data> {
|
||||||
|
val size: V
|
||||||
|
|
||||||
|
val data: Map<V, D>
|
||||||
|
val src: String
|
||||||
|
|
||||||
|
fun next(pos: V, delta: V): V
|
||||||
|
|
||||||
|
operator fun get(pos: V): D
|
||||||
|
|
||||||
|
val onChange: Event<FungeChange<V, D>>
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package befide.befunge.core.state
|
||||||
|
|
||||||
|
import befide.befunge.core.events.FungeChange
|
||||||
|
import befide.befunge.core.util.Event
|
||||||
|
|
||||||
|
abstract class MutableFunge<V, D : Data>
|
||||||
|
: Funge<V, D> {
|
||||||
|
|
||||||
|
abstract override var data: Map<V, D>
|
||||||
|
abstract override var src: String
|
||||||
|
|
||||||
|
override val onChange = Event<FungeChange<V, D>>()
|
||||||
|
|
||||||
|
fun notifyChanged(pos: V, op: () -> Unit) {
|
||||||
|
val from = this[pos]
|
||||||
|
op()
|
||||||
|
val to = this[pos]
|
||||||
|
|
||||||
|
onChange(FungeChange(pos, from, to))
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract operator fun set(pos: V, data: D)
|
||||||
|
|
||||||
|
abstract fun clear()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package befide.befunge.core.state
|
||||||
|
|
||||||
|
interface MutablePointer<V, M : Enum<M>>
|
||||||
|
: Pointer<V, M> {
|
||||||
|
|
||||||
|
override var pos: V
|
||||||
|
override var delta: V
|
||||||
|
override var mode: M
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package befide.befunge.core.state
|
||||||
|
|
||||||
|
interface Pointer<V, M : Enum<M>> {
|
||||||
|
val pos: V
|
||||||
|
val delta: V
|
||||||
|
val mode: M
|
||||||
|
|
||||||
|
fun copy(): Pointer<V, M>
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package befide.befunge.events
|
package befide.befunge.core.util
|
||||||
|
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ internal operator fun <T> Handler<T>.invoke(t: T) = accept(t)
|
|||||||
* Sample usage:
|
* Sample usage:
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* data class ServerEvent(val joined: Boolean, val user: String)
|
* contents class ServerEvent(val joined: Boolean, val user: String)
|
||||||
|
|
||||||
* fun main(args: Array<String>) {
|
* fun main(args: Array<String>) {
|
||||||
* val event = Event<ServerEvent>()
|
* val event = Event<ServerEvent>()
|
||||||
27
befunge/src/main/kotlin/befide/befunge/core/util/Misc.kt
Normal file
27
befunge/src/main/kotlin/befide/befunge/core/util/Misc.kt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package befide.befunge.core.util
|
||||||
|
|
||||||
|
import java.io.PipedReader
|
||||||
|
import java.io.Reader
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
|
infix fun Int.mod(o: Int): Int {
|
||||||
|
var rem = this % o
|
||||||
|
if (rem < 0) rem += o
|
||||||
|
return rem
|
||||||
|
}
|
||||||
|
|
||||||
|
infix fun Long.mod(o: Long): Long {
|
||||||
|
var rem = this % o
|
||||||
|
if (rem < 0) rem += o
|
||||||
|
return rem
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> List<T>.chooseOne(): T = this[Random.nextInt(size)]
|
||||||
|
|
||||||
|
fun <T> Boolean.orNull(block: () -> T): T? = if (this) block() else null
|
||||||
|
|
||||||
|
fun Reader.readAll(): String = generateSequence {
|
||||||
|
ready().orNull { read().toChar() }
|
||||||
|
}.joinToString("")
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package befide.befunge.events
|
|
||||||
|
|
||||||
import befide.befunge.state.Value
|
|
||||||
import befide.befunge.state.Vec
|
|
||||||
|
|
||||||
data class FungeChange(val vec: Vec, val from: Value, val to: Value)
|
|
||||||
|
|
||||||
data class FungeEvent(val changes: List<FungeChange>)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package befide.befunge.events
|
|
||||||
|
|
||||||
import befide.befunge.core.Pointer
|
|
||||||
|
|
||||||
data class IpEvent(val from: Pointer, val to: Pointer)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package befide.befunge.events
|
|
||||||
|
|
||||||
class OutputEvent
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package befide.befunge.events
|
|
||||||
|
|
||||||
enum class StackAction {
|
|
||||||
Push,
|
|
||||||
Pop,
|
|
||||||
Clear
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package befide.befunge.events
|
|
||||||
|
|
||||||
import befide.befunge.state.Value
|
|
||||||
|
|
||||||
data class StackEvent(val op: StackAction, val values: List<Value>)
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package befide.befunge.state
|
|
||||||
|
|
||||||
enum class IpMode {
|
|
||||||
Normal,
|
|
||||||
String,
|
|
||||||
Inactive
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package befide.befunge.state
|
|
||||||
|
|
||||||
data class Value(val value: Long) {
|
|
||||||
constructor(value: Char) : this(value.toLong())
|
|
||||||
|
|
||||||
val asChar: Char? = if (value in (32..127)) value.toChar() else null
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package befide.befunge.state
|
|
||||||
|
|
||||||
infix fun Int.mod(other: Int): Int {
|
|
||||||
val x = this % other
|
|
||||||
return if (x >= 0) x else (x + other)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Vec(val x: Int, val y: Int) {
|
|
||||||
operator fun plus(other: Vec) = Vec(x + other.x, y + other.y)
|
|
||||||
operator fun times(c: Int) = Vec(x * c, y * c)
|
|
||||||
|
|
||||||
infix fun mod(other: Vec) = Vec(x mod other.x, y mod other.y)
|
|
||||||
operator fun unaryMinus() = Vec(-x, -y)
|
|
||||||
}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
package befide.ide
|
|
||||||
|
|
||||||
import befide.befunge.core.Interpreter
|
|
||||||
import befide.befunge.state.IpMode
|
|
||||||
import javafx.animation.Animation
|
|
||||||
import javafx.animation.Timeline
|
|
||||||
import javafx.beans.property.SimpleBooleanProperty
|
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
|
||||||
import javafx.stage.FileChooser
|
|
||||||
import javafx.util.Duration
|
|
||||||
import tornadofx.*
|
|
||||||
import tornadofx.getValue
|
|
||||||
import tornadofx.setValue
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class ActionView(val interp: Interpreter, val codeView: CodeView, val ioView: IOView, val editorView: EditorView) : View() {
|
|
||||||
val stepProperty = SimpleBooleanProperty(false)
|
|
||||||
var step by stepProperty
|
|
||||||
|
|
||||||
var runTimeline: Timeline = timeline(false) {
|
|
||||||
keyframe(Duration.seconds(0.0)) {
|
|
||||||
setOnFinished {
|
|
||||||
step = interp.step()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keyframe(Duration.seconds(1.0)) {}
|
|
||||||
|
|
||||||
cycleCount = Animation.INDEFINITE
|
|
||||||
}
|
|
||||||
|
|
||||||
val isRunningProperty = SimpleBooleanProperty(false)
|
|
||||||
var isRunning by isRunningProperty
|
|
||||||
|
|
||||||
val canResetProperty = SimpleBooleanProperty(false)
|
|
||||||
var canReset by canResetProperty
|
|
||||||
|
|
||||||
val saveFileProperty = SimpleObjectProperty<File>()
|
|
||||||
var saveFile by saveFileProperty
|
|
||||||
|
|
||||||
fun start(rate: Double) {
|
|
||||||
stop()
|
|
||||||
|
|
||||||
if (interp.ip.mode == IpMode.Inactive) reset()
|
|
||||||
|
|
||||||
isRunning = true
|
|
||||||
canReset = true
|
|
||||||
|
|
||||||
interp.funge.values = codeView.values
|
|
||||||
|
|
||||||
runTimeline.rate = rate
|
|
||||||
runTimeline.playFromStart()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun step() {
|
|
||||||
canReset = true
|
|
||||||
|
|
||||||
interp.funge.values = codeView.values
|
|
||||||
|
|
||||||
step = interp.step()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
isRunning = false
|
|
||||||
|
|
||||||
runTimeline.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reset() {
|
|
||||||
stop()
|
|
||||||
interp.reset()
|
|
||||||
ioView.reset()
|
|
||||||
canReset = false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearCode() {
|
|
||||||
reset()
|
|
||||||
codeView.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun save() {
|
|
||||||
if (saveFile == null) {
|
|
||||||
saveAs()
|
|
||||||
} else {
|
|
||||||
saveFile.writeText(codeView.src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val chooser: FileChooser = FileChooser().apply {
|
|
||||||
title = "Befunge File"
|
|
||||||
extensionFilters.setAll(
|
|
||||||
FileChooser.ExtensionFilter("Befunge 93", "*.bf", "*.b93"),
|
|
||||||
FileChooser.ExtensionFilter("Befunge 98", "*.bf", "*.b98"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveAs() {
|
|
||||||
val file: File? = chooser.showSaveDialog(primaryStage)
|
|
||||||
if (file != null) {
|
|
||||||
saveFile = file
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun open() {
|
|
||||||
val file: File? = chooser.showOpenDialog(primaryStage)
|
|
||||||
if (file != null) {
|
|
||||||
saveFile = file
|
|
||||||
|
|
||||||
editorView.title = "${saveFile.nameWithoutExtension} [${saveFile.absolutePath}] - Befide"
|
|
||||||
|
|
||||||
reset()
|
|
||||||
codeView.src = saveFile.readText()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun new() {
|
|
||||||
clearCode()
|
|
||||||
saveFile = null
|
|
||||||
editorView.title = "Befide"
|
|
||||||
}
|
|
||||||
|
|
||||||
override val root = hbox {
|
|
||||||
button("step") {
|
|
||||||
setOnAction { step() }
|
|
||||||
disableWhen(isRunningProperty)
|
|
||||||
}
|
|
||||||
|
|
||||||
separator {}
|
|
||||||
|
|
||||||
button("run") {
|
|
||||||
setOnAction { start(10000.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
button("walk") {
|
|
||||||
setOnAction { start(50.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
button("crawl") {
|
|
||||||
setOnAction { start(4.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
separator {}
|
|
||||||
|
|
||||||
button("stop") {
|
|
||||||
setOnAction { stop() }
|
|
||||||
enableWhen(isRunningProperty)
|
|
||||||
}
|
|
||||||
|
|
||||||
spacer {}
|
|
||||||
|
|
||||||
button("reset") {
|
|
||||||
setOnAction { reset() }
|
|
||||||
enableWhen(canResetProperty)
|
|
||||||
}
|
|
||||||
|
|
||||||
separator {}
|
|
||||||
|
|
||||||
button("open") {
|
|
||||||
setOnAction { open() }
|
|
||||||
disableWhen(isRunningProperty)
|
|
||||||
}
|
|
||||||
|
|
||||||
button("save as") {
|
|
||||||
setOnAction { saveAs() }
|
|
||||||
}
|
|
||||||
|
|
||||||
button("save") {
|
|
||||||
setOnAction { save() }
|
|
||||||
|
|
||||||
enableWhen(saveFileProperty.isNotNull.and(isRunningProperty.not()))
|
|
||||||
}
|
|
||||||
|
|
||||||
button("new") {
|
|
||||||
setOnAction { new() }
|
|
||||||
disableWhen(isRunningProperty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
stepProperty.onChange {
|
|
||||||
if (!it) stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package befide.ide
|
|
||||||
|
|
||||||
import befide.befunge.core.Interpreter
|
|
||||||
import befide.befunge.state.Value
|
|
||||||
import befide.befunge.state.Vec
|
|
||||||
import javafx.beans.property.ObjectProperty
|
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
|
||||||
import javafx.scene.control.Label
|
|
||||||
import tornadofx.*
|
|
||||||
|
|
||||||
class CodeLabel(val pos: Vec, val cursorPos: ObjectProperty<Vec>, val interp: Interpreter) : Label() {
|
|
||||||
var valueProperty = SimpleObjectProperty<Value>(Value(' '))
|
|
||||||
var value: Value by valueProperty
|
|
||||||
|
|
||||||
fun restyle() {
|
|
||||||
styleClass.setAll("code")
|
|
||||||
|
|
||||||
val char = value.asChar ?: '\u2022'
|
|
||||||
|
|
||||||
if (char in "0123456789") styleClass.add("code-num")
|
|
||||||
if (char in "gp") styleClass.add("code-funge")
|
|
||||||
if (char in "<>^v?#") styleClass.add("code-dir")
|
|
||||||
if (char in "@") styleClass.add("code-stop")
|
|
||||||
if (char in "_|") styleClass.add("code-condition")
|
|
||||||
if (char in "\"") styleClass.add("code-quote")
|
|
||||||
|
|
||||||
if (pos == cursorPos.value) styleClass.add("code-cursor")
|
|
||||||
if (pos == interp.ip.pos) styleClass.add("code-cursor-ip")
|
|
||||||
if (char == '\u2022') styleClass.add("unknown")
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
textProperty().bind(valueProperty.stringBinding { it?.asChar?.toString() ?: "\u2022" })
|
|
||||||
|
|
||||||
setOnMouseClicked {
|
|
||||||
cursorPos.value = pos
|
|
||||||
}
|
|
||||||
|
|
||||||
valueProperty.addListener { _, _, _ -> restyle() }
|
|
||||||
|
|
||||||
restyle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
package befide.ide
|
|
||||||
|
|
||||||
import befide.befunge.core.Interpreter
|
|
||||||
import befide.befunge.state.Value
|
|
||||||
import befide.befunge.state.Vec
|
|
||||||
import javafx.beans.property.ObjectProperty
|
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
|
||||||
import javafx.scene.input.KeyCode
|
|
||||||
import tornadofx.*
|
|
||||||
|
|
||||||
operator fun <T> List<List<T>>.get(v: Vec): T = this[v.y][v.x]
|
|
||||||
|
|
||||||
class CodeView(val interp: Interpreter) : View() {
|
|
||||||
val cursorPosProperty: ObjectProperty<Vec> = SimpleObjectProperty<Vec>(Vec(0, 0))
|
|
||||||
var cursorPos by cursorPosProperty
|
|
||||||
|
|
||||||
val cursorDeltaProperty = SimpleObjectProperty<Vec>(Vec(1, 0))
|
|
||||||
var cursorDelta by cursorDeltaProperty
|
|
||||||
|
|
||||||
var labels: List<List<CodeLabel>> = List(25) { y -> List(80) { x -> CodeLabel(Vec(x, y), cursorPosProperty, interp) } }
|
|
||||||
|
|
||||||
var values: List<List<Value>>
|
|
||||||
get() = labels.map { it.map { it.value } }
|
|
||||||
set(data) {
|
|
||||||
for (y in 0 until labels.size)
|
|
||||||
for (x in 0 until labels[y].size)
|
|
||||||
labels[y][x].value = data[y][x]
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
cursorPosProperty.addListener { _, old, new ->
|
|
||||||
labels[old].restyle()
|
|
||||||
labels[new].restyle()
|
|
||||||
}
|
|
||||||
|
|
||||||
interp.ipChanged += {
|
|
||||||
labels[it.from.pos].restyle()
|
|
||||||
labels[it.to.pos].restyle()
|
|
||||||
}
|
|
||||||
|
|
||||||
interp.fungeChanged += {
|
|
||||||
for (change in it.changes) {
|
|
||||||
labels[change.vec].value = change.to
|
|
||||||
labels[change.vec].restyle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun move(delta: Vec? = null) {
|
|
||||||
cursorPos = interp.funge.nextVec(cursorPos, delta ?: cursorDelta)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
for (row in labels) {
|
|
||||||
for (lbl in row) {
|
|
||||||
lbl.value = Value(' ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var src: String
|
|
||||||
get() = labels.joinToString("\n") { row ->
|
|
||||||
row.dropLastWhile { lbl ->
|
|
||||||
lbl.value.asChar?.isWhitespace() ?: false
|
|
||||||
}.joinToString("") { lbl ->
|
|
||||||
lbl.value.asChar?.toString() ?: "\u2022"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
val lines = value.lines()
|
|
||||||
|
|
||||||
for (row in labels) {
|
|
||||||
for (lbl in row) {
|
|
||||||
val char = lines.getOrNull(lbl.pos.y)?.getOrNull(lbl.pos.x) ?: ' '
|
|
||||||
lbl.value = Value(char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val root = hbox {
|
|
||||||
isFocusTraversable = true
|
|
||||||
|
|
||||||
addClass("code-view")
|
|
||||||
|
|
||||||
vbox {
|
|
||||||
children.setAll(labels.map { row -> hbox { children.setAll(row) } })
|
|
||||||
}
|
|
||||||
|
|
||||||
setOnMouseClicked {
|
|
||||||
requestFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
setOnKeyPressed {
|
|
||||||
when (it.code) {
|
|
||||||
KeyCode.RIGHT -> move(Vec(1, 0))
|
|
||||||
KeyCode.LEFT -> move(Vec(-1, 0))
|
|
||||||
KeyCode.DOWN -> move(Vec(0, 1))
|
|
||||||
KeyCode.UP -> move(Vec(0, -1))
|
|
||||||
else -> return@setOnKeyPressed
|
|
||||||
}
|
|
||||||
if (it.isAltDown) when (it.code) {
|
|
||||||
KeyCode.RIGHT -> cursorDelta = Vec(1, 0)
|
|
||||||
KeyCode.LEFT -> cursorDelta = Vec(-1, 0)
|
|
||||||
KeyCode.DOWN -> cursorDelta = Vec(0, 1)
|
|
||||||
KeyCode.UP -> cursorDelta = Vec(0, -1)
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
it.consume()
|
|
||||||
}
|
|
||||||
|
|
||||||
setOnKeyTyped {
|
|
||||||
for (ch in it.character) {
|
|
||||||
when {
|
|
||||||
!ch.isISOControl() -> {
|
|
||||||
cursorDelta = when (ch) {
|
|
||||||
'>' -> Vec(1, 0)
|
|
||||||
'<' -> Vec(-1, 0)
|
|
||||||
'v' -> Vec(0, 1)
|
|
||||||
'^' -> Vec(0, -1)
|
|
||||||
else -> cursorDelta
|
|
||||||
}
|
|
||||||
|
|
||||||
labels[cursorPos].value = Value(ch)
|
|
||||||
|
|
||||||
move()
|
|
||||||
}
|
|
||||||
|
|
||||||
ch == '\u0008' -> { // backspace
|
|
||||||
move(-cursorDelta)
|
|
||||||
|
|
||||||
labels[cursorPos].value = Value(' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
ch == '\u000d' -> when (cursorDelta) { // return
|
|
||||||
Vec(1, 0) -> {
|
|
||||||
move(Vec(0, 1))
|
|
||||||
cursorPos = Vec(0, cursorPos.y)
|
|
||||||
}
|
|
||||||
Vec(-1, 0) -> {
|
|
||||||
move(Vec(0, -1))
|
|
||||||
cursorPos = Vec(interp.funge.width - 1, cursorPos.y)
|
|
||||||
}
|
|
||||||
Vec(0, 1) -> {
|
|
||||||
move(Vec(-1, 0))
|
|
||||||
cursorPos = Vec(cursorPos.x, 0)
|
|
||||||
}
|
|
||||||
Vec(0, -1) -> {
|
|
||||||
move(Vec(1, 0))
|
|
||||||
cursorPos = Vec(cursorPos.x, interp.funge.height - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
println("'$ch' (${ch.toInt()})")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +1,12 @@
|
|||||||
package befide.ide
|
package befide.ide
|
||||||
|
|
||||||
import befide.befunge.b93.B93Interpreter
|
|
||||||
import befide.befunge.core.Interpreter
|
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
class EditorView : View("Befide") {
|
class EditorView : View("Befide") {
|
||||||
private var interp: Interpreter = B93Interpreter()
|
|
||||||
|
|
||||||
private val codeView = CodeView(interp)
|
|
||||||
private val stackView = StackView(interp)
|
|
||||||
private val ioView = IOView(interp)
|
|
||||||
private val actionView = ActionView(interp, codeView, ioView, this)
|
|
||||||
|
|
||||||
override val root = borderpane {
|
override val root = borderpane {
|
||||||
top { add(actionView) }
|
|
||||||
|
|
||||||
center { add(codeView) }
|
|
||||||
|
|
||||||
right { add(stackView) }
|
|
||||||
|
|
||||||
bottom { add(ioView) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
primaryStage.isResizable = false
|
primaryStage.isResizable = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDock() {
|
|
||||||
super.onDock()
|
|
||||||
|
|
||||||
codeView.root.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package befide.ide
|
|
||||||
|
|
||||||
import befide.befunge.core.Interpreter
|
|
||||||
import javafx.beans.property.SimpleStringProperty
|
|
||||||
import javafx.event.EventHandler
|
|
||||||
import tornadofx.*
|
|
||||||
import tornadofx.getValue
|
|
||||||
import tornadofx.setValue
|
|
||||||
|
|
||||||
class IOView(val interp: Interpreter) : View() {
|
|
||||||
val outputProperty = SimpleStringProperty("")
|
|
||||||
var output by outputProperty
|
|
||||||
val oldinputProperty = SimpleStringProperty("")
|
|
||||||
var oldinput by oldinputProperty
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
|
||||||
interp.outputChanged += {
|
|
||||||
var str = ""
|
|
||||||
while (!interp.stdOutput.isEmpty()) {
|
|
||||||
str += interp.stdOutput.remove()
|
|
||||||
}
|
|
||||||
output += str
|
|
||||||
}
|
|
||||||
// add listeners to interp, handle streams, idk
|
|
||||||
}
|
|
||||||
|
|
||||||
override val root = vbox {
|
|
||||||
addClass("")
|
|
||||||
textarea(outputProperty) {
|
|
||||||
isWrapText = true
|
|
||||||
isEditable = false
|
|
||||||
// width, height, idk
|
|
||||||
prefHeight = 200.0
|
|
||||||
}
|
|
||||||
textarea(oldinputProperty) {
|
|
||||||
isEditable = false
|
|
||||||
prefHeight = 0.0
|
|
||||||
|
|
||||||
oldinputProperty.onChange { scrollTop = Double.MAX_VALUE }
|
|
||||||
}
|
|
||||||
textfield {
|
|
||||||
onAction = EventHandler {
|
|
||||||
oldinput += this.text + '\n'
|
|
||||||
this.text.forEach { c -> interp.stdInput.add(c) }
|
|
||||||
this.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reset() {
|
|
||||||
output = ""
|
|
||||||
oldinput = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package befide.ide
|
|
||||||
|
|
||||||
import befide.befunge.b93.padEnd
|
|
||||||
import befide.befunge.core.Interpreter
|
|
||||||
import befide.befunge.state.Value
|
|
||||||
import javafx.beans.property.SimpleListProperty
|
|
||||||
import javafx.beans.property.SimpleObjectProperty
|
|
||||||
import javafx.beans.property.SimpleStringProperty
|
|
||||||
import javafx.collections.FXCollections
|
|
||||||
import javafx.scene.layout.Priority
|
|
||||||
import javafx.scene.text.Font
|
|
||||||
import tornadofx.*
|
|
||||||
import tornadofx.getValue
|
|
||||||
import tornadofx.setValue
|
|
||||||
|
|
||||||
class StackView(val interp: Interpreter) : View() {
|
|
||||||
val charOutProperty = SimpleStringProperty()
|
|
||||||
var charOut by charOutProperty
|
|
||||||
|
|
||||||
override val root = textarea(charOutProperty) {
|
|
||||||
addClass("stack-pane")
|
|
||||||
prefWidth = 150.0
|
|
||||||
isEditable = false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> getStackStr(mapping: (Value) -> T): String {
|
|
||||||
val num = interp.funge.height - 3
|
|
||||||
return interp.stack.takeLast(num)
|
|
||||||
.map(mapping)
|
|
||||||
.padEnd(num, "")
|
|
||||||
// .reversed()
|
|
||||||
.joinToString("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
charOut = getStackStr { "${it.asChar ?: '\u2022'} (${it.value})" }
|
|
||||||
|
|
||||||
interp.stackChanged += {
|
|
||||||
charOut = getStackStr { "${it.asChar ?: '\u2022'} (${it.value})" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user