diff --git a/makina-compiler/src/xyz/colinholzman/makina/State.kt b/makina-compiler/src/xyz/colinholzman/makina/State.kt index 30468d8..58b5a82 100644 --- a/makina-compiler/src/xyz/colinholzman/makina/State.kt +++ b/makina-compiler/src/xyz/colinholzman/makina/State.kt @@ -127,6 +127,20 @@ class State(val id: String, } } + fun getInitialSubState(): State { + return subStates.find { it.initial } ?: subStates.first() + } + + //gets a list of the sub states to enter if this is the target of a transition + fun getDefaultEntrySet(): List { + return if (isLeafState()) { + emptyList() + } else { + val initialSubState = getInitialSubState() + listOf(initialSubState) + initialSubState.getDefaultEntrySet() + } + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/makina-compiler/src/xyz/colinholzman/makina/Transition.kt b/makina-compiler/src/xyz/colinholzman/makina/Transition.kt index bab382b..3ea61c4 100644 --- a/makina-compiler/src/xyz/colinholzman/makina/Transition.kt +++ b/makina-compiler/src/xyz/colinholzman/makina/Transition.kt @@ -3,6 +3,10 @@ package xyz.colinholzman.makina import xyz.colinholzman.makina.State.Companion.getLCCA data class Transition(val source: State, val target: State) { + init { + if (!source.isLeafState()) + throw RuntimeException("source must be a leaf state") + } fun getEntrySet(): List { return (listOf(target) + target.getProperAncestors(listOf(source, target).getLCCA())).reversed() } diff --git a/makina-compiler/test/xyz/colinholzman/makina/ExternalTransitionTest.kt b/makina-compiler/test/xyz/colinholzman/makina/ExternalTransitionTest.kt new file mode 100644 index 0000000..9218f8a --- /dev/null +++ b/makina-compiler/test/xyz/colinholzman/makina/ExternalTransitionTest.kt @@ -0,0 +1,46 @@ +package xyz.colinholzman.makina + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.io.File +import java.util.concurrent.TimeUnit + +class ExternalTransitionTest { + + val output = listOf( + "s1_entry", + "s1_s2_entry", + "s1_s2_e1_guard", + "s1_s2_exit", + "s1_exit", + "s1_entry", + "s1_s2_entry", + "s1_s2_e1_guard", + "s1_s2_exit", + "s1_exit", + "s1_entry", + "s1_s2_entry", + "s1_s2_e2_guard", + "s1_s2_exit", + "s1_s2_entry", + "s1_s2_e2_guard", + "s1_s2_exit", + "s1_s2_entry" + ) + @Test + fun externalTransition() { + val makeClean = ProcessBuilder() + .directory(File("./../test/external_transition")) + .command("make", "clean").start() + makeClean.waitFor(10, TimeUnit.SECONDS) + + val make = ProcessBuilder() + .directory(File("./../test/external_transition")) + .command("make").start() + make.waitFor(10, TimeUnit.SECONDS) + + File("./../test/external_transition/out/test_output.txt").reader().use { + Assertions.assertEquals(output, it.readLines()) + } + } +} \ No newline at end of file diff --git a/test/external_transition/.gitignore b/test/external_transition/.gitignore new file mode 100644 index 0000000..0f1f446 --- /dev/null +++ b/test/external_transition/.gitignore @@ -0,0 +1,2 @@ +out +.vscode \ No newline at end of file diff --git a/test/external_transition/makefile b/test/external_transition/makefile new file mode 100644 index 0000000..ad1ce61 --- /dev/null +++ b/test/external_transition/makefile @@ -0,0 +1,16 @@ +.PHONY: test +test: out/test + ./out/test + +out/test: test_driver.c out/test.c out/test.h out + gcc test_driver.c out/test.c -I "." -std=c99 -Wall -Wextra -Werror -o out/test + +out/test.c: + java -jar ../../makina-compiler/out/artifacts/makina_compiler_jar/makina-compiler.jar test_source.mkna -o out + +out: + mkdir out + +.PHONY: clean +clean: + rm -rf out diff --git a/test/external_transition/test_driver.c b/test/external_transition/test_driver.c new file mode 100644 index 0000000..36415c1 --- /dev/null +++ b/test/external_transition/test_driver.c @@ -0,0 +1,64 @@ + +#include +#include "out/test.h" + +FILE *output; + +int s1_s2_entry(struct test *self, struct test_event *event) { + (void)self; (void)event; + fprintf(output, "s1_s2_entry\n"); + return 0; +} + +static int guard = 0; + +int s1_s2_e1_guard(struct test *self, struct test_event *event) { + (void)self; (void)event; + fprintf(output, "s1_s2_e1_guard\n"); + return guard; +} + +int s1_s2_e2_guard(struct test *self, struct test_event *event) { + (void)self; (void)event; + fprintf(output, "s1_s2_e2_guard\n"); + return guard; +} + +int s1_s2_e1_action(struct test *self, struct test_event *event) { + (void)self; (void)event; + fprintf(output, "s1_s2_e1_action\n"); + return 0; +} + +int s1_s2_exit(struct test *self, struct test_event *event) { + (void)self; (void)event; + fprintf(output, "s1_s2_exit\n"); + return 0; +} + +int s1_entry(struct test *self, struct test_event *event) { + (void)self; (void)event; + fprintf(output, "s1_entry\n"); + return 0; +} + +int s1_exit(struct test *self, struct test_event *event) { + (void)self; (void)event; + fprintf(output, "s1_exit\n"); + return 0; +} + +int main (void) { + output = fopen("out/test_output.txt", "w+"); + struct test instance; + test_init(&instance); /*s1_entry, s1_s2_entry*/ + test_process_event(&instance, &(struct test_event){.id = test_event_e1}); /*s1_s2_e1_guard, s1_s2_exit, s1_exit, s1_entry, s1_s2_entry*/ + guard = 1; + test_process_event(&instance, &(struct test_event){.id = test_event_e1}); /*s1_s2_e1_guard, s1_s2_exit, s1_exit, s1_entry, s1_s2_entry*/ + guard = 0; + test_process_event(&instance, &(struct test_event){.id = test_event_e2}); /*s1_s2_e2_guard, s1_s2_exit, s1_s2_entry*/ + guard = 1; + test_process_event(&instance, &(struct test_event){.id = test_event_e2}); /*s1_s2_e2_guard, s1_s2_exit, s1_s2_entry*/ + fclose(output); + return 0; +} \ No newline at end of file diff --git a/test/external_transition/test_source.mkna b/test/external_transition/test_source.mkna new file mode 100644 index 0000000..03d42fd --- /dev/null +++ b/test/external_transition/test_source.mkna @@ -0,0 +1,14 @@ +machine test; + +state s1 { + on e1 -> s1; + on e2 -> s2; + entry s1_entry; + state s2 { + entry s1_s2_entry; + on e1 (s1_s2_e1_guard) -> s1; + on e2 (s1_s2_e2_guard) -> s2; + exit s1_s2_exit; + } + exit s1_exit; +}