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(' ') } }
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 {
return cars[vec.y][vec.x]
}

View File

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

View File

@@ -12,6 +12,8 @@ interface Funge {
operator fun get(vec: Vec): 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]
*/

View File

@@ -1,17 +1,26 @@
package befide.ide
import befide.befunge.core.Interpreter
import befide.befunge.state.IpMode
import javafx.animation.Animation
import javafx.animation.Timeline
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.stage.FileChooser
import javafx.util.Duration
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) {
keyframe(Duration.seconds(0.0)) {
setOnFinished {
if (!interp.step())
this@timeline.stop()
step = interp.step()
}
}
keyframe(Duration.seconds(1.0)) {}
@@ -19,50 +28,158 @@ class ActionView(val interp: Interpreter, val codeView: CodeView, val ioView: IO
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 {
button("step") {
setOnAction {
interp.funge.setString(codeView.src)
interp.step()
}
setOnAction { step() }
disableWhen(isRunningProperty)
}
button("reset") {
setOnAction {
interp.reset()
ioView.reset()
codeView.src = interp.funge.toString()
}
}
separator {}
button("run") {
setOnAction {
interp.funge.setString(codeView.src)
runTimeline.rate = 10000.0
runTimeline.playFromStart()
}
setOnAction { start(10000.0) }
}
button("walk") {
setOnAction {
interp.funge.setString(codeView.src)
runTimeline.rate = 50.0
runTimeline.playFromStart()
}
setOnAction { start(50.0) }
}
button("crawl") {
setOnAction {
interp.funge.setString(codeView.src)
runTimeline.rate = 4.0
runTimeline.playFromStart()
}
setOnAction { start(4.0) }
}
separator {}
button("stop") {
setOnAction {
runTimeline.stop()
}
setOnAction { 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
import befide.befunge.core.Interpreter
import befide.befunge.state.Value
import befide.befunge.state.Vec
import javafx.beans.property.ObjectProperty
import javafx.beans.property.SimpleObjectProperty
@@ -8,15 +9,20 @@ 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
var valueProperty = SimpleObjectProperty<Value>(Value(' '))
var value: Value by valueProperty
fun restyle() {
styleClass.setAll("code")
val char = value.asChar ?: '\u2022'
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 (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 == 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 {
textProperty().bind(charProperty.stringBinding { it?.toString() ?: " " })
textProperty().bind(valueProperty.stringBinding { it?.asChar?.toString() ?: "\u2022" })
setOnMouseClicked {
cursorPos.value = pos
}
charProperty.addListener { _, _, _ -> restyle() }
valueProperty.addListener { _, _, _ -> restyle() }
char = ' '
restyle()
}
}

View File

@@ -1,14 +1,12 @@
package befide.ide
import befide.befunge.core.Interpreter
import befide.befunge.state.Value
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]
@@ -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 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 {
cursorPosProperty.addListener { _, old, new ->
labels[old].restyle()
@@ -34,7 +40,7 @@ class CodeView(val interp: Interpreter) : View() {
interp.fungeChanged += {
for (change in it.changes) {
labels[change.vec].char = change.to.asChar ?: '\u2022'
labels[change.vec].value = change.to
labels[change.vec].restyle()
}
}
@@ -44,12 +50,20 @@ class CodeView(val interp: Interpreter) : View() {
cursorPos = interp.funge.nextVec(cursorPos, delta ?: cursorDelta)
}
fun clear() {
for (row in labels) {
for (lbl in row) {
lbl.value = Value(' ')
}
}
}
var src: String
get() = labels.joinToString("\n") { row ->
row.dropLastWhile { lbl ->
lbl.char.isWhitespace()
lbl.value.asChar?.isWhitespace() ?: false
}.joinToString("") { lbl ->
lbl.char.toString()
lbl.value.asChar?.toString() ?: "\u2022"
}
}
set(value) {
@@ -57,7 +71,8 @@ class CodeView(val interp: Interpreter) : View() {
for (row in labels) {
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
}
labels[cursorPos].char = ch
labels[cursorPos].value = Value(ch)
move()
}
@@ -113,7 +128,26 @@ class CodeView(val interp: Interpreter) : View() {
ch == '\u0008' -> { // backspace
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 -> {

View File

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

View File

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

View File

@@ -7,3 +7,7 @@ class MainApp : App(EditorView::class) {
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.core.Interpreter
import befide.befunge.state.Value
import javafx.beans.property.SimpleListProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections
import javafx.scene.layout.Priority
import javafx.scene.text.Font
import tornadofx.*
import tornadofx.getValue
import tornadofx.setValue
@@ -11,37 +16,27 @@ 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
}
override val root = textarea(charOutProperty) {
addClass("stack-pane")
prefWidth = 150.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()
val num = interp.funge.height - 3
return interp.stack.takeLast(num)
.map(mapping)
.padEnd(num, "")
// .reversed()
.joinToString("\n")
}
init {
charOut = getStackStr { it.asChar ?: '*'}
longOut = getStackStr { it.value }
charOut = getStackStr { "${it.asChar ?: '\u2022'} (${it.value})" }
interp.stackChanged += {
charOut = getStackStr { it.asChar ?: '*'}
longOut = getStackStr { it.value }
charOut = getStackStr { "${it.asChar ?: '\u2022'} (${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;
}
.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 {