14 Commits

Author SHA1 Message Date
9dfa3e124f better file selection, rerun handling 2018-11-26 19:12:29 -05:00
4c115f41fb fix termination 2018-11-26 18:42:31 -05:00
7ca74d51d1 title rename 2018-11-26 15:51:01 -05:00
02fc840c4d termination updates UI 2018-11-26 13:37:27 -05:00
145d9fcf3c open, save files 2018-11-26 13:18:17 -05:00
401c0a354a open file 2018-11-26 12:54:47 -05:00
133ef02945 visual tweaks 2018-11-26 12:36:12 -05:00
57520ef947 visual tweaks 2018-11-26 12:35:47 -05:00
ff4b2600a1 make stack start at top to prevent scrolling issue 2018-11-25 23:31:04 -05:00
dd73965e39 make stack start at top to prevent scrolling issue 2018-11-25 23:26:23 -05:00
1eefc31cfe fixed broken start-stop-rerun issue 2018-11-25 22:52:28 -05:00
4cc69a8dbd Merge remote-tracking branch 'origin/master' 2018-11-25 22:17:38 -05:00
bbeb703f9d add more code styles 2018-11-25 22:17:35 -05:00
Jacob
037b9a8e96 Fix code that starts on a space not running 2018-11-25 22:09:32 -05:00
13 changed files with 284 additions and 84 deletions

18
.idea/artifacts/ide_jar.xml generated Normal file
View 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>

View File

@@ -14,6 +14,14 @@ class B93Funge : Funge {
private var cars = Array(height) { Array(width) { Value(' ') } } 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 { override fun get(vec: Vec): Value {
return cars[vec.y][vec.x] return cars[vec.y][vec.x]
} }

View File

@@ -29,7 +29,7 @@ class B93Interpreter : Interpreter {
override val stdInput = LinkedList<Char>() override val stdInput = LinkedList<Char>()
override val stdOutput = LinkedList<String>() override val stdOutput = LinkedList<String>()
private var outBuf: StringBuffer = StringBuffer() private var outBuf: StringBuffer = StringBuffer()
private var bufLimit = 10 private var bufLimit = 0
private val fungeMods = HashMap<Vec,Value>() private val fungeMods = HashMap<Vec,Value>()
@@ -266,7 +266,7 @@ class B93Interpreter : Interpreter {
outBuf.delete(0, outBuf.length) outBuf.delete(0, outBuf.length)
outputChanged.invoke(OutputEvent()) outputChanged.invoke(OutputEvent())
ip.mode = IpMode.Inactive ip.mode = IpMode.Inactive
return true return false
} }
private fun pushDig(dig: Char): Boolean { private fun pushDig(dig: Char): Boolean {
@@ -295,7 +295,8 @@ class B93Interpreter : Interpreter {
'&', '~' -> input(car) '&', '~' -> input(car)
'@' -> terminate() '@' -> terminate()
in '0'..'9' -> pushDig(car) in '0'..'9' -> pushDig(car)
else -> noOp() ' ' -> true
else -> true
} }
} }
@@ -319,9 +320,14 @@ class B93Interpreter : Interpreter {
if (processed) { if (processed) {
stepIP() stepIP()
if (ip.mode != IpMode.String) { if (ip.mode != IpMode.String) {
while (funge[ip.pos].asChar == ' ') { var numsteps = 0
val maxsteps = if (funge.height > funge.width) funge.height else funge.width
while (funge[ip.pos].asChar == ' ' && numsteps++ < maxsteps) {
stepIP() stepIP()
} }
if (numsteps == maxsteps) {
ip.mode = IpMode.Inactive
}
} }
val newIP = ip.copy() val newIP = ip.copy()
ipChanged(IpEvent(currIP, newIP)) ipChanged(IpEvent(currIP, newIP))

View File

@@ -12,6 +12,8 @@ interface Funge {
operator fun get(vec: Vec): Value operator fun get(vec: Vec): Value
operator fun set(vec: Vec, value: 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] * Get the next position to be executed starting at [vec] and stepping by [delta]
*/ */

View File

@@ -1,17 +1,26 @@
package befide.ide package befide.ide
import befide.befunge.core.Interpreter import befide.befunge.core.Interpreter
import befide.befunge.state.IpMode
import javafx.animation.Animation import javafx.animation.Animation
import javafx.animation.Timeline import javafx.animation.Timeline
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.stage.FileChooser
import javafx.util.Duration import javafx.util.Duration
import tornadofx.* 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
class ActionView(val interp: Interpreter, val codeView: CodeView, val ioView: IOView) : View() {
var runTimeline: Timeline = timeline(false) { var runTimeline: Timeline = timeline(false) {
keyframe(Duration.seconds(0.0)) { keyframe(Duration.seconds(0.0)) {
setOnFinished { setOnFinished {
if (!interp.step()) step = interp.step()
this@timeline.stop()
} }
} }
keyframe(Duration.seconds(1.0)) {} keyframe(Duration.seconds(1.0)) {}
@@ -19,50 +28,158 @@ class ActionView(val interp: Interpreter, val codeView: CodeView, val ioView: IO
cycleCount = Animation.INDEFINITE 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 { override val root = hbox {
button("step") { button("step") {
setOnAction { setOnAction { step() }
interp.funge.setString(codeView.src) disableWhen(isRunningProperty)
interp.step()
}
} }
button("reset") { separator {}
setOnAction {
interp.reset()
ioView.reset()
codeView.src = interp.funge.toString()
}
}
button("run") { button("run") {
setOnAction { setOnAction { start(10000.0) }
interp.funge.setString(codeView.src)
runTimeline.rate = 10000.0
runTimeline.playFromStart()
}
} }
button("walk") { button("walk") {
setOnAction { setOnAction { start(50.0) }
interp.funge.setString(codeView.src)
runTimeline.rate = 50.0
runTimeline.playFromStart()
}
} }
button("crawl") { button("crawl") {
setOnAction { setOnAction { start(4.0) }
interp.funge.setString(codeView.src)
runTimeline.rate = 4.0
runTimeline.playFromStart()
}
} }
separator {}
button("stop") { button("stop") {
setOnAction { setOnAction { stop() }
runTimeline.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()
}
} }

View File

@@ -1,6 +1,7 @@
package befide.ide package befide.ide
import befide.befunge.core.Interpreter import befide.befunge.core.Interpreter
import befide.befunge.state.Value
import befide.befunge.state.Vec import befide.befunge.state.Vec
import javafx.beans.property.ObjectProperty import javafx.beans.property.ObjectProperty
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
@@ -8,15 +9,20 @@ import javafx.scene.control.Label
import tornadofx.* import tornadofx.*
class CodeLabel(val pos: Vec, val cursorPos: ObjectProperty<Vec>, val interp: Interpreter) : Label() { class CodeLabel(val pos: Vec, val cursorPos: ObjectProperty<Vec>, val interp: Interpreter) : Label() {
val charProperty = SimpleObjectProperty<Char>('\u0000') var valueProperty = SimpleObjectProperty<Value>(Value(' '))
var char: Char by charProperty var value: Value by valueProperty
fun restyle() { fun restyle() {
styleClass.setAll("code") styleClass.setAll("code")
val char = value.asChar ?: '\u2022'
if (char in "0123456789") styleClass.add("code-num") if (char in "0123456789") styleClass.add("code-num")
if (char in "gp") styleClass.add("code-funge") if (char in "gp") styleClass.add("code-funge")
if (char in "<>^v?#") styleClass.add("code-dir") 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 == cursorPos.value) styleClass.add("code-cursor")
if (pos == interp.ip.pos) styleClass.add("code-cursor-ip") if (pos == interp.ip.pos) styleClass.add("code-cursor-ip")
@@ -24,14 +30,14 @@ class CodeLabel(val pos: Vec, val cursorPos: ObjectProperty<Vec>, val interp: In
} }
init { init {
textProperty().bind(charProperty.stringBinding { it?.toString() ?: " " }) textProperty().bind(valueProperty.stringBinding { it?.asChar?.toString() ?: "\u2022" })
setOnMouseClicked { setOnMouseClicked {
cursorPos.value = pos cursorPos.value = pos
} }
charProperty.addListener { _, _, _ -> restyle() } valueProperty.addListener { _, _, _ -> restyle() }
char = ' ' restyle()
} }
} }

View File

@@ -1,14 +1,12 @@
package befide.ide package befide.ide
import befide.befunge.core.Interpreter import befide.befunge.core.Interpreter
import befide.befunge.state.Value
import befide.befunge.state.Vec import befide.befunge.state.Vec
import javafx.beans.property.ObjectProperty import javafx.beans.property.ObjectProperty
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import javafx.scene.input.KeyCode import javafx.scene.input.KeyCode
import tornadofx.* import tornadofx.*
import tornadofx.getValue
import tornadofx.setValue
operator fun <T> List<List<T>>.get(v: Vec): T = this[v.y][v.x] operator fun <T> List<List<T>>.get(v: Vec): T = this[v.y][v.x]
@@ -21,6 +19,14 @@ class CodeView(val interp: Interpreter) : View() {
var labels: List<List<CodeLabel>> = List(25) { y -> List(80) { x -> CodeLabel(Vec(x, y), cursorPosProperty, interp) } } 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 { init {
cursorPosProperty.addListener { _, old, new -> cursorPosProperty.addListener { _, old, new ->
labels[old].restyle() labels[old].restyle()
@@ -34,7 +40,7 @@ class CodeView(val interp: Interpreter) : View() {
interp.fungeChanged += { interp.fungeChanged += {
for (change in it.changes) { for (change in it.changes) {
labels[change.vec].char = change.to.asChar ?: '\u2022' labels[change.vec].value = change.to
labels[change.vec].restyle() labels[change.vec].restyle()
} }
} }
@@ -44,12 +50,20 @@ class CodeView(val interp: Interpreter) : View() {
cursorPos = interp.funge.nextVec(cursorPos, delta ?: cursorDelta) cursorPos = interp.funge.nextVec(cursorPos, delta ?: cursorDelta)
} }
fun clear() {
for (row in labels) {
for (lbl in row) {
lbl.value = Value(' ')
}
}
}
var src: String var src: String
get() = labels.joinToString("\n") { row -> get() = labels.joinToString("\n") { row ->
row.dropLastWhile { lbl -> row.dropLastWhile { lbl ->
lbl.char.isWhitespace() lbl.value.asChar?.isWhitespace() ?: false
}.joinToString("") { lbl -> }.joinToString("") { lbl ->
lbl.char.toString() lbl.value.asChar?.toString() ?: "\u2022"
} }
} }
set(value) { set(value) {
@@ -57,7 +71,8 @@ class CodeView(val interp: Interpreter) : View() {
for (row in labels) { for (row in labels) {
for (lbl in row) { for (lbl in row) {
lbl.char = lines.getOrNull(lbl.pos.y)?.getOrNull(lbl.pos.x) ?: ' ' val char = lines.getOrNull(lbl.pos.y)?.getOrNull(lbl.pos.x) ?: ' '
lbl.value = Value(char)
} }
} }
} }
@@ -105,7 +120,7 @@ class CodeView(val interp: Interpreter) : View() {
else -> cursorDelta else -> cursorDelta
} }
labels[cursorPos].char = ch labels[cursorPos].value = Value(ch)
move() move()
} }
@@ -113,7 +128,26 @@ class CodeView(val interp: Interpreter) : View() {
ch == '\u0008' -> { // backspace ch == '\u0008' -> { // backspace
move(-cursorDelta) move(-cursorDelta)
labels[cursorPos].char = ' ' 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 -> { else -> {

View File

@@ -10,7 +10,7 @@ class EditorView : View("Befide") {
private val codeView = CodeView(interp) private val codeView = CodeView(interp)
private val stackView = StackView(interp) private val stackView = StackView(interp)
private val ioView = IOView(interp) private val ioView = IOView(interp)
private val actionView = ActionView(interp, codeView, ioView) private val actionView = ActionView(interp, codeView, ioView, this)
override val root = borderpane { override val root = borderpane {
top { add(actionView) } top { add(actionView) }
@@ -23,12 +23,7 @@ class EditorView : View("Befide") {
} }
init { init {
codeView.src = """ 2>:3g" "-!v\ g30 < primaryStage.isResizable = false
|!`"O":+1_:.:03p>03g+:"O"`|
@ ^ p3\" ":<
2 234567890123456789012345678901234567890123456789012345678901234567890123456789"""
// codeView.src = """"hello world",,,,,,,,,,,@"""
} }
override fun onDock() { override fun onDock() {

View File

@@ -36,6 +36,8 @@ class IOView(val interp: Interpreter) : View() {
textarea(oldinputProperty) { textarea(oldinputProperty) {
isEditable = false isEditable = false
prefHeight = 0.0 prefHeight = 0.0
oldinputProperty.onChange { scrollTop = Double.MAX_VALUE }
} }
textfield { textfield {
onAction = EventHandler { onAction = EventHandler {

View File

@@ -7,3 +7,7 @@ class MainApp : App(EditorView::class) {
importStylesheet(resources["style.css"]) importStylesheet(resources["style.css"])
} }
} }
fun main(args: Array<String>) {
launch<MainApp>(args)
}

View File

@@ -3,7 +3,12 @@ package befide.ide
import befide.befunge.b93.padEnd import befide.befunge.b93.padEnd
import befide.befunge.core.Interpreter import befide.befunge.core.Interpreter
import befide.befunge.state.Value import befide.befunge.state.Value
import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections
import javafx.scene.layout.Priority
import javafx.scene.text.Font
import tornadofx.* import tornadofx.*
import tornadofx.getValue import tornadofx.getValue
import tornadofx.setValue import tornadofx.setValue
@@ -11,37 +16,27 @@ import tornadofx.setValue
class StackView(val interp: Interpreter) : View() { class StackView(val interp: Interpreter) : View() {
val charOutProperty = SimpleStringProperty() val charOutProperty = SimpleStringProperty()
var charOut by charOutProperty var charOut by charOutProperty
val longOutProperty = SimpleStringProperty()
var longOut by longOutProperty
override val root = textarea(charOutProperty) {
override val root = hbox { addClass("stack-pane")
textarea(charOutProperty) { prefWidth = 150.0
maxWidth = 1.0 isEditable = false
paddingHorizontal = 1
isEditable = false
}
textarea(longOutProperty) {
prefWidth = 90.0
isEditable = false
}
} }
private fun <T> getStackStr(mapping: (Value) -> T): String { private fun <T> getStackStr(mapping: (Value) -> T): String {
return interp.stack.takeLast(interp.funge.height) val num = interp.funge.height - 3
.map (mapping) return interp.stack.takeLast(num)
.padEnd(interp.funge.height, "") .map(mapping)
.reversed() .padEnd(num, "")
// .reversed()
.joinToString("\n") .joinToString("\n")
} }
init { init {
charOut = getStackStr { it.asChar ?: '*'} charOut = getStackStr { "${it.asChar ?: '\u2022'} (${it.value})" }
longOut = getStackStr { it.value }
interp.stackChanged += { interp.stackChanged += {
charOut = getStackStr { it.asChar ?: '*'} charOut = getStackStr { "${it.asChar ?: '\u2022'} (${it.value})" }
longOut = getStackStr { it.value }
} }
} }
} }

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: befide.ide.MainApp

View File

@@ -1,11 +1,12 @@
* { * {
-fx-font-size: 14; -fx-font-size: 18;
-fx-font-fill: black; -fx-font-fill: black;
} }
.code, .text-area { .code, .text-area, .stack-pane {
-fx-font-family: Consolas, monospace; -fx-font-family: Consolas, monospace;
-fx-text-fill: black; -fx-text-fill: black;
-fx-font-size: 18;
} }
.code-view, .text-area { .code-view, .text-area {
@@ -13,7 +14,6 @@
-fx-border-radius: 2px; -fx-border-radius: 2px;
-fx-border-width: 1px; -fx-border-width: 1px;
-fx-background-color: #fff; -fx-background-color: #fff;
-fx-graphic: none;
} }
.code-num { .code-num {
@@ -21,17 +21,27 @@
} }
.code-funge { .code-funge {
-fx-text-fill: #258135; -fx-text-fill: #418148;
-fx-font-style: italic; -fx-font-style: italic;
-fx-font-weight: bold; -fx-font-weight: bold;
} }
.code-dir { .code-dir {
-fx-text-fill: #b82893; -fx-text-fill: #b82893;
-fx-font-weight: bold;
} }
.code-str { .code-condition {
-fx-text-fill: #0a5158; -fx-text-fill: #ffa235;
}
.code-stop {
-fx-font-weight: bold;
-fx-text-fill: #990208;
}
.code-quote {
-fx-text-fill: #258135;
} }
.code-cursor-ip { .code-cursor-ip {