Add "IOStreams" to interpreter, add basic IO to GUI

This commit is contained in:
Jacob
2018-11-20 00:05:32 -05:00
parent 22efb546f1
commit aa68debd81
7 changed files with 138 additions and 53 deletions

View File

@@ -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

View File

@@ -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`

View File

@@ -0,0 +1,3 @@
package befide.befunge.events
class OutputEvent

View File

@@ -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") {

View File

@@ -14,6 +14,5 @@ class CodeView(val interp: Interpreter) : View() {
}
override val root = textarea(srcProperty) {
}
}

View File

@@ -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) }

View File

@@ -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 = ""
}
}