Skip to content

Commit

Permalink
Added State.checkForSubStatesInFinalState and renamed 'leaf' to 'atom…
Browse files Browse the repository at this point in the history
…ic'.
  • Loading branch information
clnhlzmn committed Mar 28, 2021
1 parent c1ec792 commit eb81731
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 36 deletions.
28 changes: 14 additions & 14 deletions makina-compiler/src/xyz/colinholzman/makina/CodeGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<Handler.Exit>()) {
Expand All @@ -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<Handler.Entry>()) {
println("\t\t\t${entry.action}(self, event);")
Expand All @@ -88,14 +88,14 @@ class CodeGenerator(val machine: Machine,
println("#include <stddef.h>")
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) {
Expand All @@ -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}")
}
Expand All @@ -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);")
Expand Down
17 changes: 12 additions & 5 deletions makina-compiler/src/xyz/colinholzman/makina/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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()
}

Expand Down Expand Up @@ -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()
Expand All @@ -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<State> {
return if (isLeafState()) {
return if (isAtomic()) {
emptyList()
} else {
val initialSubState = getInitialSubState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ data class StateConfiguration(val states: Set<State>) {
return orderedStates.reversed().flatMap { state -> state.handlers.filterIsInstance<Handler.Entry>() }
}

//Returns the leaf state for this configuration.
fun getLeafState(): State {
//Returns the atomic state for this configuration.
fun getAtomicState(): State {
return orderedStates.first()
}

Expand Down
4 changes: 2 additions & 2 deletions makina-compiler/src/xyz/colinholzman/makina/Transition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<State> {
Expand All @@ -12,7 +12,7 @@ data class Transition(val activeLeafState: State,

fun getExitSet(): List<State> {
val domain = getDomain()
return activeLeafState.getStateConfiguration().states
return activeAtomicState.getStateConfiguration().states
.filter { it.isDescendantOf(domain) }
.sortedBy { it.getDepth() }.reversed()
}
Expand Down
10 changes: 5 additions & 5 deletions makina-compiler/test/xyz/colinholzman/makina/ParseTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 13 additions & 6 deletions makina-compiler/test/xyz/colinholzman/makina/StateTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ internal class StateTest {
assertEquals(emptySet<State>(), bar_foo.subStates.toSet())
}

@Test
fun finalMustBeAtomic() {
assertThrows<RuntimeException> {
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 {}")
Expand All @@ -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
Expand Down

0 comments on commit eb81731

Please sign in to comment.