Skip to content

Commit

Permalink
Added prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
IgorFilimonov committed Oct 11, 2023
1 parent d8ffc5e commit 125c422
Show file tree
Hide file tree
Showing 16 changed files with 640 additions and 20 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ dependencies {
implementation("org.eclipse.elk:org.eclipse.elk.alg.graphviz.layouter:$elkVersion")
implementation("org.eclipse.core:org.eclipse.core.runtime:$eclipseCoreVersion")
implementation("com.github.h0tk3y.betterParse:better-parse:$betterParseVersion")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.0")

implementation("org.openjfx:javafx-base:$javaFXVersion:$platform")
implementation("org.openjfx:javafx-graphics:$javaFXVersion:$platform")
Expand Down
123 changes: 123 additions & 0 deletions src/main/kotlin/automaton/constructor/controller/TestsController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package automaton.constructor.controller

import automaton.constructor.model.automaton.Automaton
import automaton.constructor.model.data.MemoryUnitDescriptorData
import automaton.constructor.model.data.serializersModule
import automaton.constructor.model.memory.MemoryUnitDescriptor
import automaton.constructor.model.module.executor.ExecutionStatus
import automaton.constructor.model.module.executor.Executor
import automaton.constructor.utils.I18N
import automaton.constructor.utils.addOnSuccess
import automaton.constructor.utils.runAsyncWithDialog
import automaton.constructor.view.TestAndResult
import automaton.constructor.view.TestsFragment
import automaton.constructor.view.TestsResultsFragment
import automaton.constructor.view.module.executor.executionLeafView
import javafx.concurrent.Task
import javafx.scene.control.Alert
import javafx.scene.control.ButtonType
import javafx.stage.FileChooser
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import tornadofx.*
import java.io.File

data class Test(val input: List<MemoryUnitDescriptor>)

@Serializable
data class TestsForSerializing(val tests: List<List<MemoryUnitDescriptorData>>, val automatonType: String)

class TestsController(val openedAutomaton: Automaton) : Controller() {
var wereTestsModified = false
private val formatForSerializing = Json {
serializersModule = SerializersModule {
prettyPrint = true
include(MemoryUnitDescriptorData.serializersModule)
}
}
fun saveTests(tests: List<Test>, uiComponent: UIComponent) {
val file = chooseFile(FileChooserMode.Save)
saveAsync(tests, uiComponent, file)
}
private fun saveAsync(tests: List<Test>, uiComponent: UIComponent, file: File): Task<Unit> {
return uiComponent.runAsyncWithDialog(
"Saving tests",
daemon = false
) {
val testsForSerializing = TestsForSerializing(tests.map { test -> test.input.map { it.getData() } },
openedAutomaton::class.simpleName!!)
file.writeText(formatForSerializing.encodeToString(testsForSerializing))
} addOnSuccess {
wereTestsModified = false
}
}
fun createTests() {
find<TestsFragment>(mapOf(TestsFragment::controller to this)).openWindow()
}
private fun chooseFile(mode: FileChooserMode): File =
chooseFile(
filters = arrayOf(FileChooser.ExtensionFilter("1", "*.json")),
mode = mode
).first()
fun openTests(uiComponent: UIComponent): List<Test> {
val file = chooseFile(FileChooserMode.Single)
return openAsync(uiComponent, file).get()
}
private fun openAsync(uiComponent: UIComponent, file: File): Task<List<Test>> {
return uiComponent.runAsyncWithDialog(
"Opening tests",
daemon = false
) {
val deserializedTests =
formatForSerializing.decodeFromString<TestsForSerializing>(file.readText())
if (deserializedTests.automatonType != openedAutomaton::class.simpleName!!) {
throw RuntimeException(
"Unable to load this set of tests for chosen automaton"
)
}
deserializedTests.tests.map { test -> Test(test.map { it.createDescriptor() }) }
} addOnSuccess {
wereTestsModified = false
}
}
fun runOnTests(tests: List<Test>) {
val testsAndResults = mutableListOf<TestAndResult>()
tests.forEach { test ->
val memory = openedAutomaton.memoryDescriptors.zip(test.input).map {
(descriptor, content) -> descriptor.createMemoryUnit(content)
}
val executor = Executor(openedAutomaton)
executor.start(memory)
executor.runFor()
val executionResult = when (executor.status) {
ExecutionStatus.ACCEPTED -> I18N.messages.getString("ExecutorController.Executor.Status.Accepted")
ExecutionStatus.REJECTED -> I18N.messages.getString("ExecutorController.Executor.Status.Rejected")
ExecutionStatus.FROZEN -> I18N.messages.getString("ExecutorController.Executor.Status.Frozen")
ExecutionStatus.RUNNING -> I18N.messages.getString("ExecutorController.Executor.Status.Running")
}
val graphic = executor.acceptedExeStates.firstOrNull()?.let { executionLeafView(it) }
testsAndResults.add(TestAndResult(test, executionResult, graphic))
}
find<TestsResultsFragment>(mapOf(TestsResultsFragment::testsAndResults to testsAndResults)).openWindow()
}

fun suggestSavingChanges(tests: List<Test>, uiComponent: UIComponent): Boolean {
if (!wereTestsModified) return true
val result = alert(
Alert.AlertType.CONFIRMATION,
"Would you like to save tests?",
null,
ButtonType(I18N.messages.getString("Dialog.yes.button"), ButtonType.YES.buttonData),
ButtonType(I18N.messages.getString("Dialog.no.button"), ButtonType.NO.buttonData),
ButtonType(I18N.messages.getString("Dialog.cancel.button"), ButtonType.CANCEL.buttonData),
owner = uiComponent.currentWindow,
title = I18N.messages.getString("Dialog.confirmation")
).result
if (result.buttonData == ButtonType.YES.buttonData)
saveTests(tests, uiComponent)
return result.buttonData != ButtonType.CANCEL.buttonData
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ interface MemoryUnitDescriptorData {
@Serializable
@SerialName("input-tape")
@MostlyGeneratedOrInline
object InputTapeDescriptorData : MemoryUnitDescriptorData {
override fun createDescriptor() = InputTapeDescriptor()
class InputTapeDescriptorData(val value: String = "") : MemoryUnitDescriptorData {
override fun createDescriptor() = InputTapeDescriptor(value)
}

/**
Expand All @@ -42,8 +42,11 @@ object InputTapeDescriptorData : MemoryUnitDescriptorData {
@Serializable
@SerialName("multi-track-tape")
@MostlyGeneratedOrInline
data class MultiTrackTapeDescriptorData(val trackCount: Int) : MemoryUnitDescriptorData {
override fun createDescriptor() = MultiTrackTapeDescriptor(trackCount)
data class MultiTrackTapeDescriptorData(
val trackCount: Int,
val values: List<String> = listOf()
) : MemoryUnitDescriptorData {
override fun createDescriptor() = MultiTrackTapeDescriptor(trackCount, values)
}

/**
Expand All @@ -52,8 +55,8 @@ data class MultiTrackTapeDescriptorData(val trackCount: Int) : MemoryUnitDescrip
@Serializable
@SerialName("stack")
@MostlyGeneratedOrInline
data class StackDescriptorData(val acceptsByEmptyStack: Boolean) : MemoryUnitDescriptorData {
override fun createDescriptor() = StackDescriptor(acceptsByEmptyStack)
data class StackDescriptorData(val acceptsByEmptyStack: Boolean, val value: String = "") : MemoryUnitDescriptorData {
override fun createDescriptor() = StackDescriptor(acceptsByEmptyStack, value)
}

/**
Expand All @@ -62,8 +65,8 @@ data class StackDescriptorData(val acceptsByEmptyStack: Boolean) : MemoryUnitDes
@Serializable
@SerialName("register")
@MostlyGeneratedOrInline
object RegisterDescriptorData : MemoryUnitDescriptorData {
override fun createDescriptor() = RegisterDescriptor()
class RegisterDescriptorData(val value: String = "") : MemoryUnitDescriptorData {
override fun createDescriptor() = RegisterDescriptor(value)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,7 @@ interface MemoryUnitDescriptor : Editable {
* Creates [MemoryUnit] described by this descriptor with initial data specified in editor returned by
* [createEditor]
*/
fun createMemoryUnit(): MemoryUnit
fun createMemoryUnit(initMemoryContent: MemoryUnitDescriptor = this): MemoryUnit

fun copy(): MemoryUnitDescriptor = getData().createDescriptor()
}
11 changes: 8 additions & 3 deletions src/main/kotlin/automaton/constructor/model/memory/Register.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import automaton.constructor.utils.I18N.messages
import automaton.constructor.utils.MonospaceEditableString
import tornadofx.*

class RegisterDescriptor : MonospaceEditableString("0"), MemoryUnitDescriptor {
class RegisterDescriptor() : MonospaceEditableString("0"), MemoryUnitDescriptor {
constructor(initValue: String): this() {
value = initValue
}

val expectedValue = DynamicPropertyDescriptors.stringOrEps(
messages.getString("Register.ExpectedValue"),
canBeDeemedEpsilon = false
Expand All @@ -22,9 +26,10 @@ class RegisterDescriptor : MonospaceEditableString("0"), MemoryUnitDescriptor {
override val transitionSideEffects = listOf(newValue)
override var displayName: String = messages.getString("Register")

override fun getData() = RegisterDescriptorData
override fun getData() = RegisterDescriptorData(value)

override fun createMemoryUnit() = Register(this, value)
override fun createMemoryUnit(initMemoryContent: MemoryUnitDescriptor) =
Register(this, (initMemoryContent as RegisterDescriptor).value)
}

class Register(
Expand Down
9 changes: 7 additions & 2 deletions src/main/kotlin/automaton/constructor/model/memory/Stack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import javafx.scene.layout.VBox
import tornadofx.*

class StackDescriptor(acceptsByEmptyStack: Boolean = false) : MonospaceEditableString("z"), MemoryUnitDescriptor {
constructor(acceptsByEmptyStack: Boolean, initValue: String): this(acceptsByEmptyStack) {
value = initValue
}

val expectedChar = DynamicPropertyDescriptors.charOrEps(
messages.getString("Stack.ExpectedChar"),
canBeDeemedEpsilon = false
Expand All @@ -29,9 +33,10 @@ class StackDescriptor(acceptsByEmptyStack: Boolean = false) : MonospaceEditableS
val acceptsByEmptyStackProperty = acceptsByEmptyStack.toProperty()
var acceptsByEmptyStack by acceptsByEmptyStackProperty

override fun getData() = StackDescriptorData(acceptsByEmptyStack)
override fun getData() = StackDescriptorData(acceptsByEmptyStack, value)

override fun createMemoryUnit() = Stack(this, value)
override fun createMemoryUnit(initMemoryContent: MemoryUnitDescriptor) =
Stack(this, (initMemoryContent as StackDescriptor).value)

override fun createEditor() = VBox().apply {
add(super.createTextFieldEditor())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ import automaton.constructor.utils.I18N.messages
import automaton.constructor.utils.MonospaceEditableString
import javafx.beans.binding.Bindings.`when`
import javafx.beans.value.ObservableValue
import kotlinx.serialization.Serializable
import tornadofx.getValue

class InputTapeDescriptor : MonospaceEditableString(), MemoryUnitDescriptor {
class InputTapeDescriptor() : MonospaceEditableString(), MemoryUnitDescriptor {
constructor(initValue: String) : this() {
value = initValue
}

val expectedChar = DynamicPropertyDescriptors.formalRegex(
messages.getString("InputTape.ExpectedChar")
)
Expand All @@ -27,9 +32,10 @@ class InputTapeDescriptor : MonospaceEditableString(), MemoryUnitDescriptor {
override var displayName: String = messages.getString("InputTape")
override val isAlwaysReadyToTerminate get() = false

override fun getData() = InputTapeDescriptorData
override fun getData() = InputTapeDescriptorData(value)

override fun createMemoryUnit() = InputTape(this, Track(value))
override fun createMemoryUnit(initMemoryContent: MemoryUnitDescriptor) =
InputTape(this, Track((initMemoryContent as InputTapeDescriptor).value))
}

class InputTape(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import java.text.MessageFormat

class MultiTrackTapeDescriptor(val trackCount: Int) : MemoryUnitDescriptor {
val valueProperties = List(trackCount) { "".toProperty() }
constructor(trackCount: Int, initValues: List<String>) : this(trackCount) {
valueProperties.zip(initValues).forEach { (a, b) -> a.value = b }
}

val headMoveDirection =
DynamicPropertyDescriptors.enum<HeadMoveDirection>(messages.getString("MultitrackTape.HeadMoveDirection"))
val expectedChars = List(trackCount) { i ->
Expand All @@ -40,9 +44,11 @@ class MultiTrackTapeDescriptor(val trackCount: Int) : MemoryUnitDescriptor {
else messages.getString("MultitrackTape.Multi-trackTape")
override val allowsStepByClosure get() = false

override fun getData() = MultiTrackTapeDescriptorData(trackCount)
override fun getData() = MultiTrackTapeDescriptorData(trackCount, valueProperties.map { it.value })

override fun createMemoryUnit() = MultiTrackTape(this, valueProperties.map { Track(it.value) })
override fun createMemoryUnit(initMemoryContent: MemoryUnitDescriptor) =
MultiTrackTape(this,
(initMemoryContent as MultiTrackTapeDescriptor).valueProperties.map { Track(it.value) })

override fun createEditor() = VBox().apply {
valueProperties.forEach { valueProperty ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class OutputTapeDescriptor : MemoryUnitDescriptor {

override val acceptanceRequiringPolicy get() = AcceptanceRequiringPolicy.ALWAYS

override fun createMemoryUnit() = OutputTape(descriptor = this, initValue = "")
override fun createMemoryUnit(initMemoryContent: MemoryUnitDescriptor) =
OutputTape(descriptor = this, initValue = "")

override fun createEditor(): Node? = null

Expand Down
71 changes: 71 additions & 0 deletions src/main/kotlin/automaton/constructor/view/ExamplesFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package automaton.constructor.view

import automaton.constructor.controller.FileController
import javafx.beans.property.IntegerProperty
import javafx.beans.property.StringProperty
import javafx.scene.control.ListCell
import javafx.scene.text.Text
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import tornadofx.*
import java.io.File
import java.nio.file.Files
import kotlin.io.path.Path

@Serializable
data class Example(val name: String, val description: String)

class ExampleCell(
private val counter: IntegerProperty,
private val automatonName: StringProperty
): ListCell<Example?>() {
init {
this.setOnMouseClicked {
if (it.clickCount == 2 && this.item != null)
automatonName.set(this.item!!.name)
}
}
override fun updateItem(item: Example?, empty: Boolean) {
super.updateItem(item, empty)
graphic = if (item != null) {
counter.set(counter.value + 1) // this whole counter thing obviously needs to be remade
Text().apply { text = item.name }
} else
null
}
}

class ExamplesFragment: Fragment() {
val fileController: FileController by param()
private val examples = mutableListOf<Example>().asObservable()
init {
val examplesPath = Path("${System.getProperty("user.dir")}/src/main/resources/examples/examples.json")
val deserializedExamples = Json.decodeFromString<List<Example>>(examplesPath.toFile().readText())
deserializedExamples.forEach { examples.add(it) }
}
override val root = hbox {
val examplesListView = listview(examples)
val description = Text().apply { text = "Choose an example" }
val counter = 0.toProperty()
val automatonName = "".toProperty()
examplesListView.setCellFactory { ExampleCell(counter, automatonName) }
add(description)
counter.addListener(ChangeListener { _, _, _ ->
val selectedCellIndex = examplesListView.selectionModel.selectedIndex
if (selectedCellIndex != -1) {
description.text = examples[selectedCellIndex].description
}
})
automatonName.addListener(ChangeListener { _, _, newValue ->
val automatonsPath = Path("${System.getProperty("user.dir")}/src/main/resources/examples/automatons")
Files.walk(automatonsPath).forEach {
if (it.toFile().name == "$newValue.atmtn") {
fileController.open(it.toFile())
this@ExamplesFragment.close()
}
}
})
}
}
Loading

0 comments on commit 125c422

Please sign in to comment.