working framework
This commit is contained in:
@@ -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
|
||||
150
befunge/src/main/kotlin/befide/befunge/b93/Befunge93.kt
Normal file
150
befunge/src/main/kotlin/befide/befunge/b93/Befunge93.kt
Normal file
@@ -0,0 +1,150 @@
|
||||
package befide.befunge.b93
|
||||
|
||||
import befide.befunge.core.*
|
||||
import befide.befunge.core.util.chooseOne
|
||||
import befide.befunge.core.util.readAll
|
||||
import befide.befunge.events.Event
|
||||
import java.io.PipedReader
|
||||
import java.io.PipedWriter
|
||||
import java.util.*
|
||||
|
||||
|
||||
class Interpreter93(stdinSrc: PipedWriter, stdoutDest: PipedReader)
|
||||
: Interpreter<Vec2, LongData, PointerMode> {
|
||||
|
||||
override val ip = Pointer93()
|
||||
override val funge = Funge93()
|
||||
override val stack = Stack<LongData>()
|
||||
|
||||
override val onIpChange = Event<IpChange<Vec2, PointerMode>>()
|
||||
override val onStackChange = Event<StackChange<LongData>>()
|
||||
|
||||
override val stdin: PipedReader = PipedReader(stdinSrc)
|
||||
override val stdout: PipedWriter = PipedWriter(stdoutDest)
|
||||
|
||||
private fun notifyIp(op: () -> Unit) {
|
||||
val from = ip.copy()
|
||||
op()
|
||||
val to = ip.copy()
|
||||
onIpChange(IpChange(from, to))
|
||||
}
|
||||
|
||||
private fun move(dir: Vec2? = null) = notifyIp {
|
||||
ip.pos = funge.next(ip.pos, dir ?: ip.delta)
|
||||
}
|
||||
|
||||
private fun pop(): LongData {
|
||||
val d = if (stack.empty()) LongData.ZERO else stack.pop()
|
||||
onStackChange(StackChange(StackOp.Pop, d))
|
||||
return d
|
||||
}
|
||||
|
||||
private fun push(vararg data: LongData) {
|
||||
for (d in data) {
|
||||
stack.push(d)
|
||||
onStackChange(StackChange(StackOp.Push, d))
|
||||
}
|
||||
}
|
||||
|
||||
private fun argPop(n: Int, op: (Array<LongData>) -> Unit) {
|
||||
val args = (0 until n).map { pop() }.toTypedArray()
|
||||
op(args)
|
||||
}
|
||||
|
||||
private var mode
|
||||
get() = ip.mode
|
||||
set(mode) = notifyIp { ip.mode = mode }
|
||||
|
||||
private var delta
|
||||
get() = ip.delta
|
||||
set(delta) = notifyIp { ip.delta = delta }
|
||||
|
||||
override fun step() {
|
||||
val instr = funge[ip.pos]
|
||||
|
||||
when (mode) {
|
||||
PointerMode.Terminated -> return
|
||||
PointerMode.String -> when (instr.char) {
|
||||
null -> Unit
|
||||
|
||||
'"' -> mode = PointerMode.Normal
|
||||
|
||||
else -> push(instr)
|
||||
}
|
||||
PointerMode.Normal -> when (instr.char) {
|
||||
null -> Unit
|
||||
in "0123456789abcdef" -> push(LongData(instr.char.toString().toLong(16)))
|
||||
|
||||
'+' -> argPop(2) { (b, a) -> push(a + b) }
|
||||
'-' -> argPop(2) { (b, a) -> push(a - b) }
|
||||
'*' -> argPop(2) { (b, a) -> push(a * b) }
|
||||
'/' -> argPop(2) { (b, a) -> push(a / b) }
|
||||
'%' -> argPop(2) { (b, a) -> push(a mod b) }
|
||||
'!' -> argPop(1) { (n) -> push(LongData(if (n.data == 0L) 1L else 0L)) }
|
||||
'`' -> argPop(2) { (b, a) -> push(LongData(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()
|
||||
|
||||
'_' -> argPop(1) { (n) -> delta = if (n.data == 0L) Vec2.RIGHT else Vec2.LEFT }
|
||||
'|' -> argPop(1) { (n) -> delta = if (n.data == 0L) Vec2.DOWN else Vec2.UP }
|
||||
|
||||
'"' -> mode = PointerMode.String
|
||||
|
||||
':' -> argPop(1) { (n) -> push(n, n) }
|
||||
'\\' -> argPop(2) { (b, a) -> push(b, a) }
|
||||
'$' -> pop()
|
||||
|
||||
'.' -> argPop(1) { (n) -> stdout.write("${n.data} ") }
|
||||
',' -> argPop(1) { (ch) -> stdout.write(ch.data.toChar().toString()) }
|
||||
|
||||
'p' -> argPop(3) { (y, x, n) -> funge[Vec2(x.data.toInt(), y.data.toInt())] = n }
|
||||
'g' -> argPop(2) { (y, x) -> push(funge[Vec2(x.data.toInt(), y.data.toInt())]) }
|
||||
|
||||
'~' -> push(LongData(stdin.read().toLong()))
|
||||
'&' -> {
|
||||
val chars = generateSequence {
|
||||
stdin.read().takeIf { it.toChar().isDigit() }
|
||||
}
|
||||
val long = chars.joinToString("").toLong()
|
||||
push(LongData(long))
|
||||
}
|
||||
|
||||
'@' -> mode = PointerMode.Terminated
|
||||
}
|
||||
}
|
||||
|
||||
move()
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
stdin.readAll()
|
||||
stdout.flush()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val stdinSrc = PipedWriter()
|
||||
val stdoutDest = PipedReader()
|
||||
|
||||
val int = Interpreter93(stdinSrc, stdoutDest)
|
||||
|
||||
// int.onIpChange += {
|
||||
// println(it.to.pos)
|
||||
// }
|
||||
|
||||
int.funge.src = """2>:3g" "-!v\ g30 <
|
||||
|!`"O":+1_:.:03p>03g+:"O"`|
|
||||
@ ^ p3\" ":<
|
||||
2 234567890123456789012345678901234567890123456789012345678901234567890123456789"""
|
||||
|
||||
while (int.ip.mode != PointerMode.Terminated) {
|
||||
int.step()
|
||||
print(stdoutDest.readAll())
|
||||
}
|
||||
|
||||
}
|
||||
56
befunge/src/main/kotlin/befide/befunge/b93/Funge93.kt
Normal file
56
befunge/src/main/kotlin/befide/befunge/b93/Funge93.kt
Normal file
@@ -0,0 +1,56 @@
|
||||
package befide.befunge.b93
|
||||
|
||||
import befide.befunge.core.FungeChange
|
||||
import befide.befunge.core.MutableFunge
|
||||
import befide.befunge.events.Event
|
||||
|
||||
class Funge93
|
||||
: MutableFunge<Vec2, LongData> {
|
||||
override val size = Vec2(80, 25)
|
||||
|
||||
override val onChange = Event<FungeChange<Vec2, LongData>>()
|
||||
|
||||
override fun next(pos: Vec2, delta: Vec2): Vec2 = (pos + delta) mod size
|
||||
|
||||
val contents = Array(size.y) { Array(size.x) { LongData.SPACE } }
|
||||
|
||||
override fun get(pos: Vec2): LongData = contents[pos.y][pos.x]
|
||||
override fun set(pos: Vec2, data: LongData) {
|
||||
val from = this[pos]
|
||||
contents[pos.y][pos.x] = data
|
||||
onChange(FungeChange(this, pos, from, data))
|
||||
}
|
||||
|
||||
override var data: List<List<LongData>>
|
||||
get() {
|
||||
return contents.map(Array<LongData>::toList)
|
||||
}
|
||||
set(data) {
|
||||
for (y in 0 until size.y) for (x in 0 until size.x) {
|
||||
val pos = Vec2(x, y)
|
||||
val new = data.getOrNull(y)?.getOrNull(x) ?: this[pos]
|
||||
this[pos] = new
|
||||
}
|
||||
}
|
||||
|
||||
override var src: String
|
||||
get() {
|
||||
return contents.map { row ->
|
||||
row.dropLastWhile { ch ->
|
||||
ch.char?.isISOControl() ?: false
|
||||
}.map { ch ->
|
||||
ch.char ?: '\u2022' // bullet
|
||||
}.joinToString("")
|
||||
}.dropLastWhile { line ->
|
||||
line.isEmpty()
|
||||
}.joinToString("\n")
|
||||
}
|
||||
set(data) {
|
||||
val lines = data.lines()
|
||||
for (y in 0 until size.y) for (x in 0 until size.x) {
|
||||
val pos = Vec2(x, y)
|
||||
val new = lines.getOrNull(y)?.getOrNull(x)?.let(::LongData) ?: this[pos]
|
||||
this[pos] = new
|
||||
}
|
||||
}
|
||||
}
|
||||
27
befunge/src/main/kotlin/befide/befunge/b93/LongData.kt
Normal file
27
befunge/src/main/kotlin/befide/befunge/b93/LongData.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package befide.befunge.b93
|
||||
|
||||
import befide.befunge.core.Data
|
||||
import befide.befunge.core.util.mod
|
||||
|
||||
data class LongData(val data: Long)
|
||||
: Data {
|
||||
|
||||
companion object {
|
||||
val SPACE = LongData(' ')
|
||||
val ZERO = LongData(0)
|
||||
}
|
||||
|
||||
override val char = data.toChar().takeUnless { it.isISOControl() }
|
||||
|
||||
constructor(char: Char) : this(char.toLong())
|
||||
|
||||
operator fun plus(o: LongData) = LongData(data + o.data)
|
||||
operator fun minus(o: LongData) = LongData(data - o.data)
|
||||
operator fun times(o: LongData) = LongData(data * o.data)
|
||||
operator fun div(o: LongData) = LongData(data / o.data)
|
||||
operator fun unaryMinus() = LongData(-data)
|
||||
|
||||
infix fun mod(o: LongData) = LongData(data mod o.data)
|
||||
|
||||
override fun toString() = "${char ?: '\u2022'} ($data)"
|
||||
}
|
||||
12
befunge/src/main/kotlin/befide/befunge/b93/Pointer93.kt
Normal file
12
befunge/src/main/kotlin/befide/befunge/b93/Pointer93.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package befide.befunge.b93
|
||||
|
||||
import befide.befunge.core.MutablePointer
|
||||
|
||||
enum class PointerMode {
|
||||
Normal, String, Terminated
|
||||
}
|
||||
|
||||
data class Pointer93(override var pos: Vec2 = Vec2.ZERO,
|
||||
override var delta: Vec2 = Vec2.RIGHT,
|
||||
override var mode: PointerMode = PointerMode.Normal)
|
||||
: MutablePointer<Vec2, PointerMode>
|
||||
30
befunge/src/main/kotlin/befide/befunge/b93/Vec2.kt
Normal file
30
befunge/src/main/kotlin/befide/befunge/b93/Vec2.kt
Normal file
@@ -0,0 +1,30 @@
|
||||
package befide.befunge.b93
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,75 @@
|
||||
package befide.befunge.core
|
||||
|
||||
import befide.befunge.events.*
|
||||
import befide.befunge.state.Value
|
||||
import befide.befunge.events.Event
|
||||
import java.io.PipedReader
|
||||
import java.io.PipedWriter
|
||||
import java.util.*
|
||||
import java.util.concurrent.BlockingQueue
|
||||
|
||||
/**
|
||||
* Interface for a Befunge interpreter
|
||||
*
|
||||
* @property fungeChanged Invoked whenever the [funge] is modified
|
||||
* @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>
|
||||
val stackChanged: Event<StackEvent>
|
||||
val ipChanged: Event<IpEvent>
|
||||
val outputChanged: Event<OutputEvent>
|
||||
|
||||
val stdInput: Queue<Char>
|
||||
val stdOutput: Queue<String>
|
||||
|
||||
/**
|
||||
* @return If [ip] is inactive after this step, indicating execution has halted, then return `false`
|
||||
*/
|
||||
fun step(): Boolean
|
||||
|
||||
/**
|
||||
* 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]
|
||||
*/
|
||||
fun reset()
|
||||
interface Pointer<V, M : Enum<M>> {
|
||||
val pos: V
|
||||
val delta: V
|
||||
val mode: M
|
||||
}
|
||||
|
||||
interface MutablePointer<V, M : Enum<M>>
|
||||
: Pointer<V, M> {
|
||||
|
||||
override var pos: V
|
||||
override var delta: V
|
||||
override var mode: M
|
||||
}
|
||||
|
||||
interface Data {
|
||||
val char: Char?
|
||||
}
|
||||
|
||||
data class FungeChange<V, D : Data>
|
||||
(val funge: Funge<V, D>,
|
||||
val pos: V,
|
||||
val from: D,
|
||||
val to: D)
|
||||
|
||||
interface Funge<V, D : Data> {
|
||||
val size: V
|
||||
|
||||
val data: List<List<D>>
|
||||
val src: String
|
||||
|
||||
fun next(pos: V, delta: V): V
|
||||
|
||||
operator fun get(pos: V): D
|
||||
|
||||
val onChange: Event<FungeChange<V, D>>
|
||||
}
|
||||
|
||||
interface MutableFunge<V, D : Data>
|
||||
: Funge<V, D> {
|
||||
|
||||
override var data: List<List<D>>
|
||||
override var src: String
|
||||
|
||||
operator fun set(pos: V, data: D)
|
||||
}
|
||||
|
||||
data class IpChange<V, M : Enum<M>>
|
||||
(val from: Pointer<V, M>, val to: Pointer<V, M>)
|
||||
|
||||
enum class StackOp { Push, Pop }
|
||||
|
||||
data class StackChange<D : Data>
|
||||
(val op: StackOp, val data: D)
|
||||
|
||||
interface Interpreter<V, D : Data, M : Enum<M>> {
|
||||
val ip: Pointer<V, M>
|
||||
val funge: Funge<V, D>
|
||||
val stack: List<D>
|
||||
|
||||
val onIpChange: Event<IpChange<V, M>>
|
||||
val onStackChange: Event<StackChange<D>>
|
||||
|
||||
val stdin: PipedReader
|
||||
val stdout: PipedWriter
|
||||
|
||||
fun step()
|
||||
fun reset()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
23
befunge/src/main/kotlin/befide/befunge/core/util/Misc.kt
Normal file
23
befunge/src/main/kotlin/befide/befunge/core/util/Misc.kt
Normal file
@@ -0,0 +1,23 @@
|
||||
package befide.befunge.core.util
|
||||
|
||||
import java.io.PipedReader
|
||||
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 PipedReader.readAll(): String = generateSequence {
|
||||
if (ready()) read().toChar() else null
|
||||
}.joinToString("")
|
||||
@@ -12,7 +12,7 @@ internal operator fun <T> Handler<T>.invoke(t: T) = accept(t)
|
||||
* 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>) {
|
||||
* val event = Event<ServerEvent>()
|
||||
|
||||
@@ -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
|
||||
|
||||
import befide.befunge.b93.B93Interpreter
|
||||
import befide.befunge.core.Interpreter
|
||||
import tornadofx.*
|
||||
|
||||
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 {
|
||||
top { add(actionView) }
|
||||
|
||||
center { add(codeView) }
|
||||
|
||||
right { add(stackView) }
|
||||
|
||||
bottom { add(ioView) }
|
||||
}
|
||||
|
||||
init {
|
||||
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