Merge pull request #2 from JCRaymond/master

Restructuring to support IO, IO implemented in GUI
This commit is contained in:
2018-11-23 15:55:32 -05:00
committed by GitHub
9 changed files with 232 additions and 60 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,12 @@ 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<String>()
private var outBuf: StringBuffer = StringBuffer()
private var bufLimit = 10
private val fungeMods = HashMap<Vec,Value>()
@@ -55,9 +63,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 +76,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 +89,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 +116,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 +136,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 +160,91 @@ 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() + ' '
',' -> v.toChar().toString()
else -> null
}
print(out) //TEMPORARY
//TODO output {out} chararray
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() {
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 +256,30 @@ class B93Interpreter : Interpreter {
fungeMods[loc] = old
}
fungeChanged(FungeEvent(listOf(FungeChange(loc,old,vv))))
return true
}
return false
}
private fun terminate() {
private fun terminate(): Boolean {
stdOutput.add(outBuf.toString())
outBuf.delete(0, outBuf.length)
outputChanged.invoke(OutputEvent())
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 +299,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 +340,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<String>
/**
* @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

@@ -3,5 +3,5 @@ package befide.befunge.state
data class Value(val value: Long) {
constructor(value: Char) : this(value.toLong())
val asChar: Char? = if (value in (32..126)) value.toChar() else null
val asChar: Char? = if (value in (32..127)) value.toChar() else null
}

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,19 +25,29 @@ class ActionView(val interp: Interpreter) : View() {
}
button("reset") {
setOnAction { interp.reset() }
setOnAction {
interp.reset()
ioView.reset()
}
}
button("run") {
setOnAction {
runTimeline.rate = 1000.0
runTimeline.rate = 10000000.0
runTimeline.playFromStart()
}
}
button("walk") {
setOnAction {
runTimeline.rate = 50.0
runTimeline.playFromStart()
}
}
button("crawl") {
setOnAction {
runTimeline.rate = 2.0
runTimeline.rate = 4.0
runTimeline.playFromStart()
}
}

View File

@@ -14,6 +14,7 @@ class CodeView(val interp: Interpreter) : View() {
}
override val root = textarea(srcProperty) {
prefRowCount = 25
prefColumnCount = 80
}
}

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) }
@@ -27,6 +27,35 @@ class EditorView : View("Befide") {
}
init {
codeView.src = """64+"!dlroW ,olleH">:#,_@"""
codeView.src = """>84*>:#v_55+"ude.ub@yelruta">:#,_@>188*+>\02p\12p\:22p#v_${'$'} 55+,1- v
^ 0 v +1\ _^#-+*< >22g02g*"_@"*-!1- #v_v>
>:>::3g: ,\188 ^^ -1\g21\g22<p3\"_":<
________________________________@_________________________________^ p3\"@":<"""
// codeView.src = """--------------------------------------------------------------------------------
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// |-
// """.trimMargin()
}
}

View File

@@ -2,24 +2,52 @@ 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
}
textfield {
onAction = EventHandler {
oldinput += this.text + '\n'
this.text.forEach { c -> interp.stdInput.add(c) }
this.clear()
}
}
}
fun reset() {
output = ""
oldinput = ""
}
}

View File

@@ -1,10 +1,47 @@
package befide.ide
import befide.befunge.b93.padEnd
import befide.befunge.core.Interpreter
import befide.befunge.state.Value
import javafx.beans.property.SimpleStringProperty
import tornadofx.*
import tornadofx.getValue
import tornadofx.setValue
class StackView(val interp: Interpreter) : View() {
override val root = textarea {
prefWidth = 100.0
val charOutProperty = SimpleStringProperty()
var charOut by charOutProperty
val longOutProperty = SimpleStringProperty()
var longOut by longOutProperty
override val root = hbox {
textarea(charOutProperty) {
maxWidth = 1.0
paddingHorizontal = 1
isEditable = false
}
textarea(longOutProperty) {
prefWidth = 90.0
isEditable = false
}
}
}
private fun <T> getStackStr(mapping: (Value) -> T): String {
return interp.stack.takeLast(interp.funge.height)
.map (mapping)
.padEnd(interp.funge.height, "")
.reversed()
.joinToString("\n")
}
init {
charOut = getStackStr { it.asChar ?: '*'}
longOut = getStackStr { it.value }
interp.stackChanged += {
charOut = getStackStr { it.asChar ?: '*'}
longOut = getStackStr { it.value }
}
}
}