From 125c422d7018ba531e0aced01e20e18cdde2f14f Mon Sep 17 00:00:00 2001 From: IgorFilimonov Date: Wed, 11 Oct 2023 22:26:48 +0300 Subject: [PATCH] Added prototype --- build.gradle.kts | 1 + .../constructor/controller/TestsController.kt | 123 ++++++++++++ .../model/data/MemoryUnitDescriptorData.kt | 19 +- .../model/memory/MemoryUnitDescriptor.kt | 4 +- .../constructor/model/memory/Register.kt | 11 +- .../constructor/model/memory/Stack.kt | 9 +- .../model/memory/tape/InputTape.kt | 12 +- .../model/memory/tape/MultitrackTape.kt | 10 +- .../model/memory/tape/OutputTape.kt | 3 +- .../constructor/view/ExamplesFragment.kt | 71 +++++++ .../automaton/constructor/view/MainWindow.kt | 15 ++ .../constructor/view/TestsFragment.kt | 92 +++++++++ .../constructor/view/TestsResultsFragment.kt | 50 +++++ .../correctBracketSeqRecognizer.atmtn | 177 ++++++++++++++++++ .../evenBinaryNumbersRecognizer.atmtn | 62 ++++++ src/main/resources/examples/examples.json | 1 + 16 files changed, 640 insertions(+), 20 deletions(-) create mode 100644 src/main/kotlin/automaton/constructor/controller/TestsController.kt create mode 100644 src/main/kotlin/automaton/constructor/view/ExamplesFragment.kt create mode 100644 src/main/kotlin/automaton/constructor/view/TestsFragment.kt create mode 100644 src/main/kotlin/automaton/constructor/view/TestsResultsFragment.kt create mode 100644 src/main/resources/examples/automatons/correctBracketSeqRecognizer.atmtn create mode 100644 src/main/resources/examples/automatons/evenBinaryNumbersRecognizer.atmtn create mode 100644 src/main/resources/examples/examples.json diff --git a/build.gradle.kts b/build.gradle.kts index ff11a17..2f6c462 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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") diff --git a/src/main/kotlin/automaton/constructor/controller/TestsController.kt b/src/main/kotlin/automaton/constructor/controller/TestsController.kt new file mode 100644 index 0000000..205ff47 --- /dev/null +++ b/src/main/kotlin/automaton/constructor/controller/TestsController.kt @@ -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) + +@Serializable +data class TestsForSerializing(val tests: List>, 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, uiComponent: UIComponent) { + val file = chooseFile(FileChooserMode.Save) + saveAsync(tests, uiComponent, file) + } + private fun saveAsync(tests: List, uiComponent: UIComponent, file: File): Task { + 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(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 { + val file = chooseFile(FileChooserMode.Single) + return openAsync(uiComponent, file).get() + } + private fun openAsync(uiComponent: UIComponent, file: File): Task> { + return uiComponent.runAsyncWithDialog( + "Opening tests", + daemon = false + ) { + val deserializedTests = + formatForSerializing.decodeFromString(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) { + val testsAndResults = mutableListOf() + 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(mapOf(TestsResultsFragment::testsAndResults to testsAndResults)).openWindow() + } + + fun suggestSavingChanges(tests: List, 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 + } +} diff --git a/src/main/kotlin/automaton/constructor/model/data/MemoryUnitDescriptorData.kt b/src/main/kotlin/automaton/constructor/model/data/MemoryUnitDescriptorData.kt index 6c20e62..8769ea8 100644 --- a/src/main/kotlin/automaton/constructor/model/data/MemoryUnitDescriptorData.kt +++ b/src/main/kotlin/automaton/constructor/model/data/MemoryUnitDescriptorData.kt @@ -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) } /** @@ -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 = listOf() +) : MemoryUnitDescriptorData { + override fun createDescriptor() = MultiTrackTapeDescriptor(trackCount, values) } /** @@ -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) } /** @@ -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) } /** diff --git a/src/main/kotlin/automaton/constructor/model/memory/MemoryUnitDescriptor.kt b/src/main/kotlin/automaton/constructor/model/memory/MemoryUnitDescriptor.kt index 4d3dd8a..efc8be1 100644 --- a/src/main/kotlin/automaton/constructor/model/memory/MemoryUnitDescriptor.kt +++ b/src/main/kotlin/automaton/constructor/model/memory/MemoryUnitDescriptor.kt @@ -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() } diff --git a/src/main/kotlin/automaton/constructor/model/memory/Register.kt b/src/main/kotlin/automaton/constructor/model/memory/Register.kt index b055e5b..ebda731 100644 --- a/src/main/kotlin/automaton/constructor/model/memory/Register.kt +++ b/src/main/kotlin/automaton/constructor/model/memory/Register.kt @@ -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 @@ -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( diff --git a/src/main/kotlin/automaton/constructor/model/memory/Stack.kt b/src/main/kotlin/automaton/constructor/model/memory/Stack.kt index 4c76624..d040254 100644 --- a/src/main/kotlin/automaton/constructor/model/memory/Stack.kt +++ b/src/main/kotlin/automaton/constructor/model/memory/Stack.kt @@ -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 @@ -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()) diff --git a/src/main/kotlin/automaton/constructor/model/memory/tape/InputTape.kt b/src/main/kotlin/automaton/constructor/model/memory/tape/InputTape.kt index f7117bb..c6ca3d4 100644 --- a/src/main/kotlin/automaton/constructor/model/memory/tape/InputTape.kt +++ b/src/main/kotlin/automaton/constructor/model/memory/tape/InputTape.kt @@ -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") ) @@ -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( diff --git a/src/main/kotlin/automaton/constructor/model/memory/tape/MultitrackTape.kt b/src/main/kotlin/automaton/constructor/model/memory/tape/MultitrackTape.kt index 9be15d2..6a883cf 100644 --- a/src/main/kotlin/automaton/constructor/model/memory/tape/MultitrackTape.kt +++ b/src/main/kotlin/automaton/constructor/model/memory/tape/MultitrackTape.kt @@ -16,6 +16,10 @@ import java.text.MessageFormat class MultiTrackTapeDescriptor(val trackCount: Int) : MemoryUnitDescriptor { val valueProperties = List(trackCount) { "".toProperty() } + constructor(trackCount: Int, initValues: List) : this(trackCount) { + valueProperties.zip(initValues).forEach { (a, b) -> a.value = b } + } + val headMoveDirection = DynamicPropertyDescriptors.enum(messages.getString("MultitrackTape.HeadMoveDirection")) val expectedChars = List(trackCount) { i -> @@ -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 -> diff --git a/src/main/kotlin/automaton/constructor/model/memory/tape/OutputTape.kt b/src/main/kotlin/automaton/constructor/model/memory/tape/OutputTape.kt index 0f729b4..bdb8f4a 100644 --- a/src/main/kotlin/automaton/constructor/model/memory/tape/OutputTape.kt +++ b/src/main/kotlin/automaton/constructor/model/memory/tape/OutputTape.kt @@ -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 diff --git a/src/main/kotlin/automaton/constructor/view/ExamplesFragment.kt b/src/main/kotlin/automaton/constructor/view/ExamplesFragment.kt new file mode 100644 index 0000000..c52d7de --- /dev/null +++ b/src/main/kotlin/automaton/constructor/view/ExamplesFragment.kt @@ -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() { + 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().asObservable() + init { + val examplesPath = Path("${System.getProperty("user.dir")}/src/main/resources/examples/examples.json") + val deserializedExamples = Json.decodeFromString>(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() + } + } + }) + } +} diff --git a/src/main/kotlin/automaton/constructor/view/MainWindow.kt b/src/main/kotlin/automaton/constructor/view/MainWindow.kt index 4807193..aad58e7 100644 --- a/src/main/kotlin/automaton/constructor/view/MainWindow.kt +++ b/src/main/kotlin/automaton/constructor/view/MainWindow.kt @@ -47,6 +47,10 @@ class MainWindow( it.selectedUndoRedoControllerBinding } private val undoRedoController: UndoRedoController by undoRedoControllerProperty + private val testsControllerBinding = fileController.openedAutomatonProperty.nonNullObjectBinding { + TestsController(it) + } + private val testsController: TestsController by testsControllerBinding override val root = borderpane { top = menubar { @@ -57,6 +61,9 @@ class MainWindow( shortcutItem(I18N.messages.getString("MainView.File.Open"), "Shortcut+O") { fileController.onOpen() } + item("Examples").action { + find(mapOf(ExamplesFragment::fileController to fileController)).openModal() + } shortcutItem(I18N.messages.getString("MainView.File.Save"), "Shortcut+S") { fileController.onSave() } @@ -142,6 +149,14 @@ class MainWindow( } } } + menu("Tests") { + item("Create a set of tests").action { + testsController.createTests() + } + item("Open a set of tests").action { + + } + } menu(I18N.messages.getString("MainView.Settings")) { menu(I18N.messages.getString("MainView.Language")) { settingsController.availableLocales.forEach { locale -> diff --git a/src/main/kotlin/automaton/constructor/view/TestsFragment.kt b/src/main/kotlin/automaton/constructor/view/TestsFragment.kt new file mode 100644 index 0000000..bbc0080 --- /dev/null +++ b/src/main/kotlin/automaton/constructor/view/TestsFragment.kt @@ -0,0 +1,92 @@ +package automaton.constructor.view + +import automaton.constructor.controller.Test +import automaton.constructor.controller.TestsController +import automaton.constructor.model.data.MemoryUnitDescriptorData +import automaton.constructor.model.data.serializersModule +import automaton.constructor.utils.* +import javafx.beans.property.Property +import tornadofx.* +import javafx.scene.control.Button +import javafx.scene.control.ListCell +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule + +class TestCell(val controller: TestsController): ListCell() { + private var currentMemoryDescriptors = controller.openedAutomaton.memoryDescriptors.map { it.copy() } + private var inputEditor = SettingGroupEditor(SettingGroup( + I18N.messages.getString("MemoryView.InputData").toProperty(), + currentMemoryDescriptors.createSettings() + )) + + override fun updateItem(item: Test?, empty: Boolean) { + super.updateItem(item, empty) + graphic = if (item != null) { + currentMemoryDescriptors = item.input + inputEditor = SettingGroupEditor(SettingGroup( + I18N.messages.getString("MemoryView.InputData").toProperty(), + currentMemoryDescriptors.createSettings() + )) + controller.wereTestsModified = true + inputEditor + } else { + null + } + } +} + +class TestsFragment: Fragment() { + val controller: TestsController by param() + private val tests = mutableListOf().asObservable() + init { + /*someKindOfProperty.addListener(ChangeListener { _, _, _ -> + suggest to save changes and close + })*/ + } + override fun onDock() { + currentWindow?.setOnCloseRequest { + if (!controller.suggestSavingChanges(tests, this)) + it.consume() + } + } + override val root = vbox { + val addButton = Button("Add") + val deleteButton = Button("Delete") + val saveButton = Button("Save") + val loadButton = Button("Load") + hbox { + add(addButton) + add(deleteButton) + } + hbox { + add(saveButton) + add(loadButton) + } + val testsListView = listview(tests) + button("Run") { + action { + controller.runOnTests(tests) + } + } + + testsListView.setCellFactory { TestCell(controller) } + addButton.setOnAction { + tests.add(Test(controller.openedAutomaton.memoryDescriptors.map { it.copy() })) + } + deleteButton.setOnAction { + val selectedCellIndex = testsListView.selectionModel.selectedIndex + if (selectedCellIndex != -1) { + tests.removeAt(selectedCellIndex) + } + } + saveButton.setOnAction { + controller.saveTests(tests, this@TestsFragment) + } + loadButton.setOnAction { + val openedTests = controller.openTests(this@TestsFragment) + openedTests.forEach { test -> tests.add(test) } + } + } +} diff --git a/src/main/kotlin/automaton/constructor/view/TestsResultsFragment.kt b/src/main/kotlin/automaton/constructor/view/TestsResultsFragment.kt new file mode 100644 index 0000000..1bea928 --- /dev/null +++ b/src/main/kotlin/automaton/constructor/view/TestsResultsFragment.kt @@ -0,0 +1,50 @@ +package automaton.constructor.view + +import automaton.constructor.controller.Test +import automaton.constructor.utils.I18N +import automaton.constructor.utils.SettingGroup +import automaton.constructor.utils.SettingGroupEditor +import automaton.constructor.utils.createSettings +import javafx.scene.control.TableCell +import javafx.scene.control.TableColumn +import javafx.scene.control.cell.PropertyValueFactory +import javafx.scene.layout.HBox +import tornadofx.Fragment +import tornadofx.* +import tornadofx.toProperty + +data class TestAndResult(val test: Test, val result: String, val graphic: SettingGroupEditor?) + +class TestTableCell: TableCell() { + override fun updateItem(item: Test?, empty: Boolean) { + super.updateItem(item, empty) + graphic = if (item != null) { + val inputEditor = SettingGroupEditor(SettingGroup( + I18N.messages.getString("MemoryView.InputData").toProperty(), + item.input.createSettings() + )) + inputEditor.editingDisabled = true + HBox(inputEditor) + } else { + null + } + } +} + +class TestsResultsFragment: Fragment() { + val testsAndResults: List by param() + override val root = tableview(testsAndResults.asObservable()) + private val testColumn = TableColumn("Test") + private val resultColumn = TableColumn("Result") + private val descriptionColumn = TableColumn("Description") + + init { + testColumn.cellValueFactory = PropertyValueFactory("test") + testColumn.setCellFactory { TestTableCell() } + resultColumn.cellValueFactory = PropertyValueFactory("result") + descriptionColumn.cellValueFactory = PropertyValueFactory("graphic") + root.columns.add(testColumn) + root.columns.add(resultColumn) + root.columns.add(descriptionColumn) + } +} diff --git a/src/main/resources/examples/automatons/correctBracketSeqRecognizer.atmtn b/src/main/resources/examples/automatons/correctBracketSeqRecognizer.atmtn new file mode 100644 index 0000000..5c93c2f --- /dev/null +++ b/src/main/resources/examples/automatons/correctBracketSeqRecognizer.atmtn @@ -0,0 +1,177 @@ +{ + "base": { + "type": "pushdown-automaton", + "inputTape": { + }, + "stacks": [ + { + "acceptsByEmptyStack": false + } + ] + }, + "vertices": [ + { + "type": "state", + "id": 5, + "name": "S5", + "x": 497707.5165449231, + "y": 500085.0165449231, + "isFinal": true + }, + { + "type": "state", + "id": 0, + "name": "S0", + "x": 498787.5, + "y": 500075.0, + "isInitial": true + }, + { + "type": "state", + "id": 3, + "name": "S3", + "x": 499860.0165449231, + "y": 500712.5165449231 + }, + { + "type": "state", + "id": 1, + "name": "S1", + "x": 499857.5165449231, + "y": 499382.5165449231 + }, + { + "type": "state", + "id": 6, + "name": "S6", + "x": 497705.0165449231, + "y": 500720.0165449231, + "isFinal": true + }, + { + "type": "state", + "id": 4, + "name": "S4", + "x": 497712.5165449231, + "y": 499375.0165449231, + "isFinal": true + }, + { + "type": "state", + "id": 2, + "name": "S2", + "x": 499855.0165449231, + "y": 500057.5165449231 + } + ], + "transitions": [ + { + "source": 6, + "target": 0, + "properties": [ + "ε", + "ε", + "ε" + ] + }, + { + "source": 0, + "target": 3, + "properties": [ + "[", + "ε", + "[" + ] + }, + { + "source": 0, + "target": 2, + "properties": [ + "{", + "ε", + "{" + ] + }, + { + "source": 3, + "target": 0, + "properties": [ + "ε", + "ε", + "ε" + ] + }, + { + "source": 0, + "target": 5, + "properties": [ + "}", + "{", + "ε" + ] + }, + { + "source": 5, + "target": 0, + "properties": [ + "ε", + "ε", + "ε" + ] + }, + { + "source": 1, + "target": 0, + "properties": [ + "ε", + "ε", + "ε" + ] + }, + { + "source": 4, + "target": 0, + "properties": [ + "ε", + "ε", + "ε" + ] + }, + { + "source": 0, + "target": 6, + "properties": [ + "]", + "[", + "ε" + ] + }, + { + "source": 0, + "target": 4, + "properties": [ + ")", + "(", + "ε" + ] + }, + { + "source": 0, + "target": 1, + "properties": [ + "(", + "ε", + "(" + ] + }, + { + "source": 2, + "target": 0, + "properties": [ + "ε", + "ε", + "ε" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/examples/automatons/evenBinaryNumbersRecognizer.atmtn b/src/main/resources/examples/automatons/evenBinaryNumbersRecognizer.atmtn new file mode 100644 index 0000000..4001228 --- /dev/null +++ b/src/main/resources/examples/automatons/evenBinaryNumbersRecognizer.atmtn @@ -0,0 +1,62 @@ +{ + "base": { + "type": "finite-automaton", + "inputTape": { + } + }, + "vertices": [ + { + "type": "state", + "id": 1, + "name": "S1", + "x": 499979.9880546928, + "y": 499624.9880546928, + "isFinal": true + }, + { + "type": "state", + "id": 2, + "name": "S2", + "x": 499977.4880546928, + "y": 500317.4880546928 + }, + { + "type": "state", + "id": 0, + "name": "S0", + "x": 498950.0, + "y": 500027.5, + "isInitial": true + } + ], + "transitions": [ + { + "source": 1, + "target": 0, + "properties": [ + "ε" + ] + }, + { + "source": 2, + "target": 0, + "properties": [ + "ε" + ] + }, + { + "source": 0, + "target": 1, + "properties": [ + "0" + ] + }, + { + "source": 0, + "target": 2, + "properties": [ + "1" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/examples/examples.json b/src/main/resources/examples/examples.json new file mode 100644 index 0000000..5edc1c2 --- /dev/null +++ b/src/main/resources/examples/examples.json @@ -0,0 +1 @@ +[{"name":"evenBinaryNumbersRecognizer","description":"Even binary numbers recognizer description"},{"name":"correctBracketSeqRecognizer","description":"Correct bracket sequence recognizer description"}] \ No newline at end of file