Compare commits
26 Commits
code-view
...
interprete
| Author | SHA1 | Date | |
|---|---|---|---|
| 660a54eda5 | |||
| 14eb1a87f7 | |||
| 81750a920d | |||
| 2e03a031b0 | |||
| 8d6da62be1 | |||
| 626094320d | |||
| dad81ffd40 | |||
| ffab11f785 | |||
| dd6076441c | |||
| 53c7b87354 | |||
| fcb017e6df | |||
| bda23d7e9d | |||
| 9dfa3e124f | |||
| 4c115f41fb | |||
| 7ca74d51d1 | |||
| 02fc840c4d | |||
| 145d9fcf3c | |||
| 401c0a354a | |||
| 133ef02945 | |||
| 57520ef947 | |||
| ff4b2600a1 | |||
| dd73965e39 | |||
| 1eefc31cfe | |||
| 4cc69a8dbd | |||
| bbeb703f9d | |||
|
|
037b9a8e96 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -24,7 +24,6 @@ buildNumber.properties
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
@@ -94,3 +93,4 @@ fabric.properties
|
||||
# Mobile Tools for Java (J2ME)
|
||||
# Package Files #
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
.idea/statistic.xml
|
||||
|
||||
18
.idea/artifacts/ide_jar.xml
generated
Normal file
18
.idea/artifacts/ide_jar.xml
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
<component name="ArtifactManager">
|
||||
<artifact type="jar" build-on-make="true" name="ide:jar">
|
||||
<output-path>$PROJECT_DIR$/out/artifacts/ide_jar</output-path>
|
||||
<root id="archive" name="ide.jar">
|
||||
<element id="module-output" name="ide" />
|
||||
<element id="module-output" name="befunge" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.2.60/kotlin-stdlib-jdk8-1.2.60.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.3.0/kotlin-stdlib-1.3.0.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/javax/json/javax.json-api/1.1.2/javax.json-api-1.1.2.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/no/tornado/tornadofx/1.7.17/tornadofx-1.7.17.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/glassfish/javax.json/1.1.2/javax.json-1.1.2.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.3.0/kotlin-stdlib-common-1.3.0.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.2.60/kotlin-stdlib-jdk7-1.2.60.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.2.60/kotlin-reflect-1.2.60.jar" path-in-jar="/" />
|
||||
</root>
|
||||
</artifact>
|
||||
</component>
|
||||
@@ -1,45 +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 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,353 +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 = 10
|
||||
|
||||
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 true
|
||||
}
|
||||
|
||||
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)
|
||||
else -> noOp()
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
while (funge[ip.pos].asChar == ' ') {
|
||||
stepIP()
|
||||
}
|
||||
}
|
||||
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,48 +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)
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
import befide.befunge.events.*
|
||||
import befide.befunge.state.Value
|
||||
import java.util.*
|
||||
import java.util.concurrent.BlockingQueue
|
||||
import befide.befunge.core.events.IpChange
|
||||
import befide.befunge.core.events.StackChange
|
||||
import befide.befunge.core.state.Data
|
||||
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 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
|
||||
interface Interpreter<V, D : Data, M : Enum<M>> {
|
||||
val ip: Pointer<V, M>
|
||||
val funge: Funge<V, D>
|
||||
val stack: List<D>
|
||||
|
||||
val fungeChanged: Event<FungeEvent>
|
||||
val stackChanged: Event<StackEvent>
|
||||
val ipChanged: Event<IpEvent>
|
||||
val outputChanged: Event<OutputEvent>
|
||||
fun stackDefault(): D
|
||||
|
||||
val stdInput: Queue<Char>
|
||||
val stdOutput: Queue<String>
|
||||
val mode: M get() = ip.mode
|
||||
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)]
|
||||
|
||||
/**
|
||||
* @return If [ip] is inactive after this step, indicating execution has halted, then return `false`
|
||||
*/
|
||||
fun step(): Boolean
|
||||
val onIpChange: Event<IpChange<V, M>>
|
||||
val onStackChange: Event<StackChange<D>>
|
||||
|
||||
/**
|
||||
* 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()
|
||||
var stdin: Reader?
|
||||
val stdout: Writer?
|
||||
|
||||
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
|
||||
|
||||
@@ -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>()
|
||||
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,68 +0,0 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.core.Interpreter
|
||||
import javafx.animation.Animation
|
||||
import javafx.animation.Timeline
|
||||
import javafx.util.Duration
|
||||
import tornadofx.*
|
||||
|
||||
class ActionView(val interp: Interpreter, val codeView: CodeView, val ioView: IOView) : View() {
|
||||
var runTimeline: Timeline = timeline(false) {
|
||||
keyframe(Duration.seconds(0.0)) {
|
||||
setOnFinished {
|
||||
if (!interp.step())
|
||||
this@timeline.stop()
|
||||
}
|
||||
}
|
||||
keyframe(Duration.seconds(1.0)) {}
|
||||
|
||||
cycleCount = Animation.INDEFINITE
|
||||
}
|
||||
|
||||
override val root = hbox {
|
||||
button("step") {
|
||||
setOnAction {
|
||||
interp.funge.setString(codeView.src)
|
||||
interp.step()
|
||||
}
|
||||
}
|
||||
|
||||
button("reset") {
|
||||
setOnAction {
|
||||
interp.reset()
|
||||
ioView.reset()
|
||||
codeView.src = interp.funge.toString()
|
||||
}
|
||||
}
|
||||
|
||||
button("run") {
|
||||
setOnAction {
|
||||
interp.funge.setString(codeView.src)
|
||||
runTimeline.rate = 10000.0
|
||||
runTimeline.playFromStart()
|
||||
}
|
||||
}
|
||||
|
||||
button("walk") {
|
||||
setOnAction {
|
||||
interp.funge.setString(codeView.src)
|
||||
runTimeline.rate = 50.0
|
||||
runTimeline.playFromStart()
|
||||
}
|
||||
}
|
||||
|
||||
button("crawl") {
|
||||
setOnAction {
|
||||
interp.funge.setString(codeView.src)
|
||||
runTimeline.rate = 4.0
|
||||
runTimeline.playFromStart()
|
||||
}
|
||||
}
|
||||
|
||||
button("stop") {
|
||||
setOnAction {
|
||||
runTimeline.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.core.Interpreter
|
||||
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() {
|
||||
val charProperty = SimpleObjectProperty<Char>('\u0000')
|
||||
var char: Char by charProperty
|
||||
|
||||
fun restyle() {
|
||||
styleClass.setAll("code")
|
||||
|
||||
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 (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(charProperty.stringBinding { it?.toString() ?: " " })
|
||||
|
||||
setOnMouseClicked {
|
||||
cursorPos.value = pos
|
||||
}
|
||||
|
||||
charProperty.addListener { _, _, _ -> restyle() }
|
||||
|
||||
char = ' '
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package befide.ide
|
||||
|
||||
import befide.befunge.core.Interpreter
|
||||
import befide.befunge.state.Vec
|
||||
import javafx.beans.property.ObjectProperty
|
||||
import javafx.beans.property.SimpleBooleanProperty
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.scene.input.KeyCode
|
||||
import tornadofx.*
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
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) } }
|
||||
|
||||
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].char = change.to.asChar ?: '\u2022'
|
||||
labels[change.vec].restyle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun move(delta: Vec? = null) {
|
||||
cursorPos = interp.funge.nextVec(cursorPos, delta ?: cursorDelta)
|
||||
}
|
||||
|
||||
var src: String
|
||||
get() = labels.joinToString("\n") { row ->
|
||||
row.dropLastWhile { lbl ->
|
||||
lbl.char.isWhitespace()
|
||||
}.joinToString("") { lbl ->
|
||||
lbl.char.toString()
|
||||
}
|
||||
}
|
||||
set(value) {
|
||||
val lines = value.lines()
|
||||
|
||||
for (row in labels) {
|
||||
for (lbl in row) {
|
||||
lbl.char = lines.getOrNull(lbl.pos.y)?.getOrNull(lbl.pos.x) ?: ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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].char = ch
|
||||
|
||||
move()
|
||||
}
|
||||
|
||||
ch == '\u0008' -> { // backspace
|
||||
move(-cursorDelta)
|
||||
|
||||
labels[cursorPos].char = ' '
|
||||
}
|
||||
|
||||
else -> {
|
||||
println("'$ch' (${ch.toInt()})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +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)
|
||||
|
||||
override val root = borderpane {
|
||||
top { add(actionView) }
|
||||
|
||||
center { add(codeView) }
|
||||
|
||||
right { add(stackView) }
|
||||
|
||||
bottom { add(ioView) }
|
||||
}
|
||||
|
||||
init {
|
||||
codeView.src = """ 2>:3g" "-!v\ g30 <
|
||||
|!`"O":+1_:.:03p>03g+:"O"`|
|
||||
@ ^ p3\" ":<
|
||||
2 234567890123456789012345678901234567890123456789012345678901234567890123456789"""
|
||||
|
||||
// codeView.src = """"hello world",,,,,,,,,,,@"""
|
||||
}
|
||||
|
||||
override fun onDock() {
|
||||
super.onDock()
|
||||
|
||||
codeView.root.requestFocus()
|
||||
primaryStage.isResizable = false
|
||||
}
|
||||
}
|
||||
@@ -1,53 +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
|
||||
}
|
||||
textfield {
|
||||
onAction = EventHandler {
|
||||
oldinput += this.text + '\n'
|
||||
this.text.forEach { c -> interp.stdInput.add(c) }
|
||||
this.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
output = ""
|
||||
oldinput = ""
|
||||
}
|
||||
}
|
||||
@@ -7,3 +7,7 @@ class MainApp : App(EditorView::class) {
|
||||
importStylesheet(resources["style.css"])
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
launch<MainApp>(args)
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
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() {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
3
ide/src/main/resources/META-INF/MANIFEST.MF
Normal file
3
ide/src/main/resources/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: befide.ide.MainApp
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
* {
|
||||
-fx-font-size: 14;
|
||||
-fx-font-size: 18;
|
||||
-fx-font-fill: black;
|
||||
}
|
||||
|
||||
.code, .text-area {
|
||||
.code, .text-area, .stack-pane {
|
||||
-fx-font-family: Consolas, monospace;
|
||||
-fx-text-fill: black;
|
||||
-fx-font-size: 18;
|
||||
}
|
||||
|
||||
.code-view, .text-area {
|
||||
@@ -13,7 +14,6 @@
|
||||
-fx-border-radius: 2px;
|
||||
-fx-border-width: 1px;
|
||||
-fx-background-color: #fff;
|
||||
-fx-graphic: none;
|
||||
}
|
||||
|
||||
.code-num {
|
||||
@@ -21,17 +21,27 @@
|
||||
}
|
||||
|
||||
.code-funge {
|
||||
-fx-text-fill: #258135;
|
||||
-fx-text-fill: #418148;
|
||||
-fx-font-style: italic;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
.code-dir {
|
||||
-fx-text-fill: #b82893;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
.code-str {
|
||||
-fx-text-fill: #0a5158;
|
||||
.code-condition {
|
||||
-fx-text-fill: #ffa235;
|
||||
}
|
||||
|
||||
.code-stop {
|
||||
-fx-font-weight: bold;
|
||||
-fx-text-fill: #990208;
|
||||
}
|
||||
|
||||
.code-quote {
|
||||
-fx-text-fill: #258135;
|
||||
}
|
||||
|
||||
.code-cursor-ip {
|
||||
|
||||
Reference in New Issue
Block a user