From eb817311fbec44232b1a9ad57c114d91647ac542 Mon Sep 17 00:00:00 2001 From: Colin Date: Sun, 28 Mar 2021 11:12:48 -0400 Subject: [PATCH] Added State.checkForSubStatesInFinalState and renamed 'leaf' to 'atomic'. --- .../xyz/colinholzman/makina/CodeGenerator.kt | 28 +++++++++---------- .../src/xyz/colinholzman/makina/State.kt | 17 +++++++---- .../colinholzman/makina/StateConfiguration.kt | 4 +-- .../src/xyz/colinholzman/makina/Transition.kt | 4 +-- .../test/xyz/colinholzman/makina/ParseTest.kt | 10 +++---- .../makina/StateConfigurationTest.kt | 4 +-- .../test/xyz/colinholzman/makina/StateTest.kt | 19 +++++++++---- 7 files changed, 50 insertions(+), 36 deletions(-) diff --git a/makina-compiler/src/xyz/colinholzman/makina/CodeGenerator.kt b/makina-compiler/src/xyz/colinholzman/makina/CodeGenerator.kt index 9832c5f..893499f 100644 --- a/makina-compiler/src/xyz/colinholzman/makina/CodeGenerator.kt +++ b/makina-compiler/src/xyz/colinholzman/makina/CodeGenerator.kt @@ -53,10 +53,10 @@ class CodeGenerator(val machine: Machine, } } - private fun generateExitActions(handler: Handler.Event, sourceState: State, activeLeafState: State, output: PrintWriter) { + private fun generateExitActions(handler: Handler.Event, sourceState: State, activeAtomicState: State, output: PrintWriter) { if (handler.target != null) output.apply { val target = handler.getTargetState(sourceState, machine) - val transition = Transition(activeLeafState, sourceState, target, handler.target.kind) + val transition = Transition(activeAtomicState, sourceState, target, handler.target.kind) val exitSet = transition.getExitSet() for (stateToExit in exitSet) { for (exit in stateToExit.handlers.filterIsInstance()) { @@ -67,13 +67,13 @@ class CodeGenerator(val machine: Machine, } } - private fun generateEntryActions(handler: Handler.Event, sourceState: State, activeLeafState: State, output: PrintWriter) { + private fun generateEntryActions(handler: Handler.Event, sourceState: State, activeAtomicState: State, output: PrintWriter) { if (handler.target != null) output.apply { val target = handler.getTargetState(sourceState, machine) - val transition = Transition(activeLeafState, sourceState, target, handler.target.kind) + val transition = Transition(activeAtomicState, sourceState, target, handler.target.kind) val entrySet = transition.getEntrySet() - val targetLeafState = entrySet.last() - println("\t\t\tself->state = ${machine.id}_${targetLeafState.getFullyQualifiedIdString()};") + val targetAtomicState = entrySet.last() + println("\t\t\tself->state = ${machine.id}_${targetAtomicState.getFullyQualifiedIdString()};") for (stateToEnter in entrySet) { for (entry in stateToEnter.handlers.filterIsInstance()) { println("\t\t\t${entry.action}(self, event);") @@ -88,14 +88,14 @@ class CodeGenerator(val machine: Machine, println("#include ") println("#include \"${machine.id}.h\"") println() - for (state in machine.states.filter { it.isLeafState() }) { + for (state in machine.states.filter { it.isAtomic() }) { println("static int ${machine.id}_${state.getFullyQualifiedIdString()}($machineStructName *, $machineEventName *);") } println() - for (activeLeafState in machine.states.filter { it.isLeafState() }) { - println("static int ${machine.id}_${activeLeafState.getFullyQualifiedIdString()}($machineStructName *self, $machineEventName *event) {") + for (activeAtomicState in machine.states.filter { it.isAtomic() }) { + println("static int ${machine.id}_${activeAtomicState.getFullyQualifiedIdString()}($machineStructName *self, $machineEventName *event) {") println("\tif (!self || !event) return -1;") - val config = activeLeafState.getStateConfiguration() + val config = activeAtomicState.getStateConfiguration() println("\tswitch (event->id) {") val handlerGroups = config.getHandlers().groupByIdAndRemoveRedundantHandlers() for (entry in handlerGroups) { @@ -106,11 +106,11 @@ class CodeGenerator(val machine: Machine, val handler = handlerStatePair.second val guard = if (handler.guard != null) "${handler.guard}(self, event)" else "1" println("\t\tif ($guard) {") - generateExitActions(handler, sourceState, activeLeafState, output) + generateExitActions(handler, sourceState, activeAtomicState, output) if (handler.action != null) { println("\t\t\t${handler.action}(self, event);") } - generateEntryActions(handler, sourceState, activeLeafState, output) + generateEntryActions(handler, sourceState, activeAtomicState, output) println("\t\t\tbreak;") println("\t\t}") } @@ -125,8 +125,8 @@ class CodeGenerator(val machine: Machine, println("int ${machine.id}_init($machineStructName *self) {") println("\tif (!self) return -1;") val initialConfig = machine.getInitialStateConfiguration() - val initialLeafState = initialConfig.getLeafState() - println("\tself->state = ${machine.id}_${initialLeafState.getFullyQualifiedIdString()};") + val initialAtomicState = initialConfig.getAtomicState() + println("\tself->state = ${machine.id}_${initialAtomicState.getFullyQualifiedIdString()};") val entryHandlers = initialConfig.getEntryHandlers() for (handler in entryHandlers) { println("\t${handler.action}(self, NULL);") diff --git a/makina-compiler/src/xyz/colinholzman/makina/State.kt b/makina-compiler/src/xyz/colinholzman/makina/State.kt index 4c789da..17676c7 100644 --- a/makina-compiler/src/xyz/colinholzman/makina/State.kt +++ b/makina-compiler/src/xyz/colinholzman/makina/State.kt @@ -17,6 +17,7 @@ class State(val id: String, set(value) { field = value checkForDuplicateInitialStates() + checkForSubStatesInFinalState() } //parent field is valid only after this State has been included in a Machine @@ -64,6 +65,12 @@ class State(val id: String, } } + private fun checkForSubStatesInFinalState() { + if (!final) return + if (subStates.isNotEmpty()) + throw RuntimeException("final must be atomic at $location") + } + private fun checkForHandlersInFinalState() { if (!final) return for (handler in handlers) { @@ -84,7 +91,7 @@ class State(val id: String, ?: throw RuntimeException("parent state $parentId not found at $location") } - fun isLeafState(): Boolean { + fun isAtomic(): Boolean { return subStates.isEmpty() } @@ -127,10 +134,10 @@ class State(val id: String, } } - //if this is a leaf state then get the config that is this state + ancestors - //it this is not a leaf state then get the initial state configuration + //if this is an atomic state then get the config that is this state + ancestors + //it this is not an atomic state then get the initial state configuration fun getStateConfiguration(): StateConfiguration { - return if (isLeafState()) + return if (isAtomic()) StateConfiguration((getAncestors() + this).toSet()) else { val initial = subStates.find { it.initial } ?: subStates.first() @@ -144,7 +151,7 @@ class State(val id: String, //gets a list of the sub states to enter if this is the target of a transition fun getDefaultEntrySet(): List { - return if (isLeafState()) { + return if (isAtomic()) { emptyList() } else { val initialSubState = getInitialSubState() diff --git a/makina-compiler/src/xyz/colinholzman/makina/StateConfiguration.kt b/makina-compiler/src/xyz/colinholzman/makina/StateConfiguration.kt index 0821436..fbb1952 100644 --- a/makina-compiler/src/xyz/colinholzman/makina/StateConfiguration.kt +++ b/makina-compiler/src/xyz/colinholzman/makina/StateConfiguration.kt @@ -28,8 +28,8 @@ data class StateConfiguration(val states: Set) { return orderedStates.reversed().flatMap { state -> state.handlers.filterIsInstance() } } - //Returns the leaf state for this configuration. - fun getLeafState(): State { + //Returns the atomic state for this configuration. + fun getAtomicState(): State { return orderedStates.first() } diff --git a/makina-compiler/src/xyz/colinholzman/makina/Transition.kt b/makina-compiler/src/xyz/colinholzman/makina/Transition.kt index c1ac56e..109bbe6 100644 --- a/makina-compiler/src/xyz/colinholzman/makina/Transition.kt +++ b/makina-compiler/src/xyz/colinholzman/makina/Transition.kt @@ -2,7 +2,7 @@ package xyz.colinholzman.makina import xyz.colinholzman.makina.State.Companion.getLCCA -data class Transition(val activeLeafState: State, +data class Transition(val activeAtomicState: State, val source: State, val target: State, val kind: Target.Kind = Target.Kind.DEFAULT) { fun getEntrySet(): List { @@ -12,7 +12,7 @@ data class Transition(val activeLeafState: State, fun getExitSet(): List { val domain = getDomain() - return activeLeafState.getStateConfiguration().states + return activeAtomicState.getStateConfiguration().states .filter { it.isDescendantOf(domain) } .sortedBy { it.getDepth() }.reversed() } diff --git a/makina-compiler/test/xyz/colinholzman/makina/ParseTest.kt b/makina-compiler/test/xyz/colinholzman/makina/ParseTest.kt index 672dd22..3f2d186 100644 --- a/makina-compiler/test/xyz/colinholzman/makina/ParseTest.kt +++ b/makina-compiler/test/xyz/colinholzman/makina/ParseTest.kt @@ -128,15 +128,15 @@ internal class ParseTest { } @Test - fun testNestedStateIsLeafState() { + fun testNestedStateIsAtomic() { val machine = Parse.fileFromString("machine foo; state bar { state baz {} state qux {} } state fred {}") val bar = machine.states.find { it.id == "bar" }!! val baz = machine.states.find { it.id == "baz" }!! val qux = machine.states.find { it.id == "qux" }!! val fred = machine.states.find { it.id == "fred" }!! - assertFalse(bar.isLeafState()) - assert(baz.isLeafState()) - assert(qux.isLeafState()) - assert(fred.isLeafState()) + assertFalse(bar.isAtomic()) + assert(baz.isAtomic()) + assert(qux.isAtomic()) + assert(fred.isAtomic()) } } diff --git a/makina-compiler/test/xyz/colinholzman/makina/StateConfigurationTest.kt b/makina-compiler/test/xyz/colinholzman/makina/StateConfigurationTest.kt index d3f9178..cfb7de1 100644 --- a/makina-compiler/test/xyz/colinholzman/makina/StateConfigurationTest.kt +++ b/makina-compiler/test/xyz/colinholzman/makina/StateConfigurationTest.kt @@ -45,13 +45,13 @@ internal class StateConfigurationTest { } @Test - fun getLeafState() { + fun getAtomicState() { val parent = Parse.state("state foo { on bar; }").first() val child = Parse.state("state .foo.baz { on qux; }").first() parent.subStates = listOf(child) child.parent = parent val config = StateConfiguration(setOf(child, parent)) - assertEquals(child, config.getLeafState()) + assertEquals(child, config.getAtomicState()) } @Test diff --git a/makina-compiler/test/xyz/colinholzman/makina/StateTest.kt b/makina-compiler/test/xyz/colinholzman/makina/StateTest.kt index 06a8147..f198667 100644 --- a/makina-compiler/test/xyz/colinholzman/makina/StateTest.kt +++ b/makina-compiler/test/xyz/colinholzman/makina/StateTest.kt @@ -87,6 +87,13 @@ internal class StateTest { assertEquals(emptySet(), bar_foo.subStates.toSet()) } + @Test + fun finalMustBeAtomic() { + assertThrows { + Parse.fileFromString("machine foo; state top { final state child { state err { } } }") + } + } + @Test fun testAssignParent() { val machine = Parse.fileFromString("machine foo; state bar {} state .bar.baz {}") @@ -104,21 +111,21 @@ internal class StateTest { } @Test - fun testIsLeafState() { + fun testIsAtomic() { val machine = Parse.fileFromString("machine foo; state bar {} state .bar.baz {}") val bar = machine.states.find { it.id == "bar" }!! val baz = machine.states.find { it.id == "baz" }!! - assertFalse(bar.isLeafState()) - assert(baz.isLeafState()) + assertFalse(bar.isAtomic()) + assert(baz.isAtomic()) } @Test - fun isLeafStateNested() { + fun isAtomicStateNested() { val machine = Parse.fileFromString("machine foo; state bar { state baz {} } state .bar.qux {}") val baz = machine.states.find { it.id == "baz" }!! val qux = machine.states.find { it.id == "qux" }!! - assert(baz.isLeafState()) - assert(qux.isLeafState()) + assert(baz.isAtomic()) + assert(qux.isAtomic()) } @Test