Add "IOStreams" to interpreter, add basic IO to GUI
This commit is contained in:
@@ -6,6 +6,8 @@ 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 {
|
||||
@@ -22,6 +24,10 @@ class B93Interpreter : Interpreter {
|
||||
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<Char>()
|
||||
|
||||
private val fungeMods = HashMap<Vec,Value>()
|
||||
|
||||
@@ -55,9 +61,10 @@ class B93Interpreter : Interpreter {
|
||||
stackChanged.invoke(StackEvent(StackAction.Push, listOf(v)))
|
||||
}
|
||||
|
||||
private fun push(vs: List<Value>) {
|
||||
private fun push(v: Value, vararg vs: Value) {
|
||||
_push(v)
|
||||
vs.forEach { _push(it) }
|
||||
stackChanged.invoke(StackEvent(StackAction.Push, vs))
|
||||
stackChanged.invoke(StackEvent(StackAction.Push, listOf(v) + vs.toList()))
|
||||
}
|
||||
|
||||
private fun peek(): Value {
|
||||
@@ -67,7 +74,7 @@ class B93Interpreter : Interpreter {
|
||||
return Value(0)
|
||||
}
|
||||
|
||||
private fun binop(bop: Char) {
|
||||
private fun binop(bop: Char): Boolean {
|
||||
val (vb, va) = pop(2)
|
||||
val a = va.value
|
||||
val b = vb.value
|
||||
@@ -80,26 +87,26 @@ class B93Interpreter : Interpreter {
|
||||
'`' -> if (a > b) 1L else 0L
|
||||
else -> null
|
||||
}
|
||||
res?.let {
|
||||
return res?.let {
|
||||
val vres = Value(res)
|
||||
push(vres)
|
||||
}
|
||||
} != null
|
||||
}
|
||||
|
||||
private fun unop(uop: Char) {
|
||||
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
|
||||
}
|
||||
res?.let {
|
||||
return res?.let {
|
||||
val vres = Value(it)
|
||||
push(vres)
|
||||
}
|
||||
} != null
|
||||
}
|
||||
|
||||
private fun changeDir(dir: Char) {
|
||||
private fun changeDir(dir: Char): Boolean {
|
||||
val newDelta = when(dir) {
|
||||
'>' -> RIGHT
|
||||
'<' -> LEFT
|
||||
@@ -107,18 +114,19 @@ class B93Interpreter : Interpreter {
|
||||
'v' -> DOWN
|
||||
else -> null
|
||||
}
|
||||
newDelta?.let {
|
||||
return newDelta?.let {
|
||||
ip.delta = it
|
||||
}
|
||||
} != null
|
||||
}
|
||||
|
||||
private fun randomDir() {
|
||||
private fun randomDir(): Boolean {
|
||||
val dirs = listOf('<', '>', '^', 'v')
|
||||
val ind = Random().nextInt(4)
|
||||
changeDir(dirs[ind])
|
||||
return true
|
||||
}
|
||||
|
||||
private fun conditional(cop: Char) {
|
||||
private fun conditional(cop: Char): Boolean {
|
||||
val vcond = pop()
|
||||
val cond = vcond.value == 0L
|
||||
val newDelta = when(cop) {
|
||||
@@ -126,21 +134,23 @@ class B93Interpreter : Interpreter {
|
||||
'_' -> if (cond) RIGHT else LEFT
|
||||
else -> null
|
||||
}
|
||||
newDelta?.let {
|
||||
return newDelta?.let {
|
||||
ip.delta = it
|
||||
}
|
||||
} != null
|
||||
}
|
||||
|
||||
private fun toggleStrmode() {
|
||||
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) {
|
||||
private fun stackop(sop: Char): Boolean {
|
||||
var ret = true
|
||||
when (sop) {
|
||||
':' -> {
|
||||
val vc = peek().copy()
|
||||
@@ -148,52 +158,86 @@ class B93Interpreter : Interpreter {
|
||||
}
|
||||
'\\' -> {
|
||||
val (v2, v1) = pop(2)
|
||||
push(listOf(v2, v1))
|
||||
push(v2, v1)
|
||||
}
|
||||
'$' -> {
|
||||
pop()
|
||||
}
|
||||
else -> {
|
||||
ret = false
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun output(type: Char) {
|
||||
private fun output(type: Char): Boolean {
|
||||
val vv = pop()
|
||||
val v = vv.value
|
||||
val out = when (type) {
|
||||
'.' -> v.toString().toCharArray() + ' '
|
||||
',' -> charArrayOf(v.toChar())
|
||||
else -> charArrayOf()
|
||||
'.' -> v.toString().toList() + ' '
|
||||
',' -> listOf(v.toChar())
|
||||
else -> listOf()
|
||||
}
|
||||
print(out) //TEMPORARY
|
||||
//TODO output {out} chararray
|
||||
stdOutput.addAll(out)
|
||||
outputChanged.invoke(OutputEvent())
|
||||
return true
|
||||
}
|
||||
|
||||
private fun stepIP() {
|
||||
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) {
|
||||
val inp = readLine()
|
||||
val vinp = Value(when(type) {
|
||||
'&' -> inp?.toLong() ?: 0L
|
||||
'~' -> inp?.get(0)?.toLong() ?: 0L
|
||||
else -> 0L
|
||||
})
|
||||
push(vinp)
|
||||
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() {
|
||||
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() {
|
||||
private fun fput(): Boolean {
|
||||
val (vy, vx, vv) = pop(3)
|
||||
val x = vx.value.toInt()
|
||||
val y = vy.value.toInt()
|
||||
@@ -205,23 +249,27 @@ class B93Interpreter : Interpreter {
|
||||
fungeMods[loc] = old
|
||||
}
|
||||
fungeChanged(FungeEvent(listOf(FungeChange(loc,old,vv))))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun terminate() {
|
||||
private fun terminate(): Boolean {
|
||||
ip.mode = IpMode.Inactive
|
||||
return true
|
||||
}
|
||||
|
||||
private fun pushDig(dig: Char) {
|
||||
private fun pushDig(dig: Char): Boolean {
|
||||
val num = dig.toLong() - '0'.toLong()
|
||||
push(Value(num))
|
||||
return true
|
||||
}
|
||||
|
||||
private fun noOp() {}
|
||||
private fun noOp(): Boolean = false
|
||||
|
||||
private fun execInstr(instr: Value) {
|
||||
private fun execInstr(instr: Value): Boolean {
|
||||
val car = instr.asChar
|
||||
when (car) {
|
||||
return when (car) {
|
||||
null -> noOp()
|
||||
'+', '-', '*', '/', '%', '`' -> binop(car)
|
||||
'!' -> unop(car)
|
||||
@@ -241,23 +289,24 @@ class B93Interpreter : Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
private fun strMode(instr: Value) {
|
||||
private fun strMode(instr: Value): Boolean {
|
||||
if (instr.asChar == '"') {
|
||||
toggleStrmode()
|
||||
return
|
||||
return true
|
||||
}
|
||||
push(instr)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun step(): Boolean {
|
||||
val instr = funge[ip.pos]
|
||||
val currIP = ip.copy()
|
||||
when (ip.mode) {
|
||||
val processed = when (ip.mode) {
|
||||
IpMode.Inactive -> noOp()
|
||||
IpMode.Normal -> execInstr(instr)
|
||||
IpMode.String -> strMode(instr)
|
||||
}
|
||||
if (ip.mode != IpMode.Inactive) {
|
||||
if (processed) {
|
||||
stepIP()
|
||||
if (ip.mode != IpMode.String) {
|
||||
while (funge[ip.pos].asChar == ' ') {
|
||||
@@ -281,6 +330,9 @@ class B93Interpreter : Interpreter {
|
||||
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
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package befide.befunge.core
|
||||
|
||||
import befide.befunge.events.Event
|
||||
import befide.befunge.events.FungeEvent
|
||||
import befide.befunge.events.IpEvent
|
||||
import befide.befunge.events.StackEvent
|
||||
import befide.befunge.events.*
|
||||
import befide.befunge.state.Value
|
||||
import java.util.*
|
||||
import java.util.concurrent.BlockingQueue
|
||||
|
||||
/**
|
||||
* Interface for a Befunge interpreter
|
||||
@@ -22,6 +20,10 @@ interface Interpreter {
|
||||
val fungeChanged: Event<FungeEvent>
|
||||
val stackChanged: Event<StackEvent>
|
||||
val ipChanged: Event<IpEvent>
|
||||
val outputChanged: Event<OutputEvent>
|
||||
|
||||
val stdInput: Queue<Char>
|
||||
val stdOutput: Queue<Char>
|
||||
|
||||
/**
|
||||
* @return If [ip] is inactive after this step, indicating execution has halted, then return `false`
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package befide.befunge.events
|
||||
|
||||
class OutputEvent
|
||||
@@ -6,7 +6,7 @@ import javafx.animation.Timeline
|
||||
import javafx.util.Duration
|
||||
import tornadofx.*
|
||||
|
||||
class ActionView(val interp: Interpreter) : View() {
|
||||
class ActionView(val interp: Interpreter, val ioView: IOView) : View() {
|
||||
var runTimeline: Timeline = timeline(false) {
|
||||
keyframe(Duration.seconds(0.0)) {
|
||||
setOnFinished {
|
||||
@@ -25,7 +25,10 @@ class ActionView(val interp: Interpreter) : View() {
|
||||
}
|
||||
|
||||
button("reset") {
|
||||
setOnAction { interp.reset() }
|
||||
setOnAction {
|
||||
interp.reset()
|
||||
ioView.reset()
|
||||
}
|
||||
}
|
||||
|
||||
button("run") {
|
||||
|
||||
@@ -14,6 +14,5 @@ class CodeView(val interp: Interpreter) : View() {
|
||||
}
|
||||
|
||||
override val root = textarea(srcProperty) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ class EditorView : View("Befide") {
|
||||
private var interp: Interpreter = B93Interpreter()
|
||||
|
||||
private val codeView = CodeView(interp)
|
||||
private val actionView = ActionView(interp)
|
||||
private val stackView = StackView(interp)
|
||||
private val ioView = IOView(interp)
|
||||
private val actionView = ActionView(interp, ioView)
|
||||
|
||||
override val root = borderpane {
|
||||
top { add(actionView) }
|
||||
|
||||
@@ -2,24 +2,50 @@ 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 += {
|
||||
while (!interp.stdOutput.isEmpty()) {
|
||||
output += interp.stdOutput.remove()
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
textfield {
|
||||
|
||||
onAction = EventHandler {
|
||||
oldinput += this.text + '\n'
|
||||
this.text.forEach { c -> interp.stdInput.add(c) }
|
||||
this.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
output = ""
|
||||
oldinput = ""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user