From 7b428e5435f83a7e7db37094a9197afa2dd09bf5 Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Thu, 11 Feb 2016 10:40:02 +0100 Subject: Some tidying up of ANSI-C transformation --- README.md | 21 +- apps/uscxml-transform.cpp | 39 +- docs/COMPARISON.md | 89 +++ docs/GETTING_STARTED.md | 132 ++++ docs/NATIVE_CODE.md | 142 +++++ docs/untitled.txt | 132 ---- src/uscxml/transform/ChartToC.cpp | 203 +++++- src/uscxml/transform/ChartToC.h | 2 + src/uscxml/transform/ChartToVHDL.cpp | 112 +++- src/uscxml/transform/ChartToVHDL.h | 6 +- test/CMakeLists.txt | 25 +- test/src/test-c-inline.c | 40 ++ test/src/test-c-inline.c.scxml.c | 951 ++++++++++++++++++++++++++++ test/src/test-c-machine.cpp | 42 +- test/src/test-c-machine.machine.c | 1087 -------------------------------- test/src/test-c-machine.scxml.c | 1131 ++++++++++++++++++++++++++++++++++ 16 files changed, 2854 insertions(+), 1300 deletions(-) create mode 100644 docs/COMPARISON.md create mode 100644 docs/GETTING_STARTED.md create mode 100644 docs/NATIVE_CODE.md delete mode 100644 docs/untitled.txt create mode 100644 test/src/test-c-inline.c create mode 100644 test/src/test-c-inline.c.scxml.c delete mode 100644 test/src/test-c-machine.machine.c create mode 100644 test/src/test-c-machine.scxml.c diff --git a/README.md b/README.md index 3798be5..35ef803 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ frontend. It implements the following features: * C# bindings * PHP module for apache and cli interpreter (discontinued) * Interactive Debugger - * Accessible via a [web-frontend](http://htmlpreview.github.io/?https://github.com/tklab-tud/uscxml/blob/master/apps/uscxml-debugger.html) + * Accessible via a [web-frontend](http://htmlpreview.github.io/?apps/uscxml-debugger.html) * Complete with user-defined breakpoints, data model inspection and stepping ### Transformer @@ -65,21 +65,24 @@ made available via the uscxml-transform binary. It is a general tool for SCXML documents and currently implements the following features: * Transformations onto - * [Flattened SCXML documents](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/transform/ChartToFlatSCXML.cpp) in which only a single state is ever active + * [Flattened SCXML documents](src/uscxml/transform/ChartToFlatSCXML.cpp) in which only a single state is ever active * Resulting documents require slight adaptations to a compliant interpreter for donedata, the In predicate and invokers. * Semantic equivalence is shown via IRP tests. - * [ANSI C native code](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/transform/ChartToC.cpp) for easy embedding of SCXML state-charts in C and C++ programs + * [ANSI C native code](src/uscxml/transform/ChartToC.cpp) for easy embedding of SCXML state-charts in C and C++ programs * No custom I/O processors implemented in scaffolding just yet. - * [PROMELA programs](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/transform/ChartToPromela.cpp) for model-checking via linear temporal logic with the SPIN model-checker. + * To get started with transforming and embedding ANSI C code, read the [inline SCXML document](docs/NATIVE_CODE.md). + * [PROMELA programs](src/uscxml/transform/ChartToPromela.cpp) for model-checking via linear temporal logic with the SPIN model-checker. * Only defined for the promela and null datamodel. - * [Minimized SCXML documents](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/transform/ChartToMinimalSCXML.cpp) with dead states and executable content removed + * [Minimized SCXML documents](src/uscxml/transform/ChartToMinimalSCXML.cpp) with dead states and executable content removed * Minimization is performed dynamically by marking elements as visited and removing unvisited elements. * Annotations of the transitions exit set entry set, priority, conflicts, domain +Currently, we support a transformation from SCXML onto ANSI C. + ### Test Reports * We continuously run the [W3C IRP tests](http://www.w3.org/Voice/2013/scxml-irp/) for SCXML. -* Some tests are [excluded](https://github.com/tklab-tud/uscxml/blob/master/test/ctest/CTestCustom.ctest.in). +* Some tests are [excluded](test/ctest/CTestCustom.ctest.in). To run the tests yourself, you need to generate the build environment and pass -DBUILD_TESTS=ON via CMake: @@ -100,14 +103,14 @@ so maybe restrict yourself to some subset. uSCXML itself is distributed under the Simplified BSD license as in, do not sue us and do not misrepresent authorship. Please have a look at the licenses of the [libraries we depend -upon](https://github.com/tklab-tud/uscxml/blob/master/docs/BUILDING.md#build-dependencies) as well. +upon](docs/BUILDING.md#build-dependencies) as well. ## Performance We did some performance measurements in the scope of the C transformation. As you can see in the figure below, for most IRP tests we average to a duration of 5-20us per microstep on an early 2015 MacBook Pro 13" with 3.1GHz in the case -of [generated/compiled C](https://github.com/tklab-tud/uscxml/blob/master/test/src/test-c-machine.machine.c). For interpretation at runtime, we average at around 70-130us per +of [generated/compiled C](test/src/test-c-machine.machine.c). For interpretation at runtime, we average at around 70-130us per microstep. The generated C is rather optimized while the focus of the interpreter is more on correctness, feature completeness and extensibility. However, there are some lessons learned that are yet to be applied for the @@ -116,7 +119,7 @@ interpreter. For the tests, we took the -[highest precision timer](https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/concurrency/Timer.cpp) +[highest precision timer](src/uscxml/concurrency/Timer.cpp) we could attain and measured how long the execution of a given SCXML IRP test took while subtracting initialization, tear-down and the time spent in the data-model's routines. Time is averaged over 1.000 iterations. diff --git a/apps/uscxml-transform.cpp b/apps/uscxml-transform.cpp index 857a160..a5aa9ec 100644 --- a/apps/uscxml-transform.cpp +++ b/apps/uscxml-transform.cpp @@ -275,11 +275,44 @@ int main(int argc, char** argv) { } else { interpreter = Interpreter::fromURL(inputFile); } - if (!interpreter) { - LOG(ERROR) << "Cannot create interpreter from " << inputFile; - exit(EXIT_FAILURE); + } catch (Event e) { + // we will reattempt below, not yet a fatal error + } catch (const std::exception &e) { + std::cout << e.what() << std::endl; + } + + if (!interpreter) { + URL tmp(inputFile); + tmp.toAbsoluteCwd(); + std::stringstream contentSS; + contentSS << tmp; + + std::string inlineBeginMarker = "INLINE SCXML BEGIN\n"; + std::string inlineEndMarker = "\nINLINE SCXML END"; + + size_t inlineSCXMLBegin = contentSS.str().find(inlineBeginMarker); + if (inlineSCXMLBegin != std::string::npos) { + inlineSCXMLBegin += inlineBeginMarker.size(); + size_t inlineSCXMLEnd = contentSS.str().find(inlineEndMarker); + std::string inlineSCXMLContent = contentSS.str().substr(inlineSCXMLBegin, inlineSCXMLEnd - inlineSCXMLBegin); + try { + interpreter = Interpreter::fromXML(inlineSCXMLContent, tmp); + } catch (Event e) { + std::cout << e << std::endl; + } catch (const std::exception &e) { + std::cout << e.what() << std::endl; + } } + } + + if (!interpreter) { + LOG(ERROR) << "Cannot create interpreter from " << inputFile; + exit(EXIT_FAILURE); + + } + + try { if (verbose) { std::list issues = interpreter.validate(); for (std::list::iterator issueIter = issues.begin(); issueIter != issues.end(); issueIter++) { diff --git a/docs/COMPARISON.md b/docs/COMPARISON.md new file mode 100644 index 0000000..94e8d3b --- /dev/null +++ b/docs/COMPARISON.md @@ -0,0 +1,89 @@ +# Comparison of SCXML Interpreters + +In this document, I will make an attempt to compare and measure the performance +of the various, freely available SCXML interpreters. If you are the author of +one of these interpreters and feel that I misrepresented your work, please do +not hesitate to contact me and I will reevaluate / correct any factual errors. + +This selection is based on the list of available implementations on the [SCXML +wiki](https://en.wikipedia.org/wiki/SCXML) page, plus a few interpreters I know +from the [W3C mailing list](https://lists.w3.org/Archives/Public/www-voice/) +and the [SCXML workshops](http://scxmlworkshop.de). I will list all +interpreters known to me, but only benchmark those that pass the W3C IRP tests +as is required for an actual SCXML interpreter. + +# Methodology + +The benchmarks were performed with a [huge +SCXML](https://github.com/tklab-tud/uscxml/tree/master/test/w3c/compound) file +containing all of the automatic W3C IRP tests that do not rely on timeouts to +pass. + +## scxmlcc + +| Platforms | Tested on | Tested | Website | +|---------------|---------------------|------------------|---------------| +| Linux only | Debian Jessie 64Bit | **No**: Subset of SCXML implemented is insufficient for SCXML IRP tests. | [http://scxmlcc.org](http://scxmlcc.org) | + +### How to build + + $ sudo apt-get install build-essential libboost-all-dev autorevision + $ git clone https://github.com/jp-embedded/scxmlcc.git + $ cd scxmlcc/src + $ make + +### Evaluation + + $ ./scxmlcc -i ./test-ecma-all.scxml |sort |uniq + warning: event asteriks not currently supported + warning: event tokens not currently supported + warning: parallel support is not fully implemented/tested + warning: unknown action type 'script' + warning: unknown item 'assign' in or + warning: unknown item 'assign' in + warning: unknown item 'cancel' in + warning: unknown item 'datamodel' in + warning: unknown item 'donedata' in + warning: unknown item 'foreach' in or + warning: unknown item 'if' in or + warning: unknown item 'invoke' in + warning: unknown item 'send' in or + warning: unknown item 'send' in + +The subset of SCXML implemented in scxmlcc is insufficient to run the +performance / compliance benchmarks. + +## SCXML Lab + +| Platforms | Tested on | Tested | Website | +|---------------|---------------------|-----------|---------------| +| HTML5 | Safari 9.0.3 | **No**: Subset of SCXML implemented is insufficient for SCXML IRP tests. | [http://www.ling.gu.se/~lager/Labs/SCXML-Lab/](http://www.ling.gu.se/~lager/Labs/SCXML-Lab/) | + +I pasted the file above into their web-frontend and got a validation-error + + Unexpected attribute 'datamodel' in . Hint: + Valid attributes are: 'id', 'initialstate', 'version', 'xmlns'. + +The datamodel is indeed not required for a compliant interpreter, but +we will not be able to pass any meaningful subset of the SCXML IRP tests +without support for a datamodel. + +## Legian + +| Platforms | Tested on | Tested | Website | +|---------------|---------------------|-----------|---------------| +| Java | Java(TM) SE (build 1.8.0_45-b14) | **No**: Subset of SCXML implemented is insufficient for SCXML IRP tests. | [https://github.com/pelatimtt/Legian](https://github.com/pelatimtt/Legian/) | + +Does not claim W3C compliance and will, therefore, not pass the compound IRP tests. + +## Qt SCXML Engine + +| Platforms | Tested on | Tested | Website | +|---------------|---------------------|-----------|---------------| +| Any | Mac OSX 10.11.3 with Macports 2.3.4 | **No**: Subset of SCXML implemented is insufficient for SCXML IRP tests. | [https://qt.gitorious.org/qt-labs/scxml](https://qt.gitorious.org/qt-labs/scxml/) | + +[No +support](https://qt.gitorious.org/qt-labs/scxml?p=qt-labs:scxml.git;a=blob_plain;f=doc/scxml.qdoc;hb=HEAD) +for <donedata>, <finalize>. Furthermore the +profile (now datamodel with finalized SCXML recommendation) +attribute is ignored. diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md new file mode 100644 index 0000000..8c585bf --- /dev/null +++ b/docs/GETTING_STARTED.md @@ -0,0 +1,132 @@ +# Getting Started + +In order to use the interpreter, you need to #include "uscxml/Interpreter.h" and instantiate +objects of uscxml::Interpreter. + +### Non-Blocking Interpretation with SCXML from URL + Interpreter scxml = Interpreter::fromURL("http://www.example.com/fancy.scxml"); + scxml.start(); // non-blocking in own thread + +There are some cases, i.e. with graphical invokers, where the main thread is required in order +to react to UI events. You will have to deligate control flow from the main thread into the interpreter +every now and then: + + interpreter.runOnMainThread(25); + +This will perform a single iteration on the invoked components with a maximum of 25 frames per seconds +or return immediately. You will have to call this method every now and then if you are using e.g. the +scenegraph invoker. + +Note: Running the interpreter in its own thread via start is not exposed into the +language bindings. Just use the threading concepts native to your language to call step or +interpret as outlined below. + +### Blocking Interpretation with inline SCXML + Interpreter scxml = Interpreter::fromXML(""); + scxml.interpret(); // blocking + +When using blocking interpretation, it is assumed that it is running on the main thread and +it will call runOnMainThread between stable configurations. + +### Interleaved Interpretation with inline SCXML + Interpreter scxml = Interpreter::fromXML(""); + InterpreterState state; + do { + state = interpreter.step(ms); + } while(state != InterpreterState::USCXML_FINISHED) + +Using step, you can run a single macrostep of the interpreter and interleave +interpretation with the rest of your code. The step function will take an optional integer as +the time in milliseconds it will block and wait if no more events are available, default is to block +indefinitely until an event arrives or the interpreter finished. + +### Callbacks for an Interpreter + +You can register an InterpreterMonitor prior to start in order to receive +control-flow upon various events in the Interpreter. + + class StatusMonitor : public uscxml::InterpreterMonitor { + void onStableConfiguration(...) + void beforeCompletion(...) + void afterCompletion(...) + void beforeMicroStep(...) + void beforeTakingTransitions(...) + void beforeEnteringStates(...) + void afterEnteringStates(...) + void beforeExitingStates(...) + void afterExitingStates(...) + }; + + StatusMonitor statMon; + Interpreter scxml = Interpreter::fromXML(""); + scxml.addMonitor(&statMon); + scxml.start(); + +This will cause the interpreter to invoke the callbacks from the monitor whenever the corresponding +internal phase is reached. + +## Advanced Topics + +### Embedding uSCXML + +There are bindings for [Java](https://github.com/tklab-tud/uscxml/tree/master/embedding/java) and +[C#](https://github.com/tklab-tud/uscxml/tree/master/embedding/csharp) with some examples in the +embedding directory. The bindings consist of two parts each + +1. The C++ uscxml interpreter compiled as a loadable module for the target language and +2. A target language specific module (uscxml.jar / uscxmlCSharp.dll) with the wrapper classes. + +The first one is loaded by the target language (System.loadLibrary / SetDLLDirectory) while the second is to be +included in your actual project. Have a look at the examples in embedding and adapt the paths to reflect +your setup. See the [build instructions](https://github.com/tklab-tud/uscxml/blob/master/docs/BUILDING.md) for +details on how to build these. + +### Extending uSCXML + +The uSCXML interpreter can be extended by introducing new + +1. Data models as embedded scripting languages (e.g. ECMAScript, Prolog and XPath) +2. Invokers to represent external components that deliver and accept events (e.g. iCal, SceneGraph, DirectoryMonitor) +3. I/O-Processors to provide communication with external systems (e.g. BasicHTTP, SCXML). +4. Elements for Executable Content (e.g. <respond>, <fetch>, <postpone>). +5. Data model extionsions to establish callbacks from the data model into the host language. + +The basic approach to extend the interpreter is the same in all cases: + +1. Write a class inheriting the abstract base class (e.g. DataModelImpl, InvokerImpl, IOProcessorImpl, ExecutableContentImpl). +2. Instantiate your class and register it as a prototype at the Factory via one of its static register* methods. + 1. You can register at the global Factory Singleton via Factory::register*(prototypeInstance) + 2. Or provide a new Factory instance to selected interpreters as an in-between. +3. Write an interpreter using your new functionality. + +Note: Within the language bindings, you will have to inherit the base classes without the Impl +suffix. Have a look at the examples in embedding for examples. + +#### Ad-hoc Extensions + +Sometimes, it is more suited to provide an interpreter with an already instantiated extension (e.g. an +IOProcessor with an existing connection). In this case, it is somewhat awkward to register a prototype and +have all initialization in its create(Interpreter interpreter) method. While you can still dispatch +over the interpreter instance and access information from some global Interpreter->Data map, there is a +more straight-forward approach, e.g. in Java: + + Interpreter interpreter = Intepreter.fromURI(uri); + AdhocIOProcessor ioProc = new AdhocIOProcessor(Whatever youLike); + ioProc.setParameter1(something); + interpreter.addIOProcessor(ioProc); + +This will cause the interpreter to use the given instance for all send requests targeting one of the types +returned by ioProc.getNames() and not instantiate an instance via the factory. The instance can +deliver events into the interpreter via returnEvent(Event e, boolean toInternalQueue = false). The same +approach can be used for invokers: + + Interpreter interpreter = Intepreter.fromURI(uri); + TestAdhocInvoker invoker1 = new TestAdhocInvoker(Whatever youLike); + invoker1.setParameter1(something); + interpreter.setInvoker("invokeId", invoker1); + +This will cause the interpreter to use the given instance for a given invokeId and not instantiate via +the factory. Similarly, data models can be registered via interpreter.setDataModel(DataModel dm). + +Note: Providing ad-hoc extensions is only supported before the interpreter is started. If you change +instances with a running interpreter, the behavior is undefined. \ No newline at end of file diff --git a/docs/NATIVE_CODE.md b/docs/NATIVE_CODE.md new file mode 100644 index 0000000..91af599 --- /dev/null +++ b/docs/NATIVE_CODE.md @@ -0,0 +1,142 @@ +# Generating Native Code + +You can use the uscxml-transform tool to create native code from an +SCXML document. In this case, you will not use an instance of the +uscxml::Interpreter class, but instantiate an SCXML context from a +native description of the state-chart. + +## Embedding ANSI C + +To embed the control flow described within an SCXML document in most variances +of the C language, we provide a transformation onto ANSI C (C89) as a proper +subset of virtually any more modern C/C++ dialect. There are two general +approaches to achieve this. In any case, you need to transform your SCXML +state-chart onto ANSI C by invoking uscxml-transform: + + $ uscxml-transform -tc -i INPUT_FILE -o OUTPUT_FILE + +This transformation will create a single file that you can compile and link or +include directly. I advice to include the file into another compilation unit +and not to compile it directly, as it allows for more convenience and is +generally a more flexible approach. The file will contain: + +1. A set of pre-processor **macros** for convenience and definitions, all starting +with an USCXML_ prefix. Of special note are the following macros that +allow you to influence important characteristics of you state-machine. + + * **USCXML_NR_STATES_TYPE** / **USCXML_NR_TRANS_TYPE**: + + The type for unsigned integers that is of sufficient size to contain + the number of states / transitions of your largest state machine. The + transformation will automatically choose one of the uint*_t + types, though a popular extension, they are only available in C99 + (stdint.h). Also, if you like to reuse parts of the file (e.g. + the types below) in another compilation unit, you might need to + predefine them explicitly to a sufficient size. + + * **USCXML_MAX_NR_STATES_BYTES** / **USCXML_MAX_NR_TRANS_BYTES**: + + The minimial size for the bit-arrays as char[N] containing the + states and transitions in the various types and on the stack during a + microstep. It has to be larger or equal to the smallest positive + integer that, when multiplied by 8 is larger or equal to the number of + states and transitions repsectively. + + In other words, make sure that + states[USCXML_MAX_NR_STATES_BYTES] has room for one bit per + state and transitions[USCXML_MAX_NR_TRANS_BYTES] for one bit + per transition. + + * There are some other macros defined, but they are rather for + micro-optimizations. Have a look at a generated file. + +2. All compound data **types** (struct) to encode an SCXML state-machine. +These will refer to the macros above to require memory for a state-chart's +states and transitions, so make sure that the macros are set if you +conditionally include parts of the generated file and double-check that the +type definitions are the same in every compilation unit if you want to access +state-machines from other units (i.e. same value for macros above!). + +3. The actual **symbols** for one or many state-machines from the input SCXML +file. Their identifiers are all prefixed by an identifier derived from the +content of a given SCXML document. As such, if you transform any given SCXML +document twice, you might end up with duplicate symbols, yet again, the +state-chart's will be functionally identical as they contained the same content. + + In order for not having to guess the prefix when referring to any machine + in application code, the tranformation will define three additional macros: + + #ifndef USCXML_MACHINE + # define USCXML_MACHINE _uscxml_BC220711_machine + #endif + #define USCXML_MACHINE_0 _uscxml_BC220711_machine + #define USCXML_MACHINE_NAME_HERE _uscxml_BC220711_machine + + The first macro is useful if you only transformed a single SCXML + state-chart as it will always refer to the very first state-chart + encountered. If there is more than one SCXML state-chart within a document + (i.e. an invocation of nested machines) you can also refer to them by index + or their eventual name. + +4. Some **helper functions**, most notably bit operations for arbitrary length +bit arrays. + +5. The **micro-step function** uscxml_step, which will perform a +micro-step on a given context and delegate control flow accordingly. + +These elements are always contained and you can, selectively, disable their +inclusion by pre-defining respective macros (have a look at a generated source +file). + +Now in order to actually use an SCXML document to manage the control flow among +a set of functions, there are two general approaches. Both use the generated +ANSI C source code above, but require more or less resources at runtime as +detailled below. + +### Fully Compliant + +An SCXML interpreter does more than to perform a series of microsteps for event +over a set of states and transitions and there are quite a few responsibilities +not implemented in the generated ANSI C code: + +1. **Event Queues**: + + A compliant interpreter is required to maintain two event queues, an + internal and an external one. With the generated ANSI C source, these are + integrated via four callbacks and will need to be implemented in + user-supplied code: + + 1. **uscxml_ctx.dequeue_internal**: This callback is invoked + whenever the interpreter needs an event from the internal event queue. It + is passed an instance of a uscxml_ctx structure and is supposed to + return an opaque pointer to an event. If the internal queue is empty, + NULL is to be returned. + + 2. **uscxml_ctx.dequeue_external**: This callback is functionally + equivalent to uscxml_ctx.dequeue_internal but invoked, when an + external event is to be dequeued. + + 3. **uscxml_ctx.exec_content_send**: Whenever there is an + <send> element encountered in executable content, the generated + ANSI C code will invoke this callback with a context and an + uscxml_elem_send instance and the user code registered at the + callback is expected to handle the send request as per recommendation. + + 4. **uscxml_ctx.exec_content_raise**: This callback is invoked for + any <raise> element processed as part of executable content and + is expected to deliver an event to the internal event queue. + +2. **Transition Matching / Enabling** + + An event will match and enable a set of transitions. The generated ANSI C + source will already make sure that only valid sets of transitions can be + selected to constitute the optimal transition set for a microstep, but + user-supplied code will have to decide whether a transition is matched and + enabled. This is done via the **uscxml_ctx.is_enabled** callback. + It receives a context, a uscxml_transition structure and the + opaque event pointer and will have to return 0 for when the + transition is not matched and enabled by the given event and 1 if + it is. + + +### Light-Weight diff --git a/docs/untitled.txt b/docs/untitled.txt deleted file mode 100644 index 8c585bf..0000000 --- a/docs/untitled.txt +++ /dev/null @@ -1,132 +0,0 @@ -# Getting Started - -In order to use the interpreter, you need to #include "uscxml/Interpreter.h" and instantiate -objects of uscxml::Interpreter. - -### Non-Blocking Interpretation with SCXML from URL - Interpreter scxml = Interpreter::fromURL("http://www.example.com/fancy.scxml"); - scxml.start(); // non-blocking in own thread - -There are some cases, i.e. with graphical invokers, where the main thread is required in order -to react to UI events. You will have to deligate control flow from the main thread into the interpreter -every now and then: - - interpreter.runOnMainThread(25); - -This will perform a single iteration on the invoked components with a maximum of 25 frames per seconds -or return immediately. You will have to call this method every now and then if you are using e.g. the -scenegraph invoker. - -Note: Running the interpreter in its own thread via start is not exposed into the -language bindings. Just use the threading concepts native to your language to call step or -interpret as outlined below. - -### Blocking Interpretation with inline SCXML - Interpreter scxml = Interpreter::fromXML(""); - scxml.interpret(); // blocking - -When using blocking interpretation, it is assumed that it is running on the main thread and -it will call runOnMainThread between stable configurations. - -### Interleaved Interpretation with inline SCXML - Interpreter scxml = Interpreter::fromXML(""); - InterpreterState state; - do { - state = interpreter.step(ms); - } while(state != InterpreterState::USCXML_FINISHED) - -Using step, you can run a single macrostep of the interpreter and interleave -interpretation with the rest of your code. The step function will take an optional integer as -the time in milliseconds it will block and wait if no more events are available, default is to block -indefinitely until an event arrives or the interpreter finished. - -### Callbacks for an Interpreter - -You can register an InterpreterMonitor prior to start in order to receive -control-flow upon various events in the Interpreter. - - class StatusMonitor : public uscxml::InterpreterMonitor { - void onStableConfiguration(...) - void beforeCompletion(...) - void afterCompletion(...) - void beforeMicroStep(...) - void beforeTakingTransitions(...) - void beforeEnteringStates(...) - void afterEnteringStates(...) - void beforeExitingStates(...) - void afterExitingStates(...) - }; - - StatusMonitor statMon; - Interpreter scxml = Interpreter::fromXML(""); - scxml.addMonitor(&statMon); - scxml.start(); - -This will cause the interpreter to invoke the callbacks from the monitor whenever the corresponding -internal phase is reached. - -## Advanced Topics - -### Embedding uSCXML - -There are bindings for [Java](https://github.com/tklab-tud/uscxml/tree/master/embedding/java) and -[C#](https://github.com/tklab-tud/uscxml/tree/master/embedding/csharp) with some examples in the -embedding directory. The bindings consist of two parts each - -1. The C++ uscxml interpreter compiled as a loadable module for the target language and -2. A target language specific module (uscxml.jar / uscxmlCSharp.dll) with the wrapper classes. - -The first one is loaded by the target language (System.loadLibrary / SetDLLDirectory) while the second is to be -included in your actual project. Have a look at the examples in embedding and adapt the paths to reflect -your setup. See the [build instructions](https://github.com/tklab-tud/uscxml/blob/master/docs/BUILDING.md) for -details on how to build these. - -### Extending uSCXML - -The uSCXML interpreter can be extended by introducing new - -1. Data models as embedded scripting languages (e.g. ECMAScript, Prolog and XPath) -2. Invokers to represent external components that deliver and accept events (e.g. iCal, SceneGraph, DirectoryMonitor) -3. I/O-Processors to provide communication with external systems (e.g. BasicHTTP, SCXML). -4. Elements for Executable Content (e.g. <respond>, <fetch>, <postpone>). -5. Data model extionsions to establish callbacks from the data model into the host language. - -The basic approach to extend the interpreter is the same in all cases: - -1. Write a class inheriting the abstract base class (e.g. DataModelImpl, InvokerImpl, IOProcessorImpl, ExecutableContentImpl). -2. Instantiate your class and register it as a prototype at the Factory via one of its static register* methods. - 1. You can register at the global Factory Singleton via Factory::register*(prototypeInstance) - 2. Or provide a new Factory instance to selected interpreters as an in-between. -3. Write an interpreter using your new functionality. - -Note: Within the language bindings, you will have to inherit the base classes without the Impl -suffix. Have a look at the examples in embedding for examples. - -#### Ad-hoc Extensions - -Sometimes, it is more suited to provide an interpreter with an already instantiated extension (e.g. an -IOProcessor with an existing connection). In this case, it is somewhat awkward to register a prototype and -have all initialization in its create(Interpreter interpreter) method. While you can still dispatch -over the interpreter instance and access information from some global Interpreter->Data map, there is a -more straight-forward approach, e.g. in Java: - - Interpreter interpreter = Intepreter.fromURI(uri); - AdhocIOProcessor ioProc = new AdhocIOProcessor(Whatever youLike); - ioProc.setParameter1(something); - interpreter.addIOProcessor(ioProc); - -This will cause the interpreter to use the given instance for all send requests targeting one of the types -returned by ioProc.getNames() and not instantiate an instance via the factory. The instance can -deliver events into the interpreter via returnEvent(Event e, boolean toInternalQueue = false). The same -approach can be used for invokers: - - Interpreter interpreter = Intepreter.fromURI(uri); - TestAdhocInvoker invoker1 = new TestAdhocInvoker(Whatever youLike); - invoker1.setParameter1(something); - interpreter.setInvoker("invokeId", invoker1); - -This will cause the interpreter to use the given instance for a given invokeId and not instantiate via -the factory. Similarly, data models can be registered via interpreter.setDataModel(DataModel dm). - -Note: Providing ad-hoc extensions is only supported before the interpreter is started. If you change -instances with a running interpreter, the behavior is undefined. \ No newline at end of file diff --git a/src/uscxml/transform/ChartToC.cpp b/src/uscxml/transform/ChartToC.cpp index f9eba98..c6be393 100644 --- a/src/uscxml/transform/ChartToC.cpp +++ b/src/uscxml/transform/ChartToC.cpp @@ -56,6 +56,15 @@ ChartToC::ChartToC(const Interpreter& other) : TransformerImpl(), _topMostMachin prepare(); findNestedMachines(); + if (_extensions.find("prefix") != _extensions.end()) { + _prefixes = new std::list(); + std::pair::iterator, + std::multimap::iterator> keyRange = _extensions.equal_range("prefix"); + for (std::multimap::iterator iter = keyRange.first; iter != keyRange.second; ++iter) { + _prefixes->push_back(iter->second); + } + } + } void ChartToC::setHistoryCompletion() { @@ -423,8 +432,8 @@ void ChartToC::writeTo(std::ostream& stream) { (*machIter)->writeExecContent(stream); (*machIter)->writeStates(stream); (*machIter)->writeTransitions(stream); + (*machIter)->writeMachineInfo(stream); } - writeMachineInfo(stream); writeHelpers(stream); writeFSM(stream); @@ -434,7 +443,9 @@ void ChartToC::writeTo(std::ostream& stream) { void ChartToC::writeForwardDeclarations(std::ostream& stream) { stream << "/* forward declare machines to allow references */" << std::endl; - stream << "extern const uscxml_machine uscxml_machines[" << toStr(_allMachines.size() + 1) << "];" << std::endl; + for (std::list::iterator machIter = _allMachines.begin(); machIter != _allMachines.end(); machIter++) { + stream << "extern const uscxml_machine " << (*machIter)->_prefix << "_machine;" << std::endl; + } stream << std::endl; } @@ -488,13 +499,15 @@ void ChartToC::findNestedMachines() { } void ChartToC::writeIncludes(std::ostream& stream) { - stream << "#include /* explicit types */" << std::endl; + stream << "#ifndef USCXML_NO_STDTYPES_H" << std::endl; + stream << "# include /* explicit types */" << std::endl; + stream << "#endif" << std::endl; stream << "#include /* NULL */" << std::endl; stream << std::endl; } void ChartToC::writeMacros(std::ostream& stream) { - stream << "#ifndef USCXML_GEN_C_MACROS" << std::endl; + stream << "#ifndef USCXML_NO_GEN_C_MACROS" << std::endl; stream << std::endl; stream << "/**" << std::endl; stream << " * All macros used for the scxml types and functions" << std::endl; @@ -651,7 +664,7 @@ void ChartToC::writeMacros(std::ostream& stream) { stream << "#define USCXML_ELEM_PARAM_IS_SET(param) (param->name != NULL)" << std::endl; stream << "#define USCXML_MACHINE_IS_SET(machine) (machine->nr_states > 0)" << std::endl; stream << std::endl; - stream << "#define USCXML_GEN_C_MACROS" << std::endl; + stream << "#define USCXML_NO_GEN_C_MACROS" << std::endl; stream << "#endif" << std::endl; stream << std::endl; } @@ -659,11 +672,11 @@ void ChartToC::writeMacros(std::ostream& stream) { void ChartToC::writeTypes(std::ostream& stream) { stream << std::endl; - stream << "#ifndef USCXML_GEN_C_TYPES" << std::endl; + stream << "#ifndef USCXML_NO_GEN_C_TYPES" << std::endl; stream << std::endl; stream << "/**" << std::endl; stream << " * All types required to represent an SCXML state chart." << std::endl; - stream << " * Just predefine the USCXML_GEN_C_TYPES macro if you do not need them." << std::endl; + stream << " * Just predefine the USCXML_NO_GEN_C_TYPES macro if you do not need them." << std::endl; stream << " */" << std::endl; stream << std::endl; @@ -876,7 +889,7 @@ void ChartToC::writeTypes(std::ostream& stream) { stream << " invoke_t invoke;" << std::endl; stream << "};" << std::endl; stream << std::endl; - stream << "#define USCXML_GEN_C_TYPES" << std::endl; + stream << "#define USCXML_NO_GEN_C_TYPES" << std::endl; stream << "#endif" << std::endl; stream << std::endl; } @@ -917,7 +930,7 @@ void ChartToC::writeHelpers(std::ostream& stream) { stream << "#endif" << std::endl; stream << std::endl; - stream << "#ifndef USCXML_BIT_OPERATIONS" << std::endl; + stream << "#ifndef USCXML_NO_BIT_OPERATIONS" << std::endl; stream << "/**" << std::endl; stream << " * Return true if there is a common bit in a and b." << std::endl; stream << " */" << std::endl; @@ -993,15 +1006,22 @@ void ChartToC::writeHelpers(std::ostream& stream) { stream << " };" << std::endl; stream << "}" << std::endl; stream << std::endl; - stream << "#define USCXML_BIT_OPERATIONS" << std::endl; + stream << "#define USCXML_NO_BIT_OPERATIONS" << std::endl; stream << "#endif" << std::endl; stream << std::endl; } void ChartToC::writeExecContentFinalize(std::ostream& stream) { + // needs to be written prior to invocation elem info NodeSet finalizes = DOMUtils::inDocumentOrder(_nsInfo.xmlNSPrefix + "finalize", _scxml); + + if (finalizes.size() > 0) { + stream << "#ifndef USCXML_NO_EXEC_CONTENT" << std::endl; + stream << std::endl; + } + for (size_t i = 0; i < finalizes.size(); i++) { Element finalize(finalizes[i]); NodeSet execContent = filterChildType(Node_base::ELEMENT_NODE, finalize); @@ -1018,9 +1038,16 @@ void ChartToC::writeExecContentFinalize(std::ostream& stream) { } } + if (finalizes.size() > 0) { + stream << "#endif" << std::endl; + stream << std::endl; + } } void ChartToC::writeExecContent(std::ostream& stream) { + stream << "#ifndef USCXML_NO_EXEC_CONTENT" << std::endl; + stream << std::endl; + for (size_t i = 0; i < _states.size(); i++) { Element state(_states[i]); @@ -1118,6 +1145,9 @@ void ChartToC::writeExecContent(std::ostream& stream) { } } + stream << "#endif" << std::endl; + stream << std::endl; + } void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node& node, int indent) { @@ -1126,8 +1156,12 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node 0) { - std::string escaped = escape(node.getNodeValue()); - stream << escaped; + if (HAS_ATTR(_scxml, "datamodel") && ATTR(_scxml, "datamodel") == "native") { + stream << node.getNodeValue(); + } else { + std::string escaped = escape(node.getNodeValue()); + stream << escaped; + } } return; } @@ -1291,6 +1325,10 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _scxml, true); if (invokes.size() > 0) { _hasElement.insert("invoke"); @@ -1321,6 +1359,15 @@ void ChartToC::writeElementInfoInvocation(std::ostream& stream) { stream << " /* machine */ "; if (HAS_ATTR(invoke, "md5sum")) { +#if 1 + size_t machIdx = 0; + for (std::list::iterator machIter = _allMachines.begin(); machIter != _allMachines.end(); machIter++, machIdx++) { + if ((*machIter)->_md5 == ATTR(invoke, "md5sum")) { + stream << "&" << (*machIter)->_prefix << "_machine"; + break; + } + } +#else size_t machIdx = 0; for (std::list::iterator machIter = _allMachines.begin(); machIter != _allMachines.end(); machIter++, machIdx++) { if ((*machIter)->_md5 == ATTR(invoke, "md5sum")) { @@ -1328,6 +1375,7 @@ void ChartToC::writeElementInfoInvocation(std::ostream& stream) { break; } } +#endif } else { stream << "NULL"; } @@ -1410,9 +1458,15 @@ void ChartToC::writeElementInfoInvocation(std::ostream& stream) { stream << std::endl; } + stream << "#endif" << std::endl; + stream << std::endl; + } void ChartToC::writeElementInfo(std::ostream& stream) { + stream << "#ifndef USCXML_NO_ELEM_INFO" << std::endl; + stream << std::endl; + NodeSet foreachs = DOMUtils::inDocumentOrder(_nsInfo.xmlNSPrefix + "foreach", _scxml); if (foreachs.size() > 0) { _hasElement.insert("foreach"); @@ -1624,9 +1678,76 @@ void ChartToC::writeElementInfo(std::ostream& stream) { stream << "};" << std::endl; stream << std::endl; + stream << "#endif" << std::endl; + stream << std::endl; + } void ChartToC::writeMachineInfo(std::ostream& stream) { + + stream << "#ifndef USCXML_NO_ELEM_INFO" << std::endl; + stream << std::endl; + +#if 1 + size_t currIndex = 0; + char* currIndexStr = getenv("USCXML_CURRENT_MACHINE_INDEX"); + if (currIndexStr != NULL) { + currIndex = strTo(currIndexStr); + } + + stream << "#ifndef USCXML_MACHINE" << std::endl; + stream << "# define USCXML_MACHINE " << _prefix << "_machine" << std::endl; + stream << "#endif" << std::endl; + + stream << "#define USCXML_MACHINE_" << toStr(currIndex) << " " << _prefix << "_machine" << std::endl; + + if (_name.size() > 0) { + std::string macroName = boost::to_upper_copy(escape(_name)); + boost::replace_all(macroName, "-", "_"); + stream << "#define USCXML_MACHINE_" << macroName << " " << _prefix << "_machine" << std::endl; + } + stream << std::endl; + + currIndex++; + setenv("USCXML_CURRENT_MACHINE_INDEX", toStr(currIndex).c_str(), 1); + + stream << "const uscxml_machine " << _prefix << "_machine = {" << std::endl; + stream << " /* flags */ 0," << std::endl; + stream << " /* nr_states */ " << _states.size() << "," << std::endl; + stream << " /* nr_transitions */ " << _transitions.size() << "," << std::endl; + stream << " /* name */ \"" << escape(_name) << "\"," << std::endl; + stream << " /* datamodel */ \"" << (HAS_ATTR(_scxml, "datamodel") ? ATTR(_scxml, "datamodel") : "null") << "\"," << std::endl; + stream << " /* uuid */ \"" << _md5 << "\"," << std::endl; + stream << " /* states */ " << "&" << _prefix << "_states[0], " << std::endl; + stream << " /* transitions */ " << "&" << _prefix << "_transitions[0], " << std::endl; + stream << " /* parent */ "; + if (_parentMachine != NULL) { + size_t parentIndex = 0; + for (std::list::iterator parentIter = _topMostMachine->_allMachines.begin(); + parentIter != _topMostMachine->_allMachines.end(); + parentIter++, parentIndex++) { + if (*parentIter == _parentMachine) { + stream << "&" << (*parentIter)->_prefix << "_machine"; + } + } + } else { + stream << "NULL"; + } + stream << "," << std::endl; + + stream << " /* donedata */ " << "&" << _prefix << "_elem_donedatas[0], " << std::endl; + stream << " /* script */ "; + if (filterChildElements(_nsInfo.xmlNSPrefix + "script", _scxml).size() > 0) { + stream << _prefix << "_global_script" << std::endl; + } else { + stream << "NULL"; + } + stream << std::endl; + + stream << "};" << std::endl; + stream << std::endl; + +#else if (_topMostMachine != NULL) return; @@ -1670,9 +1791,17 @@ void ChartToC::writeMachineInfo(std::ostream& stream) { stream << " {0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }" << std::endl; stream << "};" << std::endl; stream << std::endl; +#endif + + stream << "#endif" << std::endl; + stream << std::endl; + } void ChartToC::writeStates(std::ostream& stream) { + stream << "#ifndef USCXML_NO_ELEM_INFO" << std::endl; + stream << std::endl; + stream << "static const uscxml_state " << _prefix << "_states[" << toStr(_states.size()) << "] = {" << std::endl; for (size_t i = 0; i < _states.size(); i++) { Element state(_states[i]); @@ -1754,11 +1883,18 @@ void ChartToC::writeStates(std::ostream& stream) { } stream << "};" << std::endl; stream << std::endl; + + stream << "#endif" << std::endl; + stream << std::endl; + } void ChartToC::writeTransitions(std::ostream& stream) { + stream << "#ifndef USCXML_NO_ELEM_INFO" << std::endl; + stream << std::endl; + // cross reference transition by document order - is this really needed?! std::set elements; elements.insert(_nsInfo.xmlNSPrefix + "transition"); @@ -1853,6 +1989,10 @@ void ChartToC::writeTransitions(std::ostream& stream) { } stream << "};" << std::endl; stream << std::endl; + + stream << "#endif" << std::endl; + stream << std::endl; + } Arabica::XPath::NodeSet ChartToC::computeExitSet(const Arabica::DOM::Element& transition) { @@ -1916,7 +2056,7 @@ void ChartToC::writeCharArrayInitList(std::ostream& stream, const std::string& b } void ChartToC::writeFSM(std::ostream& stream) { - stream << "#ifndef USCXML_STEP_FUNCTION" << std::endl; + stream << "#ifndef USCXML_NO_STEP_FUNCTION" << std::endl; stream << "int uscxml_step(uscxml_ctx* ctx) {" << std::endl; stream << std::endl; @@ -1981,7 +2121,7 @@ void ChartToC::writeFSM(std::ostream& stream) { stream << " ctx->event = NULL;" << std::endl; stream << " goto SELECT_TRANSITIONS;" << std::endl; stream << " }" << std::endl; - stream << " if ((ctx->event = ctx->dequeue_internal(ctx)) != NULL) {" << std::endl; + stream << " if (ctx->dequeue_internal != NULL && (ctx->event = ctx->dequeue_internal(ctx)) != NULL) {" << std::endl; stream << " goto SELECT_TRANSITIONS;" << std::endl; stream << " }" << std::endl; stream << std::endl; @@ -2003,10 +2143,15 @@ void ChartToC::writeFSM(std::ostream& stream) { stream << " }" << std::endl; stream << std::endl; - stream << " if ((ctx->event = ctx->dequeue_external(ctx)) != NULL) {" << std::endl; + stream << " if (ctx->dequeue_external != NULL && (ctx->event = ctx->dequeue_external(ctx)) != NULL) {" << std::endl; stream << " goto SELECT_TRANSITIONS;" << std::endl; stream << " }" << std::endl; stream << std::endl; + stream << " if (ctx->dequeue_external == NULL) {" << std::endl; + stream << " return USCXML_ERR_DONE;" << std::endl; + stream << " }" << std::endl; + stream << " return USCXML_ERR_IDLE;" << std::endl; + stream << std::endl; stream << "SELECT_TRANSITIONS:" << std::endl; stream << " bit_clear_all(conflicts, nr_trans_bytes);" << std::endl; @@ -2020,22 +2165,26 @@ void ChartToC::writeFSM(std::ostream& stream) { stream << " if (BIT_HAS(USCXML_GET_TRANS(i).source, ctx->config)) {" << std::endl; stream << " /* is it non-conflicting? */" << std::endl; stream << " if (!BIT_HAS(i, conflicts)) {" << std::endl; - stream << " /* is it enabled? */" << std::endl; - stream << " if (ctx->is_enabled(ctx, &USCXML_GET_TRANS(i), ctx->event) > 0) {" << std::endl; - stream << " /* remember that we found a transition */" << std::endl; - stream << " ctx->flags |= USCXML_CTX_TRANSITION_FOUND;" << std::endl; + stream << " /* is it spontaneous with an event or vice versa? */" << std::endl; + stream << " if ((USCXML_GET_TRANS(i).event == NULL && ctx->event == NULL) || " << std::endl; + stream << " (USCXML_GET_TRANS(i).event != NULL && ctx->event != NULL)) {" << std::endl; + stream << " /* is it enabled? */" << std::endl; + stream << " if (ctx->is_enabled(ctx, &USCXML_GET_TRANS(i), ctx->event) > 0) {" << std::endl; + stream << " /* remember that we found a transition */" << std::endl; + stream << " ctx->flags |= USCXML_CTX_TRANSITION_FOUND;" << std::endl; stream << std::endl; - stream << " /* transitions that are pre-empted */" << std::endl; - stream << " bit_or(conflicts, USCXML_GET_TRANS(i).conflicts, nr_trans_bytes);" << std::endl; + stream << " /* transitions that are pre-empted */" << std::endl; + stream << " bit_or(conflicts, USCXML_GET_TRANS(i).conflicts, nr_trans_bytes);" << std::endl; stream << std::endl; - stream << " /* states that are directly targeted (resolve as entry-set later) */" << std::endl; - stream << " bit_or(target_set, USCXML_GET_TRANS(i).target, nr_states_bytes);" << std::endl; + stream << " /* states that are directly targeted (resolve as entry-set later) */" << std::endl; + stream << " bit_or(target_set, USCXML_GET_TRANS(i).target, nr_states_bytes);" << std::endl; stream << std::endl; - stream << " /* states that will be left */" << std::endl; - stream << " bit_or(exit_set, USCXML_GET_TRANS(i).exit_set, nr_states_bytes);" << std::endl; + stream << " /* states that will be left */" << std::endl; + stream << " bit_or(exit_set, USCXML_GET_TRANS(i).exit_set, nr_states_bytes);" << std::endl; stream << std::endl; - stream << " BIT_SET_AT(i, trans_set);" << std::endl; + stream << " BIT_SET_AT(i, trans_set);" << std::endl; + stream << " }" << std::endl; stream << " }" << std::endl; stream << " }" << std::endl; stream << " }" << std::endl; @@ -2334,7 +2483,7 @@ void ChartToC::writeFSM(std::ostream& stream) { stream << "}" << std::endl; stream << std::endl; - stream << "#define USCXML_STEP_FUNCTION" << std::endl; + stream << "#define USCXML_NO_STEP_FUNCTION" << std::endl; stream << "#endif" << std::endl; stream << std::endl; } diff --git a/src/uscxml/transform/ChartToC.h b/src/uscxml/transform/ChartToC.h index 4e1d14a..bb17102 100644 --- a/src/uscxml/transform/ChartToC.h +++ b/src/uscxml/transform/ChartToC.h @@ -93,6 +93,8 @@ protected: ChartToC* _parentMachine; std::list _nestedMachines; std::list _allMachines; + + std::list* _prefixes; }; } diff --git a/src/uscxml/transform/ChartToVHDL.cpp b/src/uscxml/transform/ChartToVHDL.cpp index 8e7f43f..f37ad7e 100644 --- a/src/uscxml/transform/ChartToVHDL.cpp +++ b/src/uscxml/transform/ChartToVHDL.cpp @@ -125,28 +125,14 @@ void ChartToVHDL::writeTo(std::ostream& stream) { // _eventTrie.dump(); - writeOptimalTransitionSetSelection(stream); writeTypes(stream); writeFiFo(stream); - writeTransitionSet(stream); + writeOptimalTransitionSetSelection(stream); writeExitSet(stream); writeEntrySet(stream); writeFSM(stream); } -void ChartToVHDL::writeTransitionSet(std::ostream & stream) { - for (size_t i = 0; i < _transitions.size(); i++) { - Element transition(_transitions[i]); - std::string name = DOMUtils::idForNode(transition); - - } -} - -void ChartToVHDL::writeExitSet(std::ostream & stream) { -} - -void ChartToVHDL::writeEntrySet(std::ostream & stream) { -} void ChartToVHDL::writeFSM(std::ostream & stream) { @@ -475,14 +461,14 @@ std::string ChartToVHDL::eventNameEscape(const std::string& eventName) { } void ChartToVHDL::writeOptimalTransitionSetSelection(std::ostream & stream) { - stream << "-- write optimal transition set selection" << std::endl; + stream << "-- optimal transition set selection" << std::endl; for (size_t i = 0; i < _transitions.size(); i++) { Element transition(_transitions[i]); std::string conflicts = ATTR(transition, "conflictBools"); stream << "in_optimal_transition_set_" << ATTR(transition, "postFixOrder") << "_sig " << "<= " << (HAS_ATTR(transition, "event") ? "(not spontaneous_sig)" : "spontaneous_sig") << " and " << std::endl - << " state_active_" << ATTR(transition, "source") << "_sig and not ( 0 " << std::endl; + << " state_active_" << ATTR(transition, "source") << "_sig and not ( '0' " << std::endl; for (size_t j = 0; j < i; j++) { if (conflicts[j] == '1') { stream << " or in_optimal_transition_set_" << toStr(j) << "_sig" << std::endl; @@ -490,7 +476,7 @@ void ChartToVHDL::writeOptimalTransitionSetSelection(std::ostream & stream) { } stream << " )"; if (HAS_ATTR(transition, "event")) { - stream << " and ( 0 " << std::endl;; + stream << " and ( '0' " << std::endl;; // find all matching event literals std::list eventDescs = tokenizeIdRefs(ATTR(transition, "event")); @@ -508,6 +494,96 @@ void ChartToVHDL::writeOptimalTransitionSetSelection(std::ostream & stream) { } +void ChartToVHDL::writeExitSet(std::ostream & stream) { + stream << "-- exit set selection" << std::endl; + + for (size_t i = 0; i < _states.size(); i++) { + Element state(_states[i]); + std::string completion = ATTR(state, "completionBools"); + std::string ancestors = ATTR(state, "ancBools"); + std::string children = ATTR(state, "childBools"); + std::string parent = ATTR(state, "parent"); + + stream << "in_exit_set_" << toStr(i) << "_sig " + << "<= state_active_ " << toStr(i) << "_sig and ('0'" << std::endl; + for (size_t j = 0; j < _transitions.size(); j++) { + Element transition(_transitions[j]); + std::string exitSet = ATTR(transition, "exitSetBools"); + if (exitSet[i] == '1') { + stream << " or in_optimal_transition_set_" << toStr(j) << "_sig " << std::endl; + } + } + + stream << ")"; + stream << ";" << std::endl; + + } +} + +void ChartToVHDL::writeEntrySet(std::ostream & stream) { + stream << "-- entry set selection" << std::endl; + + for (size_t i = 0; i < _states.size(); i++) { + Element state(_states[i]); + std::string completion = ATTR(state, "completionBools"); + std::string ancestors = ATTR(state, "ancBools"); + std::string children = ATTR(state, "childBools"); + std::string parent = ATTR(state, "parent"); + + stream << "in_complete_entry_set_up_" << toStr(i) << "_sig <= ('0'" << std::endl; + + for (size_t j = 0; j < _transitions.size(); j++) { + Element transition(_transitions[j]); +// std::cout << transition; + std::string targetSet = ATTR(transition, "targetBools"); + if (targetSet[i] == '1') { + stream << " or in_optimal_transition_set_" << toStr(j) << std::endl; + } + } + if (isCompound(state)) { + for (size_t j = 0; j < _states.size(); j++) { + if (children[j] != '1') + continue; + + stream << " or in_complete_entry_set_up_" << toStr(j) << "_sig" << std::endl; + } + + } + stream << ");" << std::endl; + + } + + for (size_t i = 0; i < _states.size(); i++) { + Element state(_states[i]); + std::string completion = ATTR(state, "completionBools"); + std::string ancestors = ATTR(state, "ancBools"); + std::string children = ATTR(state, "childBools"); + std::string parent = ATTR(state, "parent"); + + if (parent.size() == 0) { + continue; // TODO: FixMe + } + + stream << "in_complete_entry_set_" << toStr(i) << "_sig <= (in_complete_entry_set_up_" << toStr(i) << "_sig or (" << std::endl; + + if (isParallel(Element(_states[strTo(parent)]))) { + stream << " in_complete_entry_set_" << toStr(parent) << "_sig" << std::endl; + } else if (isCompound(Element(_states[strTo(parent)]))) { + stream << " default_completion_" << toStr(parent) << "_sig" << std::endl; + + for (size_t j = 0; j < _states.size(); j++) { + if (children[j] != '1') + continue; + stream << " and not (is_active" << toStr(j) << "_sig and not in_exit_set_" << toStr(j) << "_sig)" << std::endl; + + } + } + + stream << ");" << std::endl; + + } +} + //TODO write event generator // wie die letzten beiden states erkennen // process bauen der bei fail 0 ausgibt und bei accept 1 diff --git a/src/uscxml/transform/ChartToVHDL.h b/src/uscxml/transform/ChartToVHDL.h index 64cf444..abcb477 100644 --- a/src/uscxml/transform/ChartToVHDL.h +++ b/src/uscxml/transform/ChartToVHDL.h @@ -51,7 +51,11 @@ protected: void writeTopDown(std::ostream& stream); void writeTypes(std::ostream& stream); + void writeOptimalTransitionSetSelection(std::ostream& stream); + void writeExitSet(std::ostream & stream); + void writeEntrySet(std::ostream & stream); + void writeNextStateLogic(std::ostream& stream); void writeOutputLogic(std::ostream& stream); void writeSignals(std::ostream& stream); @@ -61,8 +65,6 @@ protected: void writeFSM(std::ostream& stream); void writeTransitionSet(std::ostream & stream); - void writeExitSet(std::ostream & stream); - void writeEntrySet(std::ostream & stream); Trie _eventTrie; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d61abf7..1c1223e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -82,7 +82,30 @@ add_test(test-execution ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE add_test(test-communication ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser -t5493 ${CMAKE_SOURCE_DIR}/test/uscxml/test-communication.scxml) add_test(test-done-data ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE_SOURCE_DIR}/test/uscxml/test-donedata.scxml) -# declare W#C tests +# tests for inline SCXML with generated C + +add_executable(test-c-inline src/test-c-inline.c) +set_target_properties(test-c-inline PROPERTIES FOLDER "Tests") + +add_test(NAME "gen/c/inline" + COMMAND ${CMAKE_COMMAND} + -DOUTDIR:FILEPATH=${CMAKE_CURRENT_BINARY_DIR}/gen/c + -DTESTFILE:FILEPATH=${CMAKE_CURRENT_SOURCE_DIR}/src/test-c-inline.c + -DTARGETLANG=c + -DUSCXML_TRANSFORM_BIN:FILEPATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-transform + -DCC_BIN:FILEPATH=${CC} + -DCXX_BIN:FILEPATH=${CXX} + -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} + -DUSCXML_PLATFORM_ID=${USCXML_PLATFORM_ID} + -DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR} + -DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR} + -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + -P ${CMAKE_CURRENT_SOURCE_DIR}/w3c/run_generated_inline_test.cmake) +set_tests_properties("gen/c/inline" PROPERTIES DEPENDS uscxml-transform) +set_property(TEST "gen/c/inline" PROPERTY LABELS "gen/c/inline") + + +# declare W3C tests find_program(SPIN spin) find_program(CC gcc) diff --git a/test/src/test-c-inline.c b/test/src/test-c-inline.c new file mode 100644 index 0000000..c12ac73 --- /dev/null +++ b/test/src/test-c-inline.c @@ -0,0 +1,40 @@ +#include // EXIT_SUCCESS +#include // printf +#include // memset + +/** + * Preprocess: + * uscxml-transform -tc -i ./test-c-inline.c -o ./test-c-inline.c.scxml.c + */ + +/** INLINE SCXML BEGIN + + + + enteredFoo(); + + + +INLINE SCXML END */ + +/** + * These function can be called from within executable content + */ +void enteredFoo() { + printf("Entered Foo!\n"); +} + +#include "test-c-inline.c.scxml.c" + +int main(int argc, char** argv) { + uscxml_ctx ctx; + memset(&ctx, 0, sizeof(uscxml_ctx)); + ctx.machine = &USCXML_MACHINE_TEST_INLINE; + + int err = USCXML_ERR_OK; + while(err != USCXML_ERR_DONE) { + err = uscxml_step(&ctx); + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/test/src/test-c-inline.c.scxml.c b/test/src/test-c-inline.c.scxml.c new file mode 100644 index 0000000..d586bb3 --- /dev/null +++ b/test/src/test-c-inline.c.scxml.c @@ -0,0 +1,951 @@ +/** + Generated from source: + file:///Users/sradomski/Documents/TK/Code/uscxml/test/src/test-c-inline.c +*/ + +#ifndef USCXML_NO_STDTYPES_H +# include /* explicit types */ +#endif +#include /* NULL */ + +#ifndef USCXML_NO_GEN_C_MACROS + +/** + * All macros used for the scxml types and functions + * + * ** IMPORTANT: Make sure to set the following macros prior to including. ** + * They are used to represent the machine in the types to follow + * and to allocate stack memory during a micro-step function. + * When in doubt, overprovide. + * + * USCXML_NR_STATES_TYPE + * as the smallest type for positive integers that can contain the + * largest number of states from an individual state machine. E.g.: + * < 2^8 states => uint8_t + * < 2^16 states => uint16_t + * < 2^32 states => uint32_t + */ + +#ifndef USCXML_NR_STATES_TYPE +# define USCXML_NR_STATES_TYPE uint8_t +#endif + +/** + * USCXML_NR_TRANS_TYPE + * the same as above but for the number of transitions. + */ + +#ifndef USCXML_NR_TRANS_TYPE +# define USCXML_NR_TRANS_TYPE uint8_t +#endif + +/** + * USCXML_MAX_NR_STATES_BYTES + * the smallest multiple of 8 that, if multiplied by 8, + * is larger than USCXML_NR_STATES_TYPE, e.g: + * 1-8 states => 1 + * 9-16 states => 2 + * 17-24 states => 3 + * 25-32 states => 4 + * ... + */ + +#ifndef USCXML_MAX_NR_STATES_BYTES +# define USCXML_MAX_NR_STATES_BYTES 1 +#endif + +/** + * USCXML_MAX_NR_TRANS_BYTES + * same as above but for transitions. + */ + +#ifndef USCXML_MAX_NR_TRANS_BYTES +# define USCXML_MAX_NR_TRANS_BYTES 0 +#endif + +/** + * USCXML_NUMBER_STATES / USCXML_NUMBER_TRANS + * Per default the number of states / transitions is retrieved from the machine + * info in the uscxml_ctx struct, but you can also hard-code it per macro. + */ + +#ifndef USCXML_NUMBER_STATES +# define USCXML_NUMBER_STATES (ctx->machine->nr_states) +#endif + +#ifndef USCXML_NUMBER_TRANS +# define USCXML_NUMBER_TRANS (ctx->machine->nr_transitions) +#endif + +/** + * USCXML_GET_STATE / USCXML_GET_TRANS + * Per default an individual state or transitions is retrieved from the machine + * info in the uscxml_ctx struct, but you can also hard-code it per macro. + */ + +#ifndef USCXML_GET_STATE +# define USCXML_GET_STATE(i) (ctx->machine->states[i]) +#endif + +#ifndef USCXML_GET_TRANS +# define USCXML_GET_TRANS(i) (ctx->machine->transitions[i]) +#endif + + +/* Common macros below */ + +#define BIT_HAS(idx, bitset) ((bitset[idx >> 3] & (1 << (idx & 7))) != 0) +#define BIT_SET_AT(idx, bitset) bitset[idx >> 3] |= (1 << (idx & 7)); +#define BIT_CLEAR(idx, bitset) bitset[idx >> 3] &= (1 << (idx & 7)) ^ 0xFF; + +#ifdef __GNUC__ +# define likely(x) (__builtin_expect(!!(x), 1)) +# define unlikely(x) (__builtin_expect(!!(x), 0)) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + +/* error return codes */ +#define USCXML_ERR_OK 0 +#define USCXML_ERR_IDLE 1 +#define USCXML_ERR_DONE 2 +#define USCXML_ERR_MISSING_CALLBACK 3 +#define USCXML_ERR_FOREACH_DONE 4 +#define USCXML_ERR_EXEC_CONTENT 5 +#define USCXML_ERR_INVALID_TARGET 6 +#define USCXML_ERR_INVALID_TYPE 7 +#define USCXML_ERR_UNSUPPORTED 8 + +#define USCXML_TRANS_SPONTANEOUS 0x01 +#define USCXML_TRANS_TARGETLESS 0x02 +#define USCXML_TRANS_INTERNAL 0x04 +#define USCXML_TRANS_HISTORY 0x08 +#define USCXML_TRANS_INITIAL 0x10 + +#define USCXML_STATE_ATOMIC 0x01 +#define USCXML_STATE_PARALLEL 0x02 +#define USCXML_STATE_COMPOUND 0x03 +#define USCXML_STATE_FINAL 0x04 +#define USCXML_STATE_HISTORY_DEEP 0x05 +#define USCXML_STATE_HISTORY_SHALLOW 0x06 +#define USCXML_STATE_INITIAL 0x07 +#define USCXML_STATE_HAS_HISTORY 0x80 /* highest bit */ +#define USCXML_STATE_MASK(t) (t & 0x7F) /* mask highest bit */ + +#define USCXML_CTX_PRISTINE 0x00 +#define USCXML_CTX_SPONTANEOUS 0x01 +#define USCXML_CTX_INITIALIZED 0x02 +#define USCXML_CTX_TOP_LEVEL_FINAL 0x04 +#define USCXML_CTX_TRANSITION_FOUND 0x08 +#define USCXML_CTX_FINISHED 0x10 + +#define USCXML_ELEM_DATA_IS_SET(data) (data->id != NULL) +#define USCXML_ELEM_DONEDATA_IS_SET(donedata) (donedata->content != NULL || donedata->contentexpr != NULL || donedata->params != NULL) +#define USCXML_ELEM_PARAM_IS_SET(param) (param->name != NULL) +#define USCXML_MACHINE_IS_SET(machine) (machine->nr_states > 0) + +#define USCXML_NO_GEN_C_MACROS +#endif + + +#ifndef USCXML_NO_GEN_C_TYPES + +/** + * All types required to represent an SCXML state chart. + * Just predefine the USCXML_NO_GEN_C_TYPES macro if you do not need them. + */ + +typedef struct uscxml_machine uscxml_machine; +typedef struct uscxml_transition uscxml_transition; +typedef struct uscxml_state uscxml_state; +typedef struct uscxml_ctx uscxml_ctx; +typedef struct uscxml_elem_invoke uscxml_elem_invoke; + +typedef struct uscxml_elem_send uscxml_elem_send; +typedef struct uscxml_elem_param uscxml_elem_param; +typedef struct uscxml_elem_data uscxml_elem_data; +typedef struct uscxml_elem_donedata uscxml_elem_donedata; +typedef struct uscxml_elem_foreach uscxml_elem_foreach; + +typedef void* (*dequeue_internal_t)(const uscxml_ctx* ctx); +typedef void* (*dequeue_external_t)(const uscxml_ctx* ctx); +typedef int (*is_enabled_t)(const uscxml_ctx* ctx, const uscxml_transition* transition, const void* event); +typedef int (*is_true_t)(const uscxml_ctx* ctx, const char* expr); +typedef int (*exec_content_t)(const uscxml_ctx* ctx, const uscxml_state* state, const void* event); +typedef int (*raise_done_event_t)(const uscxml_ctx* ctx, const uscxml_state* state, const uscxml_elem_donedata* donedata); +typedef int (*invoke_t)(const uscxml_ctx* ctx, const uscxml_state* s, const uscxml_elem_invoke* invocation, unsigned char uninvoke); + +typedef int (*exec_content_log_t)(const uscxml_ctx* ctx, const char* label, const char* expr); +typedef int (*exec_content_raise_t)(const uscxml_ctx* ctx, const char* event); +typedef int (*exec_content_send_t)(const uscxml_ctx* ctx, const uscxml_elem_send* send); +typedef int (*exec_content_foreach_init_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); +typedef int (*exec_content_foreach_next_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); +typedef int (*exec_content_foreach_done_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); +typedef int (*exec_content_assign_t)(const uscxml_ctx* ctx, const char* location, const char* expr); +typedef int (*exec_content_init_t)(const uscxml_ctx* ctx, const uscxml_elem_data* data); +typedef int (*exec_content_cancel_t)(const uscxml_ctx* ctx, const char* sendid, const char* sendidexpr); +typedef int (*exec_content_finalize_t)(const uscxml_ctx* ctx, const uscxml_elem_invoke* invoker, const void* event); +typedef int (*exec_content_script_t)(const uscxml_ctx* ctx, const char* src, const char* content); + +/** + * A single SCXML state-machine. + */ +struct uscxml_machine { + unsigned char flags; /* Unused */ + USCXML_NR_STATES_TYPE nr_states; /* Make sure to set type per macro! */ + USCXML_NR_TRANS_TYPE nr_transitions; /* Make sure to set type per macro! */ + const char* name; + const char* datamodel; + const char* uuid; /* currently MD5 sum */ + const uscxml_state* states; + const uscxml_transition* transitions; + const uscxml_machine* parent; + const uscxml_elem_donedata* donedata; + const exec_content_t script; /* Global script elements */ +}; + +/** + * All information pertaining to a element. + * With late data binding, blocks of data elements are separated by NULL + * use USCXML_ELEM_DATA_IS_SET to test for end of a block. + */ +struct uscxml_elem_data { + const char* id; + const char* src; + const char* expr; + const char* content; +}; + +/** + * All information pertaining to any state element. + */ +struct uscxml_state { + const char* name; /* eventual name */ + const USCXML_NR_STATES_TYPE parent; /* parent */ + const exec_content_t on_entry; /* on entry handlers */ + const exec_content_t on_exit; /* on exit handlers */ + const invoke_t invoke; /* invocations */ + const char children[USCXML_MAX_NR_STATES_BYTES]; /* all children */ + const char completion[USCXML_MAX_NR_STATES_BYTES]; /* default completion */ + const char ancestors[USCXML_MAX_NR_STATES_BYTES]; /* all ancestors */ + const uscxml_elem_data* data; /* data with late binding */ + const unsigned char type; /* One of USCXML_STATE_* */ +}; + +/** + * All information pertaining to a element. + */ +struct uscxml_transition { + const USCXML_NR_STATES_TYPE source; + const char target[USCXML_MAX_NR_STATES_BYTES]; + const char* event; + const char* condition; + const exec_content_t on_transition; + const unsigned char type; + const char conflicts[USCXML_MAX_NR_TRANS_BYTES]; + const char exit_set[USCXML_MAX_NR_STATES_BYTES]; +}; + +/** + * All information pertaining to a element. + */ +struct uscxml_elem_foreach { + const char* array; + const char* item; + const char* index; +}; + +/** + * All information pertaining to a element. + * Blocks of params are separated by NULL params, use + * USCXML_ELEM_PARAM_IS_SET to test for end of a block. + */ +struct uscxml_elem_param { + const char* name; + const char* expr; + const char* location; +}; + +/** + * All information pertaining to a element. + */ +struct uscxml_elem_donedata { + const USCXML_NR_STATES_TYPE source; + const char* content; + const char* contentexpr; + const uscxml_elem_param* params; +}; + +/** + * All information pertaining to an element. + */ +struct uscxml_elem_invoke { + const uscxml_machine* machine; + const char* type; + const char* typeexpr; + const char* src; + const char* srcexpr; + const char* id; + const char* idlocation; + const char* sourcename; + const char* namelist; + const unsigned char autoforward; + const uscxml_elem_param* params; + exec_content_finalize_t finalize; + const char* content; + const char* contentexpr; +}; + +/** + * All information pertaining to a element. + */ +struct uscxml_elem_send { + const char* event; + const char* eventexpr; + const char* target; + const char* targetexpr; + const char* type; + const char* typeexpr; + const char* id; + const char* idlocation; + const char* delay; + const char* delayexpr; + const char* namelist; /* not space-separated, still as in attribute value */ + const char* content; + const char* contentexpr; + const uscxml_elem_param* params; +}; + +/** + * Represents an instance of a state-chart at runtime/ + */ +struct uscxml_ctx { + unsigned char flags; + const uscxml_machine* machine; + + char config[USCXML_MAX_NR_STATES_BYTES]; /* Make sure these macros specify a sufficient size */ + char history[USCXML_MAX_NR_STATES_BYTES]; + char invocations[USCXML_MAX_NR_STATES_BYTES]; + char initialized_data[USCXML_MAX_NR_STATES_BYTES]; + + void* user_data; + void* event; + + dequeue_internal_t dequeue_internal; + dequeue_external_t dequeue_external; + is_enabled_t is_enabled; + is_true_t is_true; + raise_done_event_t raise_done_event; + + exec_content_log_t exec_content_log; + exec_content_raise_t exec_content_raise; + exec_content_send_t exec_content_send; + exec_content_foreach_init_t exec_content_foreach_init; + exec_content_foreach_next_t exec_content_foreach_next; + exec_content_foreach_done_t exec_content_foreach_done; + exec_content_assign_t exec_content_assign; + exec_content_init_t exec_content_init; + exec_content_cancel_t exec_content_cancel; + exec_content_script_t exec_content_script; + + invoke_t invoke; +}; + +#define USCXML_NO_GEN_C_TYPES +#endif + +/* forward declare machines to allow references */ +extern const uscxml_machine _uscxml_9FAC9BE9_machine; + +#ifndef USCXML_NO_ELEM_INFO + +static const uscxml_elem_donedata _uscxml_9FAC9BE9_elem_donedatas[1] = { + /* source, content, contentexpr, params */ + { 0, NULL, NULL, NULL } +}; + +#endif + +#ifndef USCXML_NO_ELEM_INFO + +#endif + +#ifndef USCXML_NO_EXEC_CONTENT + +static int _uscxml_9FAC9BE9_foo_on_entry_0(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + int err = USCXML_ERR_OK; + + enteredFoo(); + return USCXML_ERR_OK; +} + +static int _uscxml_9FAC9BE9_foo_on_entry(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + _uscxml_9FAC9BE9_foo_on_entry_0(ctx, state, event); + return USCXML_ERR_OK; +} + +#endif + +#ifndef USCXML_NO_ELEM_INFO + +static const uscxml_state _uscxml_9FAC9BE9_states[2] = { + { /* state number 0 */ + /* name */ NULL, + /* parent */ 0, + /* onentry */ NULL, + /* onexit */ NULL, + /* invoke */ NULL, + /* children */ { 0x02 /* 01 */ }, + /* completion */ { 0x02 /* 01 */ }, + /* ancestors */ { 0x00 /* 00 */ }, + /* data */ NULL, + /* type */ USCXML_STATE_COMPOUND, + }, + { /* state number 1 */ + /* name */ "foo", + /* parent */ 0, + /* onentry */ _uscxml_9FAC9BE9_foo_on_entry, + /* onexit */ NULL, + /* invoke */ NULL, + /* children */ { 0x00 /* 00 */ }, + /* completion */ { 0x00 /* 00 */ }, + /* ancestors */ { 0x01 /* 10 */ }, + /* data */ NULL, + /* type */ USCXML_STATE_ATOMIC, + } +}; + +#endif + +#ifndef USCXML_NO_ELEM_INFO + +static const uscxml_transition _uscxml_9FAC9BE9_transitions[0] = { +}; + +#endif + +#ifndef USCXML_NO_ELEM_INFO + +#ifndef USCXML_MACHINE +# define USCXML_MACHINE _uscxml_9FAC9BE9_machine +#endif +#define USCXML_MACHINE_0 _uscxml_9FAC9BE9_machine +#define USCXML_MACHINE_TEST_INLINE _uscxml_9FAC9BE9_machine + +const uscxml_machine _uscxml_9FAC9BE9_machine = { + /* flags */ 0, + /* nr_states */ 2, + /* nr_transitions */ 0, + /* name */ "test-inline", + /* datamodel */ "native", + /* uuid */ "9FAC9BE9A82F66AFD36A205557064B27", + /* states */ &_uscxml_9FAC9BE9_states[0], + /* transitions */ &_uscxml_9FAC9BE9_transitions[0], + /* parent */ NULL, + /* donedata */ &_uscxml_9FAC9BE9_elem_donedatas[0], + /* script */ NULL +}; + +#endif + +#ifdef USCXML_VERBOSE +/** + * Print name of states contained in a (debugging). + */ +static void printStateNames(const uscxml_ctx* ctx, const char* a, size_t length) { + size_t i; + const char* seperator = ""; + for (i = 0; i < length; i++) { + if (BIT_HAS(i, a)) { + printf("%s%s", seperator, (USCXML_GET_STATE(i).name != NULL ? USCXML_GET_STATE(i).name : "UNK")); + seperator = ", "; + } + } + printf("\n"); +} + +/** + * Print bits set in a in a binary representation (debugging). + */ +static void printBitsetIndices(const char* a, size_t length) { + size_t i; + const char* seperator = ""; + for (i = 0; i < length; i++) { + if (BIT_HAS(i, a)) { + printf("%s%lu", seperator, i); + seperator = ", "; + } + } + printf("\n"); +} +#endif + +#ifndef USCXML_NO_BIT_OPERATIONS +/** + * Return true if there is a common bit in a and b. + */ +static int bit_has_and(const char* a, const char* b, size_t i) { + while(i--) { + if (a[i] & b[i]) + return 1; + } + return 0; +} + +/** + * Set all bits to 0, this corresponds to memset(a, 0, i), + * but does not require string.h or cstring. + */ +static void bit_clear_all(char* a, size_t i) { + while(i--) { + a[i] = 0; + } +} + +/** + * Return true if there is any bit set in a. + */ +static int bit_has_any(const char* a, size_t i) { + while(i--) { + if (a[i] > 0) + return 1; + } + return 0; +} + +/** + * Set all bits from given mask in dest, this is |= for bit arrays. + */ +static void bit_or(char* dest, const char* mask, size_t i) { + while(i--) { + dest[i] |= mask[i]; + } +} + +/** + * Copy all bits from source to dest, this corresponds to memcpy(a, b, i), + * but does not require string.h or cstring. + */ +static void bit_copy(char* dest, const char* source, size_t i) { + while(i--) { + dest[i] = source[i]; + } +} + +/** + * Unset bits from mask in dest. + */ +static void bit_and_not(char* dest, const char* mask, size_t i) { + while(i--) { + dest[i] &= ~mask[i]; + } +} + +/** + * Set bits from mask in dest. + */ +static void bit_and(char* dest, const char* mask, size_t i) { + while(i--) { + dest[i] &= mask[i]; + }; +} + +#define USCXML_NO_BIT_OPERATIONS +#endif + +#ifndef USCXML_NO_STEP_FUNCTION +int uscxml_step(uscxml_ctx* ctx) { + + USCXML_NR_STATES_TYPE i, j, k; + USCXML_NR_STATES_TYPE nr_states_bytes = ((USCXML_NUMBER_STATES + 7) & ~7) >> 3; + USCXML_NR_TRANS_TYPE nr_trans_bytes = ((USCXML_NUMBER_TRANS + 7) & ~7) >> 3; + int err = USCXML_ERR_OK; + char conflicts [USCXML_MAX_NR_TRANS_BYTES]; + char trans_set [USCXML_MAX_NR_TRANS_BYTES]; + char target_set [USCXML_MAX_NR_STATES_BYTES]; + char exit_set [USCXML_MAX_NR_STATES_BYTES]; + char entry_set [USCXML_MAX_NR_STATES_BYTES]; + char tmp_states [USCXML_MAX_NR_STATES_BYTES]; + +#ifdef USCXML_VERBOSE + printf("Config: "); + printStateNames(ctx, ctx->config, USCXML_NUMBER_STATES); +#endif + + if (ctx->flags & USCXML_CTX_FINISHED) + return USCXML_ERR_DONE; + + if (ctx->flags & USCXML_CTX_TOP_LEVEL_FINAL) { + /* exit all remaining states */ + i = USCXML_NUMBER_STATES; + while(i-- > 0) { + if (BIT_HAS(i, ctx->config)) { + /* call all on exit handlers */ + if (USCXML_GET_STATE(i).on_exit != NULL) { + if unlikely((err = USCXML_GET_STATE(i).on_exit(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) + return err; + } + } + if (BIT_HAS(i, ctx->invocations)) { + if (USCXML_GET_STATE(i).invoke != NULL) + USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 1); + BIT_CLEAR(i, ctx->invocations); + } + } + ctx->flags |= USCXML_CTX_FINISHED; + return USCXML_ERR_DONE; + } + + bit_clear_all(target_set, nr_states_bytes); + bit_clear_all(trans_set, nr_trans_bytes); + if unlikely(ctx->flags == USCXML_CTX_PRISTINE) { + if (ctx->machine->script != NULL) + ctx->machine->script(ctx, &ctx->machine->states[0], NULL); + bit_or(target_set, ctx->machine->states[0].completion, nr_states_bytes); + ctx->flags |= USCXML_CTX_SPONTANEOUS | USCXML_CTX_INITIALIZED; + goto ESTABLISH_ENTRY_SET; + } + + if (ctx->flags & USCXML_CTX_SPONTANEOUS) { + ctx->event = NULL; + goto SELECT_TRANSITIONS; + } + if (ctx->dequeue_internal != NULL && (ctx->event = ctx->dequeue_internal(ctx)) != NULL) { + goto SELECT_TRANSITIONS; + } + + /* manage invocations */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + /* uninvoke */ + if (!BIT_HAS(i, ctx->config) && BIT_HAS(i, ctx->invocations)) { + if (USCXML_GET_STATE(i).invoke != NULL) + USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 1); + BIT_CLEAR(i, ctx->invocations) + } + /* invoke */ + if (BIT_HAS(i, ctx->config) && !BIT_HAS(i, ctx->invocations)) { + if (USCXML_GET_STATE(i).invoke != NULL) + USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 0); + BIT_SET_AT(i, ctx->invocations) + } + } + + if (ctx->dequeue_external != NULL && (ctx->event = ctx->dequeue_external(ctx)) != NULL) { + goto SELECT_TRANSITIONS; + } + + if (ctx->dequeue_external == NULL) { + return USCXML_ERR_DONE; + } + return USCXML_ERR_IDLE; + +SELECT_TRANSITIONS: + bit_clear_all(conflicts, nr_trans_bytes); + bit_clear_all(exit_set, nr_states_bytes); + for (i = 0; i < USCXML_NUMBER_TRANS; i++) { + /* never select history or initial transitions automatically */ + if unlikely(USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) + continue; + + /* is the transition active? */ + if (BIT_HAS(USCXML_GET_TRANS(i).source, ctx->config)) { + /* is it non-conflicting? */ + if (!BIT_HAS(i, conflicts)) { + /* is it spontaneous with an event or vice versa? */ + if ((USCXML_GET_TRANS(i).event == NULL && ctx->event == NULL) || + (USCXML_GET_TRANS(i).event != NULL && ctx->event != NULL)) { + /* is it enabled? */ + if (ctx->is_enabled(ctx, &USCXML_GET_TRANS(i), ctx->event) > 0) { + /* remember that we found a transition */ + ctx->flags |= USCXML_CTX_TRANSITION_FOUND; + + /* transitions that are pre-empted */ + bit_or(conflicts, USCXML_GET_TRANS(i).conflicts, nr_trans_bytes); + + /* states that are directly targeted (resolve as entry-set later) */ + bit_or(target_set, USCXML_GET_TRANS(i).target, nr_states_bytes); + + /* states that will be left */ + bit_or(exit_set, USCXML_GET_TRANS(i).exit_set, nr_states_bytes); + + BIT_SET_AT(i, trans_set); + } + } + } + } + } + bit_and(exit_set, ctx->config, nr_states_bytes); + + if (ctx->flags & USCXML_CTX_TRANSITION_FOUND) { + ctx->flags |= USCXML_CTX_SPONTANEOUS; + ctx->flags &= ~USCXML_CTX_TRANSITION_FOUND; + } else { + ctx->flags &= ~USCXML_CTX_SPONTANEOUS; + } + +#ifdef USCXML_VERBOSE + printf("Targets: "); + printStateNames(ctx, target_set, USCXML_NUMBER_STATES); +#endif + +#ifdef USCXML_VERBOSE + printf("Exiting: "); + printStateNames(ctx, exit_set, USCXML_NUMBER_STATES); +#endif + +#ifdef USCXML_VERBOSE + printf("History: "); + printStateNames(ctx, ctx->history, USCXML_NUMBER_STATES); +#endif + +/* REMEMBER_HISTORY: */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW || + USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP) { + /* a history state whose parent is about to be exited */ + if unlikely(BIT_HAS(USCXML_GET_STATE(i).parent, exit_set)) { + bit_copy(tmp_states, USCXML_GET_STATE(i).completion, nr_states_bytes); + + /* set those states who were enabled */ + bit_and(tmp_states, ctx->config, nr_states_bytes); + + /* clear current history with completion mask */ + bit_and_not(ctx->history, USCXML_GET_STATE(i).completion, nr_states_bytes); + + /* set history */ + bit_or(ctx->history, tmp_states, nr_states_bytes); + } + } + } + +ESTABLISH_ENTRY_SET: + /* calculate new entry set */ + bit_copy(entry_set, target_set, nr_states_bytes); + + /* iterate for ancestors */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + if (BIT_HAS(i, entry_set)) { + bit_or(entry_set, USCXML_GET_STATE(i).ancestors, nr_states_bytes); + } + } + + /* iterate for descendants */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + if (BIT_HAS(i, entry_set)) { + switch (USCXML_STATE_MASK(USCXML_GET_STATE(i).type)) { + case USCXML_STATE_PARALLEL: { + bit_or(entry_set, USCXML_GET_STATE(i).completion, nr_states_bytes); + break; + } + case USCXML_STATE_HISTORY_SHALLOW: + case USCXML_STATE_HISTORY_DEEP: { + if (!bit_has_and(USCXML_GET_STATE(i).completion, ctx->history, nr_states_bytes) && + !BIT_HAS(USCXML_GET_STATE(i).parent, ctx->config)) { + /* nothing set for history, look for a default transition */ + for (j = 0; j < USCXML_NUMBER_TRANS; j++) { + if unlikely(ctx->machine->transitions[j].source == i) { + bit_or(entry_set, ctx->machine->transitions[j].target, nr_states_bytes); + if(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP && + !bit_has_and(ctx->machine->transitions[j].target, USCXML_GET_STATE(i).children, nr_states_bytes)) { + for (k = i + 1; k < USCXML_NUMBER_STATES; k++) { + if (BIT_HAS(k, ctx->machine->transitions[j].target)) { + bit_or(entry_set, ctx->machine->states[k].ancestors, nr_states_bytes); + break; + } + } + } + BIT_SET_AT(j, trans_set); + break; + } + /* Note: SCXML mandates every history to have a transition! */ + } + } else { + bit_copy(tmp_states, USCXML_GET_STATE(i).completion, nr_states_bytes); + bit_and(tmp_states, ctx->history, nr_states_bytes); + bit_or(entry_set, tmp_states, nr_states_bytes); + if (USCXML_GET_STATE(i).type == (USCXML_STATE_HAS_HISTORY | USCXML_STATE_HISTORY_DEEP)) { + /* a deep history state with nested histories -> more completion */ + for (j = i + 1; j < USCXML_NUMBER_STATES; j++) { + if (BIT_HAS(j, USCXML_GET_STATE(i).completion) && + BIT_HAS(j, entry_set) && + (ctx->machine->states[j].type & USCXML_STATE_HAS_HISTORY)) { + for (k = j + 1; k < USCXML_NUMBER_STATES; k++) { + /* add nested history to entry_set */ + if ((USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_HISTORY_DEEP || + USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_HISTORY_SHALLOW) && + BIT_HAS(k, ctx->machine->states[j].children)) { + /* a nested history state */ + BIT_SET_AT(k, entry_set); + } + } + } + } + } + } + break; + } + case USCXML_STATE_INITIAL: { + for (j = 0; j < USCXML_NUMBER_TRANS; j++) { + if (ctx->machine->transitions[j].source == i) { + BIT_SET_AT(j, trans_set); + BIT_CLEAR(i, entry_set); + bit_or(entry_set, ctx->machine->transitions[j].target, nr_states_bytes); + for (k = i + 1; k < USCXML_NUMBER_STATES; k++) { + if (BIT_HAS(k, ctx->machine->transitions[j].target)) { + bit_or(entry_set, ctx->machine->states[k].ancestors, nr_states_bytes); + } + } + } + } + break; + } + case USCXML_STATE_COMPOUND: { /* we need to check whether one child is already in entry_set */ + if (!bit_has_and(entry_set, USCXML_GET_STATE(i).children, nr_states_bytes) && + (!bit_has_and(ctx->config, USCXML_GET_STATE(i).children, nr_states_bytes) || + bit_has_and(exit_set, USCXML_GET_STATE(i).children, nr_states_bytes))) + { + bit_or(entry_set, USCXML_GET_STATE(i).completion, nr_states_bytes); + if (!bit_has_and(USCXML_GET_STATE(i).completion, USCXML_GET_STATE(i).children, nr_states_bytes)) { + /* deep completion */ + for (j = i + 1; j < USCXML_NUMBER_STATES; j++) { + if (BIT_HAS(j, USCXML_GET_STATE(i).completion)) { + bit_or(entry_set, ctx->machine->states[j].ancestors, nr_states_bytes); + break; /* completion of compound is single state */ + } + } + } + } + break; + } + } + } + } + +#ifdef USCXML_VERBOSE + printf("Transitions: "); + printBitsetIndices(trans_set, sizeof(char) * 8 * nr_trans_bytes); +#endif + +/* EXIT_STATES: */ + i = USCXML_NUMBER_STATES; + while(i-- > 0) { + if (BIT_HAS(i, exit_set) && BIT_HAS(i, ctx->config)) { + /* call all on exit handlers */ + if (USCXML_GET_STATE(i).on_exit != NULL) { + if unlikely((err = USCXML_GET_STATE(i).on_exit(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) + return err; + } + BIT_CLEAR(i, ctx->config); + } + } + +/* TAKE_TRANSITIONS: */ + for (i = 0; i < USCXML_NUMBER_TRANS; i++) { + if (BIT_HAS(i, trans_set) && (USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { + /* call executable content in transition */ + if (USCXML_GET_TRANS(i).on_transition != NULL) { + if unlikely((err = USCXML_GET_TRANS(i).on_transition(ctx, + &ctx->machine->states[USCXML_GET_TRANS(i).source], + ctx->event)) != USCXML_ERR_OK) + return err; + } + } + } + +#ifdef USCXML_VERBOSE + printf("Entering: "); + printStateNames(ctx, entry_set, USCXML_NUMBER_STATES); +#endif + +/* ENTER_STATES: */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + if (BIT_HAS(i, entry_set) && !BIT_HAS(i, ctx->config)) { + /* these are no proper states */ + if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP || + USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW || + USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_INITIAL) + continue; + + BIT_SET_AT(i, ctx->config); + + /* initialize data */ + if (!BIT_HAS(i, ctx->initialized_data)) { + if unlikely(USCXML_GET_STATE(i).data != NULL && ctx->exec_content_init != NULL) { + ctx->exec_content_init(ctx, USCXML_GET_STATE(i).data); + } + BIT_SET_AT(i, ctx->initialized_data); + } + + if (USCXML_GET_STATE(i).on_entry != NULL) { + if unlikely((err = USCXML_GET_STATE(i).on_entry(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) + return err; + } + + /* take history and initial transitions */ + for (j = 0; j < USCXML_NUMBER_TRANS; j++) { + if unlikely(BIT_HAS(j, trans_set) && + (ctx->machine->transitions[j].type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) && + ctx->machine->states[ctx->machine->transitions[j].source].parent == i) { + /* call executable content in transition */ + if (ctx->machine->transitions[j].on_transition != NULL) { + if unlikely((err = ctx->machine->transitions[j].on_transition(ctx, + &USCXML_GET_STATE(i), + ctx->event)) != USCXML_ERR_OK) + return err; + } + } + } + + /* handle final states */ + if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_FINAL) { + if unlikely(USCXML_GET_STATE(i).ancestors[0] == 0x01) { + ctx->flags |= USCXML_CTX_TOP_LEVEL_FINAL; + } else { + /* raise done event */ + const uscxml_elem_donedata* donedata = &ctx->machine->donedata[0]; + while(USCXML_ELEM_DONEDATA_IS_SET(donedata)) { + if unlikely(donedata->source == i) + break; + donedata++; + } + ctx->raise_done_event(ctx, &ctx->machine->states[USCXML_GET_STATE(i).parent], (USCXML_ELEM_DONEDATA_IS_SET(donedata) ? donedata : NULL)); + } + + /** + * are we the last final state to leave a parallel state?: + * 1. Gather all parallel states in our ancestor chain + * 2. Find all states for which these parallels are ancestors + * 3. Iterate all active final states and remove their ancestors + * 4. If a state remains, not all children of a parallel are final + */ + for (j = 0; j < USCXML_NUMBER_STATES; j++) { + if unlikely(USCXML_STATE_MASK(ctx->machine->states[j].type) == USCXML_STATE_PARALLEL && + BIT_HAS(j, USCXML_GET_STATE(i).ancestors)) { + bit_clear_all(tmp_states, nr_states_bytes); + for (k = 0; k < USCXML_NUMBER_STATES; k++) { + if unlikely(BIT_HAS(j, ctx->machine->states[k].ancestors) && BIT_HAS(k, ctx->config)) { + if (USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_FINAL) { + bit_and_not(tmp_states, ctx->machine->states[k].ancestors, nr_states_bytes); + } else { + BIT_SET_AT(k, tmp_states); + } + } + } + if unlikely(!bit_has_any(tmp_states, nr_states_bytes)) { + ctx->raise_done_event(ctx, &ctx->machine->states[j], NULL); + } + } + } + + } + + } + } + + return USCXML_ERR_OK; +} + +#define USCXML_NO_STEP_FUNCTION +#endif + diff --git a/test/src/test-c-machine.cpp b/test/src/test-c-machine.cpp index 3bd92aa..2879ef9 100644 --- a/test/src/test-c-machine.cpp +++ b/test/src/test-c-machine.cpp @@ -17,7 +17,7 @@ #endif #ifndef AUTOINCLUDE_TEST -#include "test-c-machine.machine.c" +#include "test-c-machine.scxml.c" #endif #include "uscxml/Convenience.h" @@ -637,27 +637,27 @@ public: Data d; std::stringstream content; - if (data->expr != NULL) { - d = USER_DATA(ctx)->dataModel.getStringAsData(data->expr); -// d = Data(data->expr, Data::INTERPRETED); - } else if (data->content != NULL) { - content << data->content; - d = USER_DATA(ctx)->dataModel.getStringAsData(content.str()); -// d = Data(content.str(), Data::INTERPRETED); - } else if (data->src != NULL) { - URL sourceURL(data->src); - if (USER_DATA(ctx)->baseURL.size() > 0) { - sourceURL.toAbsolute(USER_DATA(ctx)->baseURL); + try { + if (data->expr != NULL) { +// d = USER_DATA(ctx)->dataModel.getStringAsData(data->expr); + d = Data(data->expr, Data::INTERPRETED); + } else if (data->content != NULL) { + content << data->content; + d = USER_DATA(ctx)->dataModel.getStringAsData(content.str()); + // d = Data(content.str(), Data::INTERPRETED); + } else if (data->src != NULL) { + URL sourceURL(data->src); + if (USER_DATA(ctx)->baseURL.size() > 0) { + sourceURL.toAbsolute(USER_DATA(ctx)->baseURL); + } else { + sourceURL.toAbsoluteCwd(); + } + content << sourceURL; + // d = Data(content.str(), Data::INTERPRETED); + d = USER_DATA(ctx)->dataModel.getStringAsData(content.str()); } else { - sourceURL.toAbsoluteCwd(); + d = Data("undefined", Data::INTERPRETED); } - content << sourceURL; -// d = Data(content.str(), Data::INTERPRETED); - d = USER_DATA(ctx)->dataModel.getStringAsData(content.str()); - } else { - d = Data("undefined", Data::INTERPRETED); - } - try { // this might fail with an unquoted string literal in content USER_DATA(ctx)->dataModel.init(data->id, d); } catch (Event e) { @@ -930,7 +930,7 @@ int main(int argc, char** argv) { double avgDm = 0; #endif - StateMachine rootMachine(&uscxml_machines[0]); + StateMachine rootMachine(&USCXML_MACHINE); Timer tTotal; tTotal.start(); diff --git a/test/src/test-c-machine.machine.c b/test/src/test-c-machine.machine.c deleted file mode 100644 index 5ac3994..0000000 --- a/test/src/test-c-machine.machine.c +++ /dev/null @@ -1,1087 +0,0 @@ -/** - Generated from source: - file:///Users/sradomski/Documents/TK/Code/uscxml/test/w3c/lua/test150.scxml -*/ - -#include /* explicit types */ -#include /* NULL */ - -#ifndef USCXML_GEN_C_MACROS - -/** - * All macros used for the scxml types and functions - * - * ** IMPORTANT: Make sure to set the following macros prior to including. ** - * They are used to represent the machine in the types to follow - * and to allocate stack memory during a micro-step function. - * When in doubt, overprovide. - * - * USCXML_NR_STATES_TYPE - * as the smallest type for positive integers that can contain the - * largest number of states from an individual state machine. E.g.: - * < 2^8 states => uint8_t - * < 2^16 states => uint16_t - * < 2^32 states => uint32_t - */ - -#ifndef USCXML_NR_STATES_TYPE -# define USCXML_NR_STATES_TYPE uint8_t -#endif - -/** - * USCXML_NR_TRANS_TYPE - * the same as above but for the number of transitions. - */ - -#ifndef USCXML_NR_TRANS_TYPE -# define USCXML_NR_TRANS_TYPE uint8_t -#endif - -/** - * USCXML_MAX_NR_STATES_BYTES - * the smallest multiple of 8 that, if multiplied by 8, - * is larger than USCXML_NR_STATES_TYPE, e.g: - * 1-8 states => 1 - * 9-16 states => 2 - * 17-24 states => 3 - * 25-32 states => 4 - * ... - */ - -#ifndef USCXML_MAX_NR_STATES_BYTES -# define USCXML_MAX_NR_STATES_BYTES 1 -#endif - -/** - * USCXML_MAX_NR_TRANS_BYTES - * same as above but for transitions. - */ - -#ifndef USCXML_MAX_NR_TRANS_BYTES -# define USCXML_MAX_NR_TRANS_BYTES 1 -#endif - -/** - * USCXML_NUMBER_STATES / USCXML_NUMBER_TRANS - * Per default the number of states / transitions is retrieved from the machine - * info in the uscxml_ctx struct, but you can also hard-code it per macro. - */ - -#ifndef USCXML_NUMBER_STATES -# define USCXML_NUMBER_STATES (ctx->machine->nr_states) -#endif - -#ifndef USCXML_NUMBER_TRANS -# define USCXML_NUMBER_TRANS (ctx->machine->nr_transitions) -#endif - -/** - * USCXML_GET_STATE / USCXML_GET_TRANS - * Per default an individual state or transitions is retrieved from the machine - * info in the uscxml_ctx struct, but you can also hard-code it per macro. - */ - -#ifndef USCXML_GET_STATE -# define USCXML_GET_STATE(i) (ctx->machine->states[i]) -#endif - -#ifndef USCXML_GET_TRANS -# define USCXML_GET_TRANS(i) (ctx->machine->transitions[i]) -#endif - - -/* Common macros below */ - -#define BIT_HAS(idx, bitset) ((bitset[idx >> 3] & (1 << (idx & 7))) != 0) -#define BIT_SET_AT(idx, bitset) bitset[idx >> 3] |= (1 << (idx & 7)); -#define BIT_CLEAR(idx, bitset) bitset[idx >> 3] &= (1 << (idx & 7)) ^ 0xFF; - -#ifdef __GNUC__ -# define likely(x) (__builtin_expect(!!(x), 1)) -# define unlikely(x) (__builtin_expect(!!(x), 0)) -#else -# define likely(x) (x) -# define unlikely(x) (x) -#endif - -/* error return codes */ -#define USCXML_ERR_OK 0 -#define USCXML_ERR_IDLE 1 -#define USCXML_ERR_DONE 2 -#define USCXML_ERR_MISSING_CALLBACK 3 -#define USCXML_ERR_FOREACH_DONE 4 -#define USCXML_ERR_EXEC_CONTENT 5 -#define USCXML_ERR_INVALID_TARGET 6 -#define USCXML_ERR_INVALID_TYPE 7 -#define USCXML_ERR_UNSUPPORTED 8 - -#define USCXML_TRANS_SPONTANEOUS 0x01 -#define USCXML_TRANS_TARGETLESS 0x02 -#define USCXML_TRANS_INTERNAL 0x04 -#define USCXML_TRANS_HISTORY 0x08 -#define USCXML_TRANS_INITIAL 0x10 - -#define USCXML_STATE_ATOMIC 0x01 -#define USCXML_STATE_PARALLEL 0x02 -#define USCXML_STATE_COMPOUND 0x03 -#define USCXML_STATE_FINAL 0x04 -#define USCXML_STATE_HISTORY_DEEP 0x05 -#define USCXML_STATE_HISTORY_SHALLOW 0x06 -#define USCXML_STATE_INITIAL 0x07 -#define USCXML_STATE_HAS_HISTORY 0x80 /* highest bit */ -#define USCXML_STATE_MASK(t) (t & 0x7F) /* mask highest bit */ - -#define USCXML_CTX_PRISTINE 0x00 -#define USCXML_CTX_SPONTANEOUS 0x01 -#define USCXML_CTX_INITIALIZED 0x02 -#define USCXML_CTX_TOP_LEVEL_FINAL 0x04 -#define USCXML_CTX_TRANSITION_FOUND 0x08 -#define USCXML_CTX_FINISHED 0x10 - -#define USCXML_ELEM_DATA_IS_SET(data) (data->id != NULL) -#define USCXML_ELEM_DONEDATA_IS_SET(donedata) (donedata->content != NULL || donedata->contentexpr != NULL || donedata->params != NULL) -#define USCXML_ELEM_PARAM_IS_SET(param) (param->name != NULL) -#define USCXML_MACHINE_IS_SET(machine) (machine->nr_states > 0) - -#define USCXML_GEN_C_MACROS -#endif - - -#ifndef USCXML_GEN_C_TYPES - -/** - * All types required to represent an SCXML state chart. - * Just predefine the USCXML_GEN_C_TYPES macro if you do not need them. - */ - -typedef struct uscxml_machine uscxml_machine; -typedef struct uscxml_transition uscxml_transition; -typedef struct uscxml_state uscxml_state; -typedef struct uscxml_ctx uscxml_ctx; -typedef struct uscxml_elem_invoke uscxml_elem_invoke; - -typedef struct uscxml_elem_send uscxml_elem_send; -typedef struct uscxml_elem_param uscxml_elem_param; -typedef struct uscxml_elem_data uscxml_elem_data; -typedef struct uscxml_elem_donedata uscxml_elem_donedata; -typedef struct uscxml_elem_foreach uscxml_elem_foreach; - -typedef void* (*dequeue_internal_t)(const uscxml_ctx* ctx); -typedef void* (*dequeue_external_t)(const uscxml_ctx* ctx); -typedef int (*is_enabled_t)(const uscxml_ctx* ctx, const uscxml_transition* transition, const void* event); -typedef int (*is_true_t)(const uscxml_ctx* ctx, const char* expr); -typedef int (*exec_content_t)(const uscxml_ctx* ctx, const uscxml_state* state, const void* event); -typedef int (*raise_done_event_t)(const uscxml_ctx* ctx, const uscxml_state* state, const uscxml_elem_donedata* donedata); -typedef int (*invoke_t)(const uscxml_ctx* ctx, const uscxml_state* s, const uscxml_elem_invoke* invocation, unsigned char uninvoke); - -typedef int (*exec_content_log_t)(const uscxml_ctx* ctx, const char* label, const char* expr); -typedef int (*exec_content_raise_t)(const uscxml_ctx* ctx, const char* event); -typedef int (*exec_content_send_t)(const uscxml_ctx* ctx, const uscxml_elem_send* send); -typedef int (*exec_content_foreach_init_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); -typedef int (*exec_content_foreach_next_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); -typedef int (*exec_content_foreach_done_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); -typedef int (*exec_content_assign_t)(const uscxml_ctx* ctx, const char* location, const char* expr); -typedef int (*exec_content_init_t)(const uscxml_ctx* ctx, const uscxml_elem_data* data); -typedef int (*exec_content_cancel_t)(const uscxml_ctx* ctx, const char* sendid, const char* sendidexpr); -typedef int (*exec_content_finalize_t)(const uscxml_ctx* ctx, const uscxml_elem_invoke* invoker, const void* event); -typedef int (*exec_content_script_t)(const uscxml_ctx* ctx, const char* src, const char* content); - -/** - * A single SCXML state-machine. - */ -struct uscxml_machine { - unsigned char flags; /* Unused */ - USCXML_NR_STATES_TYPE nr_states; /* Make sure to set type per macro! */ - USCXML_NR_TRANS_TYPE nr_transitions; /* Make sure to set type per macro! */ - const char* name; - const char* datamodel; - const char* uuid; /* currently MD5 sum */ - const uscxml_state* states; - const uscxml_transition* transitions; - const uscxml_machine* parent; - const uscxml_elem_donedata* donedata; - const exec_content_t script; /* Global script elements */ -}; - -/** - * All information pertaining to a element. - * With late data binding, blocks of data elements are separated by NULL - * use USCXML_ELEM_DATA_IS_SET to test for end of a block. - */ -struct uscxml_elem_data { - const char* id; - const char* src; - const char* expr; - const char* content; -}; - -/** - * All information pertaining to any state element. - */ -struct uscxml_state { - const char* name; /* eventual name */ - const USCXML_NR_STATES_TYPE parent; /* parent */ - const exec_content_t on_entry; /* on entry handlers */ - const exec_content_t on_exit; /* on exit handlers */ - const invoke_t invoke; /* invocations */ - const char children[USCXML_MAX_NR_STATES_BYTES]; /* all children */ - const char completion[USCXML_MAX_NR_STATES_BYTES]; /* default completion */ - const char ancestors[USCXML_MAX_NR_STATES_BYTES]; /* all ancestors */ - const uscxml_elem_data* data; /* data with late binding */ - const unsigned char type; /* One of USCXML_STATE_* */ -}; - -/** - * All information pertaining to a element. - */ -struct uscxml_transition { - const USCXML_NR_STATES_TYPE source; - const char target[USCXML_MAX_NR_STATES_BYTES]; - const char* event; - const char* condition; - const exec_content_t on_transition; - const unsigned char type; - const char conflicts[USCXML_MAX_NR_TRANS_BYTES]; - const char exit_set[USCXML_MAX_NR_STATES_BYTES]; -}; - -/** - * All information pertaining to a element. - */ -struct uscxml_elem_foreach { - const char* array; - const char* item; - const char* index; -}; - -/** - * All information pertaining to a element. - * Blocks of params are separated by NULL params, use - * USCXML_ELEM_PARAM_IS_SET to test for end of a block. - */ -struct uscxml_elem_param { - const char* name; - const char* expr; - const char* location; -}; - -/** - * All information pertaining to a element. - */ -struct uscxml_elem_donedata { - const USCXML_NR_STATES_TYPE source; - const char* content; - const char* contentexpr; - const uscxml_elem_param* params; -}; - -/** - * All information pertaining to an element. - */ -struct uscxml_elem_invoke { - const uscxml_machine* machine; - const char* type; - const char* typeexpr; - const char* src; - const char* srcexpr; - const char* id; - const char* idlocation; - const char* sourcename; - const char* namelist; - const unsigned char autoforward; - const uscxml_elem_param* params; - exec_content_finalize_t finalize; - const char* content; - const char* contentexpr; -}; - -/** - * All information pertaining to a element. - */ -struct uscxml_elem_send { - const char* event; - const char* eventexpr; - const char* target; - const char* targetexpr; - const char* type; - const char* typeexpr; - const char* id; - const char* idlocation; - const char* delay; - const char* delayexpr; - const char* namelist; /* not space-separated, still as in attribute value */ - const char* content; - const char* contentexpr; - const uscxml_elem_param* params; -}; - -/** - * Represents an instance of a state-chart at runtime/ - */ -struct uscxml_ctx { - unsigned char flags; - const uscxml_machine* machine; - - char config[USCXML_MAX_NR_STATES_BYTES]; /* Make sure these macros specify a sufficient size */ - char history[USCXML_MAX_NR_STATES_BYTES]; - char invocations[USCXML_MAX_NR_STATES_BYTES]; - char initialized_data[USCXML_MAX_NR_STATES_BYTES]; - - void* user_data; - void* event; - - dequeue_internal_t dequeue_internal; - dequeue_external_t dequeue_external; - is_enabled_t is_enabled; - is_true_t is_true; - raise_done_event_t raise_done_event; - - exec_content_log_t exec_content_log; - exec_content_raise_t exec_content_raise; - exec_content_send_t exec_content_send; - exec_content_foreach_init_t exec_content_foreach_init; - exec_content_foreach_next_t exec_content_foreach_next; - exec_content_foreach_done_t exec_content_foreach_done; - exec_content_assign_t exec_content_assign; - exec_content_init_t exec_content_init; - exec_content_cancel_t exec_content_cancel; - exec_content_script_t exec_content_script; - - invoke_t invoke; -}; - -#define USCXML_GEN_C_TYPES -#endif - -/* forward declare machines to allow references */ -extern const uscxml_machine uscxml_machines[2]; - -static const uscxml_elem_foreach _uscxml_9FEEFF45_elem_foreachs[2] = { - /* array, item, index */ - { "testvar3", "testvar1", "testvar2" }, - { "testvar3", "testvar4", "testvar5" } -}; - -static const uscxml_elem_data _uscxml_9FEEFF45_elem_datas[4] = { - /* id, src, expr, content */ - { "testvar1", NULL, NULL, NULL }, - { "testvar2", NULL, NULL, NULL }, - { "testvar3", NULL, NULL, "{1,2,3}" }, - { NULL, NULL, NULL, NULL } -}; - -static const uscxml_elem_donedata _uscxml_9FEEFF45_elem_donedatas[1] = { - /* source, content, contentexpr, params */ - { 0, NULL, NULL, NULL } -}; - -static int _uscxml_9FEEFF45_s0_on_entry_0(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { - int err = USCXML_ERR_OK; - if likely(ctx->exec_content_foreach_init != NULL && - ctx->exec_content_foreach_next != NULL && - ctx->exec_content_foreach_done != NULL) { - - if unlikely((ctx->exec_content_foreach_init(ctx, &_uscxml_9FEEFF45_elem_foreachs[0])) != USCXML_ERR_OK) return err; - while (ctx->exec_content_foreach_next(ctx, &_uscxml_9FEEFF45_elem_foreachs[0]) == USCXML_ERR_OK) { - } - if ((ctx->exec_content_foreach_done(ctx, &_uscxml_9FEEFF45_elem_foreachs[0])) != USCXML_ERR_OK) return err; - } else { - return USCXML_ERR_MISSING_CALLBACK; - } - if likely(ctx->exec_content_raise != NULL) { - if unlikely((ctx->exec_content_raise(ctx, "foo")) != USCXML_ERR_OK) return err; - } else { - return USCXML_ERR_MISSING_CALLBACK; - } - return USCXML_ERR_OK; -} - -static int _uscxml_9FEEFF45_s0_on_entry(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { - _uscxml_9FEEFF45_s0_on_entry_0(ctx, state, event); - return USCXML_ERR_OK; -} - -static int _uscxml_9FEEFF45_s1_on_entry_0(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { - int err = USCXML_ERR_OK; - if likely(ctx->exec_content_foreach_init != NULL && - ctx->exec_content_foreach_next != NULL && - ctx->exec_content_foreach_done != NULL) { - - if unlikely((ctx->exec_content_foreach_init(ctx, &_uscxml_9FEEFF45_elem_foreachs[1])) != USCXML_ERR_OK) return err; - while (ctx->exec_content_foreach_next(ctx, &_uscxml_9FEEFF45_elem_foreachs[1]) == USCXML_ERR_OK) { - } - if ((ctx->exec_content_foreach_done(ctx, &_uscxml_9FEEFF45_elem_foreachs[1])) != USCXML_ERR_OK) return err; - } else { - return USCXML_ERR_MISSING_CALLBACK; - } - if likely(ctx->exec_content_raise != NULL) { - if unlikely((ctx->exec_content_raise(ctx, "bar")) != USCXML_ERR_OK) return err; - } else { - return USCXML_ERR_MISSING_CALLBACK; - } - return USCXML_ERR_OK; -} - -static int _uscxml_9FEEFF45_s1_on_entry(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { - _uscxml_9FEEFF45_s1_on_entry_0(ctx, state, event); - return USCXML_ERR_OK; -} - -static const uscxml_state _uscxml_9FEEFF45_states[6] = { - { /* state number 0 */ - /* name */ NULL, - /* parent */ 0, - /* onentry */ NULL, - /* onexit */ NULL, - /* invoke */ NULL, - /* children */ { 0x3e /* 011111 */ }, - /* completion */ { 0x02 /* 010000 */ }, - /* ancestors */ { 0x00 /* 000000 */ }, - /* data */ &_uscxml_9FEEFF45_elem_datas[0], - /* type */ USCXML_STATE_COMPOUND, - }, - { /* state number 1 */ - /* name */ "s0", - /* parent */ 0, - /* onentry */ _uscxml_9FEEFF45_s0_on_entry, - /* onexit */ NULL, - /* invoke */ NULL, - /* children */ { 0x00 /* 000000 */ }, - /* completion */ { 0x00 /* 000000 */ }, - /* ancestors */ { 0x01 /* 100000 */ }, - /* data */ NULL, - /* type */ USCXML_STATE_ATOMIC, - }, - { /* state number 2 */ - /* name */ "s1", - /* parent */ 0, - /* onentry */ _uscxml_9FEEFF45_s1_on_entry, - /* onexit */ NULL, - /* invoke */ NULL, - /* children */ { 0x00 /* 000000 */ }, - /* completion */ { 0x00 /* 000000 */ }, - /* ancestors */ { 0x01 /* 100000 */ }, - /* data */ NULL, - /* type */ USCXML_STATE_ATOMIC, - }, - { /* state number 3 */ - /* name */ "s2", - /* parent */ 0, - /* onentry */ NULL, - /* onexit */ NULL, - /* invoke */ NULL, - /* children */ { 0x00 /* 000000 */ }, - /* completion */ { 0x00 /* 000000 */ }, - /* ancestors */ { 0x01 /* 100000 */ }, - /* data */ NULL, - /* type */ USCXML_STATE_ATOMIC, - }, - { /* state number 4 */ - /* name */ "pass", - /* parent */ 0, - /* onentry */ NULL, - /* onexit */ NULL, - /* invoke */ NULL, - /* children */ { 0x00 /* 000000 */ }, - /* completion */ { 0x00 /* 000000 */ }, - /* ancestors */ { 0x01 /* 100000 */ }, - /* data */ NULL, - /* type */ USCXML_STATE_FINAL, - }, - { /* state number 5 */ - /* name */ "fail", - /* parent */ 0, - /* onentry */ NULL, - /* onexit */ NULL, - /* invoke */ NULL, - /* children */ { 0x00 /* 000000 */ }, - /* completion */ { 0x00 /* 000000 */ }, - /* ancestors */ { 0x01 /* 100000 */ }, - /* data */ NULL, - /* type */ USCXML_STATE_FINAL, - } -}; - -static const uscxml_transition _uscxml_9FEEFF45_transitions[6] = { - { /* transition number 0 with priority 0 - target: fail - */ - /* source */ 1, - /* target */ { 0x20 /* 000001 */ }, - /* event */ "error", - /* condition */ NULL, - /* ontrans */ NULL, - /* type */ 0, - /* conflicts */ { 0x3f /* 111111 */ }, - /* exit set */ { 0x3e /* 011111 */ } - }, - { /* transition number 1 with priority 1 - target: s1 - */ - /* source */ 1, - /* target */ { 0x04 /* 001000 */ }, - /* event */ "*", - /* condition */ NULL, - /* ontrans */ NULL, - /* type */ 0, - /* conflicts */ { 0x3f /* 111111 */ }, - /* exit set */ { 0x3e /* 011111 */ } - }, - { /* transition number 2 with priority 2 - target: fail - */ - /* source */ 2, - /* target */ { 0x20 /* 000001 */ }, - /* event */ "error", - /* condition */ NULL, - /* ontrans */ NULL, - /* type */ 0, - /* conflicts */ { 0x3f /* 111111 */ }, - /* exit set */ { 0x3e /* 011111 */ } - }, - { /* transition number 3 with priority 3 - target: s2 - */ - /* source */ 2, - /* target */ { 0x08 /* 000100 */ }, - /* event */ "*", - /* condition */ NULL, - /* ontrans */ NULL, - /* type */ 0, - /* conflicts */ { 0x3f /* 111111 */ }, - /* exit set */ { 0x3e /* 011111 */ } - }, - { /* transition number 4 with priority 4 - target: pass - */ - /* source */ 3, - /* target */ { 0x10 /* 000010 */ }, - /* event */ NULL, - /* condition */ "testvar4 ~= nil", - /* ontrans */ NULL, - /* type */ USCXML_TRANS_SPONTANEOUS, - /* conflicts */ { 0x3f /* 111111 */ }, - /* exit set */ { 0x3e /* 011111 */ } - }, - { /* transition number 5 with priority 5 - target: fail - */ - /* source */ 3, - /* target */ { 0x20 /* 000001 */ }, - /* event */ NULL, - /* condition */ NULL, - /* ontrans */ NULL, - /* type */ USCXML_TRANS_SPONTANEOUS, - /* conflicts */ { 0x3f /* 111111 */ }, - /* exit set */ { 0x3e /* 011111 */ } - } -}; - -const uscxml_machine uscxml_machines[2] = { - { - /* flags */ 0, - /* nr_states */ 6, - /* nr_transitions */ 6, - /* name */ "", - /* datamodel */ "lua", - /* uuid */ "9FEEFF45D10C557438897A21612B7382", - /* states */ &_uscxml_9FEEFF45_states[0], - /* transitions */ &_uscxml_9FEEFF45_transitions[0], - /* parent */ NULL, - /* donedata */ &_uscxml_9FEEFF45_elem_donedatas[0], - /* script */ NULL - }, - {0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } -}; - -#ifdef USCXML_VERBOSE -/** - * Print name of states contained in a (debugging). - */ -static void printStateNames(const uscxml_ctx* ctx, const char* a, size_t length) { - size_t i; - const char* seperator = ""; - for (i = 0; i < length; i++) { - if (BIT_HAS(i, a)) { - printf("%s%s", seperator, (USCXML_GET_STATE(i).name != NULL ? USCXML_GET_STATE(i).name : "UNK")); - seperator = ", "; - } - } - printf("\n"); -} - -/** - * Print bits set in a in a binary representation (debugging). - */ -static void printBitsetIndices(const char* a, size_t length) { - size_t i; - const char* seperator = ""; - for (i = 0; i < length; i++) { - if (BIT_HAS(i, a)) { - printf("%s%lu", seperator, i); - seperator = ", "; - } - } - printf("\n"); -} -#endif - -#ifndef USCXML_BIT_OPERATIONS -/** - * Return true if there is a common bit in a and b. - */ -static int bit_has_and(const char* a, const char* b, size_t i) { - while(i--) { - if (a[i] & b[i]) - return 1; - } - return 0; -} - -/** - * Set all bits to 0, this corresponds to memset(a, 0, i), - * but does not require string.h or cstring. - */ -static void bit_clear_all(char* a, size_t i) { - while(i--) { - a[i] = 0; - } -} - -/** - * Return true if there is any bit set in a. - */ -static int bit_has_any(const char* a, size_t i) { - while(i--) { - if (a[i] > 0) - return 1; - } - return 0; -} - -/** - * Set all bits from given mask in dest, this is |= for bit arrays. - */ -static void bit_or(char* dest, const char* mask, size_t i) { - while(i--) { - dest[i] |= mask[i]; - } -} - -/** - * Copy all bits from source to dest, this corresponds to memcpy(a, b, i), - * but does not require string.h or cstring. - */ -static void bit_copy(char* dest, const char* source, size_t i) { - while(i--) { - dest[i] = source[i]; - } -} - -/** - * Unset bits from mask in dest. - */ -static void bit_and_not(char* dest, const char* mask, size_t i) { - while(i--) { - dest[i] &= ~mask[i]; - } -} - -/** - * Set bits from mask in dest. - */ -static void bit_and(char* dest, const char* mask, size_t i) { - while(i--) { - dest[i] &= mask[i]; - }; -} - -#define USCXML_BIT_OPERATIONS -#endif - -#ifndef USCXML_STEP_FUNCTION -int uscxml_step(uscxml_ctx* ctx) { - - USCXML_NR_TRANS_TYPE i, j, k; - USCXML_NR_STATES_TYPE nr_states_bytes = ((USCXML_NUMBER_STATES + 7) & ~7) >> 3; - USCXML_NR_TRANS_TYPE nr_trans_bytes = ((USCXML_NUMBER_TRANS + 7) & ~7) >> 3; - int err = USCXML_ERR_OK; - char conflicts [USCXML_MAX_NR_TRANS_BYTES]; - char trans_set [USCXML_MAX_NR_TRANS_BYTES]; - char target_set [USCXML_MAX_NR_STATES_BYTES]; - char exit_set [USCXML_MAX_NR_STATES_BYTES]; - char entry_set [USCXML_MAX_NR_STATES_BYTES]; - char tmp_states [USCXML_MAX_NR_STATES_BYTES]; - -#ifdef USCXML_VERBOSE - printf("Config: "); - printStateNames(ctx, ctx->config, USCXML_NUMBER_STATES); -#endif - - if (ctx->flags & USCXML_CTX_FINISHED) - return USCXML_ERR_DONE; - - if (ctx->flags & USCXML_CTX_TOP_LEVEL_FINAL) { - /* exit all remaining states */ - i = USCXML_NUMBER_STATES; - while(i-- > 0) { - if (BIT_HAS(i, ctx->config)) { - /* call all on exit handlers */ - if (USCXML_GET_STATE(i).on_exit != NULL) { - if unlikely((err = USCXML_GET_STATE(i).on_exit(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) - return err; - } - } - if (BIT_HAS(i, ctx->invocations)) { - if (USCXML_GET_STATE(i).invoke != NULL) - USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 1); - BIT_CLEAR(i, ctx->invocations); - } - } - ctx->flags |= USCXML_CTX_FINISHED; - return USCXML_ERR_DONE; - } - - bit_clear_all(target_set, nr_states_bytes); - bit_clear_all(trans_set, nr_trans_bytes); - if unlikely(ctx->flags == USCXML_CTX_PRISTINE) { - if (ctx->machine->script != NULL) - ctx->machine->script(ctx, &ctx->machine->states[0], NULL); - bit_or(target_set, ctx->machine->states[0].completion, nr_states_bytes); - ctx->flags |= USCXML_CTX_SPONTANEOUS | USCXML_CTX_INITIALIZED; - goto ESTABLISH_ENTRY_SET; - } - - if (ctx->flags & USCXML_CTX_SPONTANEOUS) { - ctx->event = NULL; - goto SELECT_TRANSITIONS; - } - if ((ctx->event = ctx->dequeue_internal(ctx)) != NULL) { - goto SELECT_TRANSITIONS; - } - - /* manage invocations */ - for (i = 0; i < USCXML_NUMBER_STATES; i++) { - /* uninvoke */ - if (!BIT_HAS(i, ctx->config) && BIT_HAS(i, ctx->invocations)) { - if (USCXML_GET_STATE(i).invoke != NULL) - USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 1); - BIT_CLEAR(i, ctx->invocations) - } - /* invoke */ - if (BIT_HAS(i, ctx->config) && !BIT_HAS(i, ctx->invocations)) { - if (USCXML_GET_STATE(i).invoke != NULL) - USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 0); - BIT_SET_AT(i, ctx->invocations) - } - } - - if ((ctx->event = ctx->dequeue_external(ctx)) != NULL) { - goto SELECT_TRANSITIONS; - } - -SELECT_TRANSITIONS: - bit_clear_all(conflicts, nr_trans_bytes); - bit_clear_all(exit_set, nr_states_bytes); - for (i = 0; i < USCXML_NUMBER_TRANS; i++) { - /* never select history or initial transitions automatically */ - if unlikely(USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) - continue; - - /* is the transition active? */ - if (BIT_HAS(USCXML_GET_TRANS(i).source, ctx->config)) { - /* is it non-conflicting? */ - if (!BIT_HAS(i, conflicts)) { - /* is it enabled? */ - if (ctx->is_enabled(ctx, &USCXML_GET_TRANS(i), ctx->event) > 0) { - /* remember that we found a transition */ - ctx->flags |= USCXML_CTX_TRANSITION_FOUND; - - /* transitions that are pre-empted */ - bit_or(conflicts, USCXML_GET_TRANS(i).conflicts, nr_trans_bytes); - - /* states that are directly targeted (resolve as entry-set later) */ - bit_or(target_set, USCXML_GET_TRANS(i).target, nr_states_bytes); - - /* states that will be left */ - bit_or(exit_set, USCXML_GET_TRANS(i).exit_set, nr_states_bytes); - - BIT_SET_AT(i, trans_set); - } - } - } - } - bit_and(exit_set, ctx->config, nr_states_bytes); - - if (ctx->flags & USCXML_CTX_TRANSITION_FOUND) { - ctx->flags |= USCXML_CTX_SPONTANEOUS; - ctx->flags &= ~USCXML_CTX_TRANSITION_FOUND; - } else { - ctx->flags &= ~USCXML_CTX_SPONTANEOUS; - } - -#ifdef USCXML_VERBOSE - printf("Targets: "); - printStateNames(ctx, target_set, USCXML_NUMBER_STATES); -#endif - -#ifdef USCXML_VERBOSE - printf("Exiting: "); - printStateNames(ctx, exit_set, USCXML_NUMBER_STATES); -#endif - -#ifdef USCXML_VERBOSE - printf("History: "); - printStateNames(ctx, ctx->history, USCXML_NUMBER_STATES); -#endif - -/* REMEMBER_HISTORY: */ - for (i = 0; i < USCXML_NUMBER_STATES; i++) { - if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW || - USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP) { - /* a history state whose parent is about to be exited */ - if unlikely(BIT_HAS(USCXML_GET_STATE(i).parent, exit_set)) { - bit_copy(tmp_states, USCXML_GET_STATE(i).completion, nr_states_bytes); - - /* set those states who were enabled */ - bit_and(tmp_states, ctx->config, nr_states_bytes); - - /* clear current history with completion mask */ - bit_and_not(ctx->history, USCXML_GET_STATE(i).completion, nr_states_bytes); - - /* set history */ - bit_or(ctx->history, tmp_states, nr_states_bytes); - } - } - } - -ESTABLISH_ENTRY_SET: - /* calculate new entry set */ - bit_copy(entry_set, target_set, nr_states_bytes); - - /* iterate for ancestors */ - for (i = 0; i < USCXML_NUMBER_STATES; i++) { - if (BIT_HAS(i, entry_set)) { - bit_or(entry_set, USCXML_GET_STATE(i).ancestors, nr_states_bytes); - } - } - - /* iterate for descendants */ - for (i = 0; i < USCXML_NUMBER_STATES; i++) { - if (BIT_HAS(i, entry_set)) { - switch (USCXML_STATE_MASK(USCXML_GET_STATE(i).type)) { - case USCXML_STATE_PARALLEL: { - bit_or(entry_set, USCXML_GET_STATE(i).completion, nr_states_bytes); - break; - } - case USCXML_STATE_HISTORY_SHALLOW: - case USCXML_STATE_HISTORY_DEEP: { - if (!bit_has_and(USCXML_GET_STATE(i).completion, ctx->history, nr_states_bytes) && - !BIT_HAS(USCXML_GET_STATE(i).parent, ctx->config)) { - /* nothing set for history, look for a default transition */ - for (j = 0; j < USCXML_NUMBER_TRANS; j++) { - if unlikely(ctx->machine->transitions[j].source == i) { - bit_or(entry_set, ctx->machine->transitions[j].target, nr_states_bytes); - if(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP && - !bit_has_and(ctx->machine->transitions[j].target, USCXML_GET_STATE(i).children, nr_states_bytes)) { - for (k = i + 1; k < USCXML_NUMBER_STATES; k++) { - if (BIT_HAS(k, ctx->machine->transitions[j].target)) { - bit_or(entry_set, ctx->machine->states[k].ancestors, nr_states_bytes); - break; - } - } - } - BIT_SET_AT(j, trans_set); - break; - } - /* Note: SCXML mandates every history to have a transition! */ - } - } else { - bit_copy(tmp_states, USCXML_GET_STATE(i).completion, nr_states_bytes); - bit_and(tmp_states, ctx->history, nr_states_bytes); - bit_or(entry_set, tmp_states, nr_states_bytes); - if (USCXML_GET_STATE(i).type == (USCXML_STATE_HAS_HISTORY | USCXML_STATE_HISTORY_DEEP)) { - /* a deep history state with nested histories -> more completion */ - for (j = i + 1; j < USCXML_NUMBER_STATES; j++) { - if (BIT_HAS(j, USCXML_GET_STATE(i).completion) && - BIT_HAS(j, entry_set) && - (ctx->machine->states[j].type & USCXML_STATE_HAS_HISTORY)) { - for (k = j + 1; k < USCXML_NUMBER_STATES; k++) { - /* add nested history to entry_set */ - if ((USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_HISTORY_DEEP || - USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_HISTORY_SHALLOW) && - BIT_HAS(k, ctx->machine->states[j].children)) { - /* a nested history state */ - BIT_SET_AT(k, entry_set); - } - } - } - } - } - } - break; - } - case USCXML_STATE_INITIAL: { - for (j = 0; j < USCXML_NUMBER_TRANS; j++) { - if (ctx->machine->transitions[j].source == i) { - BIT_SET_AT(j, trans_set); - BIT_CLEAR(i, entry_set); - bit_or(entry_set, ctx->machine->transitions[j].target, nr_states_bytes); - for (k = i + 1; k < USCXML_NUMBER_STATES; k++) { - if (BIT_HAS(k, ctx->machine->transitions[j].target)) { - bit_or(entry_set, ctx->machine->states[k].ancestors, nr_states_bytes); - } - } - } - } - break; - } - case USCXML_STATE_COMPOUND: { /* we need to check whether one child is already in entry_set */ - if (!bit_has_and(entry_set, USCXML_GET_STATE(i).children, nr_states_bytes) && - (!bit_has_and(ctx->config, USCXML_GET_STATE(i).children, nr_states_bytes) || - bit_has_and(exit_set, USCXML_GET_STATE(i).children, nr_states_bytes))) - { - bit_or(entry_set, USCXML_GET_STATE(i).completion, nr_states_bytes); - if (!bit_has_and(USCXML_GET_STATE(i).completion, USCXML_GET_STATE(i).children, nr_states_bytes)) { - /* deep completion */ - for (j = i + 1; j < USCXML_NUMBER_STATES; j++) { - if (BIT_HAS(j, USCXML_GET_STATE(i).completion)) { - bit_or(entry_set, ctx->machine->states[j].ancestors, nr_states_bytes); - break; /* completion of compound is single state */ - } - } - } - } - break; - } - } - } - } - -#ifdef USCXML_VERBOSE - printf("Transitions: "); - printBitsetIndices(trans_set, sizeof(char) * 8 * nr_trans_bytes); -#endif - -/* EXIT_STATES: */ - i = USCXML_NUMBER_STATES; - while(i-- > 0) { - if (BIT_HAS(i, exit_set) && BIT_HAS(i, ctx->config)) { - /* call all on exit handlers */ - if (USCXML_GET_STATE(i).on_exit != NULL) { - if unlikely((err = USCXML_GET_STATE(i).on_exit(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) - return err; - } - BIT_CLEAR(i, ctx->config); - } - } - -/* TAKE_TRANSITIONS: */ - for (i = 0; i < USCXML_NUMBER_TRANS; i++) { - if (BIT_HAS(i, trans_set) && (USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { - /* call executable content in transition */ - if (USCXML_GET_TRANS(i).on_transition != NULL) { - if unlikely((err = USCXML_GET_TRANS(i).on_transition(ctx, - &ctx->machine->states[USCXML_GET_TRANS(i).source], - ctx->event)) != USCXML_ERR_OK) - return err; - } - } - } - -#ifdef USCXML_VERBOSE - printf("Entering: "); - printStateNames(ctx, entry_set, USCXML_NUMBER_STATES); -#endif - -/* ENTER_STATES: */ - for (i = 0; i < USCXML_NUMBER_STATES; i++) { - if (BIT_HAS(i, entry_set) && !BIT_HAS(i, ctx->config)) { - /* these are no proper states */ - if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP || - USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW || - USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_INITIAL) - continue; - - BIT_SET_AT(i, ctx->config); - - /* initialize data */ - if (!BIT_HAS(i, ctx->initialized_data)) { - if unlikely(USCXML_GET_STATE(i).data != NULL && ctx->exec_content_init != NULL) { - ctx->exec_content_init(ctx, USCXML_GET_STATE(i).data); - } - BIT_SET_AT(i, ctx->initialized_data); - } - - if (USCXML_GET_STATE(i).on_entry != NULL) { - if unlikely((err = USCXML_GET_STATE(i).on_entry(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) - return err; - } - - /* take history and initial transitions */ - for (j = 0; j < USCXML_NUMBER_TRANS; j++) { - if unlikely(BIT_HAS(j, trans_set) && - (ctx->machine->transitions[j].type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) && - ctx->machine->states[ctx->machine->transitions[j].source].parent == i) { - /* call executable content in transition */ - if (ctx->machine->transitions[j].on_transition != NULL) { - if unlikely((err = ctx->machine->transitions[j].on_transition(ctx, - &USCXML_GET_STATE(i), - ctx->event)) != USCXML_ERR_OK) - return err; - } - } - } - - /* handle final states */ - if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_FINAL) { - if unlikely(USCXML_GET_STATE(i).ancestors[0] == 0x01) { - ctx->flags |= USCXML_CTX_TOP_LEVEL_FINAL; - } else { - /* raise done event */ - const uscxml_elem_donedata* donedata = &ctx->machine->donedata[0]; - while(USCXML_ELEM_DONEDATA_IS_SET(donedata)) { - if unlikely(donedata->source == i) - break; - donedata++; - } - ctx->raise_done_event(ctx, &ctx->machine->states[USCXML_GET_STATE(i).parent], (USCXML_ELEM_DONEDATA_IS_SET(donedata) ? donedata : NULL)); - } - - /** - * are we the last final state to leave a parallel state?: - * 1. Gather all parallel states in our ancestor chain - * 2. Find all states for which these parallels are ancestors - * 3. Iterate all active final states and remove their ancestors - * 4. If a state remains, not all children of a parallel are final - */ - for (j = 0; j < USCXML_NUMBER_STATES; j++) { - if unlikely(USCXML_STATE_MASK(ctx->machine->states[j].type) == USCXML_STATE_PARALLEL && - BIT_HAS(j, USCXML_GET_STATE(i).ancestors)) { - bit_clear_all(tmp_states, nr_states_bytes); - for (k = 0; k < USCXML_NUMBER_STATES; k++) { - if unlikely(BIT_HAS(j, ctx->machine->states[k].ancestors) && BIT_HAS(k, ctx->config)) { - if (USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_FINAL) { - bit_and_not(tmp_states, ctx->machine->states[k].ancestors, nr_states_bytes); - } else { - BIT_SET_AT(k, tmp_states); - } - } - } - if unlikely(!bit_has_any(tmp_states, nr_states_bytes)) { - ctx->raise_done_event(ctx, &ctx->machine->states[j], NULL); - } - } - } - - } - - } - } - - return USCXML_ERR_OK; -} - -#define USCXML_STEP_FUNCTION -#endif - diff --git a/test/src/test-c-machine.scxml.c b/test/src/test-c-machine.scxml.c new file mode 100644 index 0000000..cbbfbfe --- /dev/null +++ b/test/src/test-c-machine.scxml.c @@ -0,0 +1,1131 @@ +/** + Generated from source: + file:///Users/sradomski/Documents/TK/Code/uscxml/test/w3c/ecma/test326.scxml +*/ + +#ifndef USCXML_NO_STDTYPES_H +# include /* explicit types */ +#endif +#include /* NULL */ + +#ifndef USCXML_NO_GEN_C_MACROS + +/** + * All macros used for the scxml types and functions + * + * ** IMPORTANT: Make sure to set the following macros prior to including. ** + * They are used to represent the machine in the types to follow + * and to allocate stack memory during a micro-step function. + * When in doubt, overprovide. + * + * USCXML_NR_STATES_TYPE + * as the smallest type for positive integers that can contain the + * largest number of states from an individual state machine. E.g.: + * < 2^8 states => uint8_t + * < 2^16 states => uint16_t + * < 2^32 states => uint32_t + */ + +#ifndef USCXML_NR_STATES_TYPE +# define USCXML_NR_STATES_TYPE uint8_t +#endif + +/** + * USCXML_NR_TRANS_TYPE + * the same as above but for the number of transitions. + */ + +#ifndef USCXML_NR_TRANS_TYPE +# define USCXML_NR_TRANS_TYPE uint8_t +#endif + +/** + * USCXML_MAX_NR_STATES_BYTES + * the smallest multiple of 8 that, if multiplied by 8, + * is larger than USCXML_NR_STATES_TYPE, e.g: + * 1-8 states => 1 + * 9-16 states => 2 + * 17-24 states => 3 + * 25-32 states => 4 + * ... + */ + +#ifndef USCXML_MAX_NR_STATES_BYTES +# define USCXML_MAX_NR_STATES_BYTES 1 +#endif + +/** + * USCXML_MAX_NR_TRANS_BYTES + * same as above but for transitions. + */ + +#ifndef USCXML_MAX_NR_TRANS_BYTES +# define USCXML_MAX_NR_TRANS_BYTES 1 +#endif + +/** + * USCXML_NUMBER_STATES / USCXML_NUMBER_TRANS + * Per default the number of states / transitions is retrieved from the machine + * info in the uscxml_ctx struct, but you can also hard-code it per macro. + */ + +#ifndef USCXML_NUMBER_STATES +# define USCXML_NUMBER_STATES (ctx->machine->nr_states) +#endif + +#ifndef USCXML_NUMBER_TRANS +# define USCXML_NUMBER_TRANS (ctx->machine->nr_transitions) +#endif + +/** + * USCXML_GET_STATE / USCXML_GET_TRANS + * Per default an individual state or transitions is retrieved from the machine + * info in the uscxml_ctx struct, but you can also hard-code it per macro. + */ + +#ifndef USCXML_GET_STATE +# define USCXML_GET_STATE(i) (ctx->machine->states[i]) +#endif + +#ifndef USCXML_GET_TRANS +# define USCXML_GET_TRANS(i) (ctx->machine->transitions[i]) +#endif + + +/* Common macros below */ + +#define BIT_HAS(idx, bitset) ((bitset[idx >> 3] & (1 << (idx & 7))) != 0) +#define BIT_SET_AT(idx, bitset) bitset[idx >> 3] |= (1 << (idx & 7)); +#define BIT_CLEAR(idx, bitset) bitset[idx >> 3] &= (1 << (idx & 7)) ^ 0xFF; + +#ifdef __GNUC__ +# define likely(x) (__builtin_expect(!!(x), 1)) +# define unlikely(x) (__builtin_expect(!!(x), 0)) +#else +# define likely(x) (x) +# define unlikely(x) (x) +#endif + +/* error return codes */ +#define USCXML_ERR_OK 0 +#define USCXML_ERR_IDLE 1 +#define USCXML_ERR_DONE 2 +#define USCXML_ERR_MISSING_CALLBACK 3 +#define USCXML_ERR_FOREACH_DONE 4 +#define USCXML_ERR_EXEC_CONTENT 5 +#define USCXML_ERR_INVALID_TARGET 6 +#define USCXML_ERR_INVALID_TYPE 7 +#define USCXML_ERR_UNSUPPORTED 8 + +#define USCXML_TRANS_SPONTANEOUS 0x01 +#define USCXML_TRANS_TARGETLESS 0x02 +#define USCXML_TRANS_INTERNAL 0x04 +#define USCXML_TRANS_HISTORY 0x08 +#define USCXML_TRANS_INITIAL 0x10 + +#define USCXML_STATE_ATOMIC 0x01 +#define USCXML_STATE_PARALLEL 0x02 +#define USCXML_STATE_COMPOUND 0x03 +#define USCXML_STATE_FINAL 0x04 +#define USCXML_STATE_HISTORY_DEEP 0x05 +#define USCXML_STATE_HISTORY_SHALLOW 0x06 +#define USCXML_STATE_INITIAL 0x07 +#define USCXML_STATE_HAS_HISTORY 0x80 /* highest bit */ +#define USCXML_STATE_MASK(t) (t & 0x7F) /* mask highest bit */ + +#define USCXML_CTX_PRISTINE 0x00 +#define USCXML_CTX_SPONTANEOUS 0x01 +#define USCXML_CTX_INITIALIZED 0x02 +#define USCXML_CTX_TOP_LEVEL_FINAL 0x04 +#define USCXML_CTX_TRANSITION_FOUND 0x08 +#define USCXML_CTX_FINISHED 0x10 + +#define USCXML_ELEM_DATA_IS_SET(data) (data->id != NULL) +#define USCXML_ELEM_DONEDATA_IS_SET(donedata) (donedata->content != NULL || donedata->contentexpr != NULL || donedata->params != NULL) +#define USCXML_ELEM_PARAM_IS_SET(param) (param->name != NULL) +#define USCXML_MACHINE_IS_SET(machine) (machine->nr_states > 0) + +#define USCXML_NO_GEN_C_MACROS +#endif + + +#ifndef USCXML_NO_GEN_C_TYPES + +/** + * All types required to represent an SCXML state chart. + * Just predefine the USCXML_NO_GEN_C_TYPES macro if you do not need them. + */ + +typedef struct uscxml_machine uscxml_machine; +typedef struct uscxml_transition uscxml_transition; +typedef struct uscxml_state uscxml_state; +typedef struct uscxml_ctx uscxml_ctx; +typedef struct uscxml_elem_invoke uscxml_elem_invoke; + +typedef struct uscxml_elem_send uscxml_elem_send; +typedef struct uscxml_elem_param uscxml_elem_param; +typedef struct uscxml_elem_data uscxml_elem_data; +typedef struct uscxml_elem_donedata uscxml_elem_donedata; +typedef struct uscxml_elem_foreach uscxml_elem_foreach; + +typedef void* (*dequeue_internal_t)(const uscxml_ctx* ctx); +typedef void* (*dequeue_external_t)(const uscxml_ctx* ctx); +typedef int (*is_enabled_t)(const uscxml_ctx* ctx, const uscxml_transition* transition, const void* event); +typedef int (*is_true_t)(const uscxml_ctx* ctx, const char* expr); +typedef int (*exec_content_t)(const uscxml_ctx* ctx, const uscxml_state* state, const void* event); +typedef int (*raise_done_event_t)(const uscxml_ctx* ctx, const uscxml_state* state, const uscxml_elem_donedata* donedata); +typedef int (*invoke_t)(const uscxml_ctx* ctx, const uscxml_state* s, const uscxml_elem_invoke* invocation, unsigned char uninvoke); + +typedef int (*exec_content_log_t)(const uscxml_ctx* ctx, const char* label, const char* expr); +typedef int (*exec_content_raise_t)(const uscxml_ctx* ctx, const char* event); +typedef int (*exec_content_send_t)(const uscxml_ctx* ctx, const uscxml_elem_send* send); +typedef int (*exec_content_foreach_init_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); +typedef int (*exec_content_foreach_next_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); +typedef int (*exec_content_foreach_done_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach); +typedef int (*exec_content_assign_t)(const uscxml_ctx* ctx, const char* location, const char* expr); +typedef int (*exec_content_init_t)(const uscxml_ctx* ctx, const uscxml_elem_data* data); +typedef int (*exec_content_cancel_t)(const uscxml_ctx* ctx, const char* sendid, const char* sendidexpr); +typedef int (*exec_content_finalize_t)(const uscxml_ctx* ctx, const uscxml_elem_invoke* invoker, const void* event); +typedef int (*exec_content_script_t)(const uscxml_ctx* ctx, const char* src, const char* content); + +/** + * A single SCXML state-machine. + */ +struct uscxml_machine { + unsigned char flags; /* Unused */ + USCXML_NR_STATES_TYPE nr_states; /* Make sure to set type per macro! */ + USCXML_NR_TRANS_TYPE nr_transitions; /* Make sure to set type per macro! */ + const char* name; + const char* datamodel; + const char* uuid; /* currently MD5 sum */ + const uscxml_state* states; + const uscxml_transition* transitions; + const uscxml_machine* parent; + const uscxml_elem_donedata* donedata; + const exec_content_t script; /* Global script elements */ +}; + +/** + * All information pertaining to a element. + * With late data binding, blocks of data elements are separated by NULL + * use USCXML_ELEM_DATA_IS_SET to test for end of a block. + */ +struct uscxml_elem_data { + const char* id; + const char* src; + const char* expr; + const char* content; +}; + +/** + * All information pertaining to any state element. + */ +struct uscxml_state { + const char* name; /* eventual name */ + const USCXML_NR_STATES_TYPE parent; /* parent */ + const exec_content_t on_entry; /* on entry handlers */ + const exec_content_t on_exit; /* on exit handlers */ + const invoke_t invoke; /* invocations */ + const char children[USCXML_MAX_NR_STATES_BYTES]; /* all children */ + const char completion[USCXML_MAX_NR_STATES_BYTES]; /* default completion */ + const char ancestors[USCXML_MAX_NR_STATES_BYTES]; /* all ancestors */ + const uscxml_elem_data* data; /* data with late binding */ + const unsigned char type; /* One of USCXML_STATE_* */ +}; + +/** + * All information pertaining to a element. + */ +struct uscxml_transition { + const USCXML_NR_STATES_TYPE source; + const char target[USCXML_MAX_NR_STATES_BYTES]; + const char* event; + const char* condition; + const exec_content_t on_transition; + const unsigned char type; + const char conflicts[USCXML_MAX_NR_TRANS_BYTES]; + const char exit_set[USCXML_MAX_NR_STATES_BYTES]; +}; + +/** + * All information pertaining to a element. + */ +struct uscxml_elem_foreach { + const char* array; + const char* item; + const char* index; +}; + +/** + * All information pertaining to a element. + * Blocks of params are separated by NULL params, use + * USCXML_ELEM_PARAM_IS_SET to test for end of a block. + */ +struct uscxml_elem_param { + const char* name; + const char* expr; + const char* location; +}; + +/** + * All information pertaining to a element. + */ +struct uscxml_elem_donedata { + const USCXML_NR_STATES_TYPE source; + const char* content; + const char* contentexpr; + const uscxml_elem_param* params; +}; + +/** + * All information pertaining to an element. + */ +struct uscxml_elem_invoke { + const uscxml_machine* machine; + const char* type; + const char* typeexpr; + const char* src; + const char* srcexpr; + const char* id; + const char* idlocation; + const char* sourcename; + const char* namelist; + const unsigned char autoforward; + const uscxml_elem_param* params; + exec_content_finalize_t finalize; + const char* content; + const char* contentexpr; +}; + +/** + * All information pertaining to a element. + */ +struct uscxml_elem_send { + const char* event; + const char* eventexpr; + const char* target; + const char* targetexpr; + const char* type; + const char* typeexpr; + const char* id; + const char* idlocation; + const char* delay; + const char* delayexpr; + const char* namelist; /* not space-separated, still as in attribute value */ + const char* content; + const char* contentexpr; + const uscxml_elem_param* params; +}; + +/** + * Represents an instance of a state-chart at runtime/ + */ +struct uscxml_ctx { + unsigned char flags; + const uscxml_machine* machine; + + char config[USCXML_MAX_NR_STATES_BYTES]; /* Make sure these macros specify a sufficient size */ + char history[USCXML_MAX_NR_STATES_BYTES]; + char invocations[USCXML_MAX_NR_STATES_BYTES]; + char initialized_data[USCXML_MAX_NR_STATES_BYTES]; + + void* user_data; + void* event; + + dequeue_internal_t dequeue_internal; + dequeue_external_t dequeue_external; + is_enabled_t is_enabled; + is_true_t is_true; + raise_done_event_t raise_done_event; + + exec_content_log_t exec_content_log; + exec_content_raise_t exec_content_raise; + exec_content_send_t exec_content_send; + exec_content_foreach_init_t exec_content_foreach_init; + exec_content_foreach_next_t exec_content_foreach_next; + exec_content_foreach_done_t exec_content_foreach_done; + exec_content_assign_t exec_content_assign; + exec_content_init_t exec_content_init; + exec_content_cancel_t exec_content_cancel; + exec_content_script_t exec_content_script; + + invoke_t invoke; +}; + +#define USCXML_NO_GEN_C_TYPES +#endif + +/* forward declare machines to allow references */ +extern const uscxml_machine _uscxml_EC83C2A5_machine; + +#ifndef USCXML_NO_ELEM_INFO + +static const uscxml_elem_data _uscxml_EC83C2A5_elem_datas[3] = { + /* id, src, expr, content */ + { "Var1", NULL, "_ioprocessors", NULL }, + { "Var2", NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL } +}; + +static const uscxml_elem_donedata _uscxml_EC83C2A5_elem_donedatas[1] = { + /* source, content, contentexpr, params */ + { 0, NULL, NULL, NULL } +}; + +#endif + +#ifndef USCXML_NO_ELEM_INFO + +#endif + +#ifndef USCXML_NO_EXEC_CONTENT + +static int _uscxml_EC83C2A5_s1_on_entry_0(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + int err = USCXML_ERR_OK; + if likely(ctx->exec_content_assign != NULL) { + if ((ctx->exec_content_assign(ctx, "_ioprocessors", "'otherName'")) != USCXML_ERR_OK) return err; + } else { + return USCXML_ERR_MISSING_CALLBACK; + } + if likely(ctx->exec_content_raise != NULL) { + if unlikely((ctx->exec_content_raise(ctx, "foo")) != USCXML_ERR_OK) return err; + } else { + return USCXML_ERR_MISSING_CALLBACK; + } + return USCXML_ERR_OK; +} + +static int _uscxml_EC83C2A5_s1_on_entry(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + _uscxml_EC83C2A5_s1_on_entry_0(ctx, state, event); + return USCXML_ERR_OK; +} + +static int _uscxml_EC83C2A5_s2_on_entry_0(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + int err = USCXML_ERR_OK; + if likely(ctx->exec_content_assign != NULL) { + if ((ctx->exec_content_assign(ctx, "Var2", "_ioprocessors")) != USCXML_ERR_OK) return err; + } else { + return USCXML_ERR_MISSING_CALLBACK; + } + return USCXML_ERR_OK; +} + +static int _uscxml_EC83C2A5_s2_on_entry(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + _uscxml_EC83C2A5_s2_on_entry_0(ctx, state, event); + return USCXML_ERR_OK; +} + +static int _uscxml_EC83C2A5_pass_on_entry_0(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + int err = USCXML_ERR_OK; + if likely(ctx->exec_content_log != NULL) { + if unlikely((ctx->exec_content_log(ctx, "Outcome", "'pass'")) != USCXML_ERR_OK) return err; + } else { + return USCXML_ERR_MISSING_CALLBACK; + } + return USCXML_ERR_OK; +} + +static int _uscxml_EC83C2A5_pass_on_entry(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + _uscxml_EC83C2A5_pass_on_entry_0(ctx, state, event); + return USCXML_ERR_OK; +} + +static int _uscxml_EC83C2A5_fail_on_entry_0(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + int err = USCXML_ERR_OK; + if likely(ctx->exec_content_log != NULL) { + if unlikely((ctx->exec_content_log(ctx, "Outcome", "'fail'")) != USCXML_ERR_OK) return err; + } else { + return USCXML_ERR_MISSING_CALLBACK; + } + return USCXML_ERR_OK; +} + +static int _uscxml_EC83C2A5_fail_on_entry(const uscxml_ctx* ctx, const uscxml_state* state, const void* event) { + _uscxml_EC83C2A5_fail_on_entry_0(ctx, state, event); + return USCXML_ERR_OK; +} + +#endif + +#ifndef USCXML_NO_ELEM_INFO + +static const uscxml_state _uscxml_EC83C2A5_states[6] = { + { /* state number 0 */ + /* name */ NULL, + /* parent */ 0, + /* onentry */ NULL, + /* onexit */ NULL, + /* invoke */ NULL, + /* children */ { 0x3e /* 011111 */ }, + /* completion */ { 0x02 /* 010000 */ }, + /* ancestors */ { 0x00 /* 000000 */ }, + /* data */ &_uscxml_EC83C2A5_elem_datas[0], + /* type */ USCXML_STATE_COMPOUND, + }, + { /* state number 1 */ + /* name */ "s0", + /* parent */ 0, + /* onentry */ NULL, + /* onexit */ NULL, + /* invoke */ NULL, + /* children */ { 0x00 /* 000000 */ }, + /* completion */ { 0x00 /* 000000 */ }, + /* ancestors */ { 0x01 /* 100000 */ }, + /* data */ NULL, + /* type */ USCXML_STATE_ATOMIC, + }, + { /* state number 2 */ + /* name */ "s1", + /* parent */ 0, + /* onentry */ _uscxml_EC83C2A5_s1_on_entry, + /* onexit */ NULL, + /* invoke */ NULL, + /* children */ { 0x00 /* 000000 */ }, + /* completion */ { 0x00 /* 000000 */ }, + /* ancestors */ { 0x01 /* 100000 */ }, + /* data */ NULL, + /* type */ USCXML_STATE_ATOMIC, + }, + { /* state number 3 */ + /* name */ "s2", + /* parent */ 0, + /* onentry */ _uscxml_EC83C2A5_s2_on_entry, + /* onexit */ NULL, + /* invoke */ NULL, + /* children */ { 0x00 /* 000000 */ }, + /* completion */ { 0x00 /* 000000 */ }, + /* ancestors */ { 0x01 /* 100000 */ }, + /* data */ NULL, + /* type */ USCXML_STATE_ATOMIC, + }, + { /* state number 4 */ + /* name */ "pass", + /* parent */ 0, + /* onentry */ _uscxml_EC83C2A5_pass_on_entry, + /* onexit */ NULL, + /* invoke */ NULL, + /* children */ { 0x00 /* 000000 */ }, + /* completion */ { 0x00 /* 000000 */ }, + /* ancestors */ { 0x01 /* 100000 */ }, + /* data */ NULL, + /* type */ USCXML_STATE_FINAL, + }, + { /* state number 5 */ + /* name */ "fail", + /* parent */ 0, + /* onentry */ _uscxml_EC83C2A5_fail_on_entry, + /* onexit */ NULL, + /* invoke */ NULL, + /* children */ { 0x00 /* 000000 */ }, + /* completion */ { 0x00 /* 000000 */ }, + /* ancestors */ { 0x01 /* 100000 */ }, + /* data */ NULL, + /* type */ USCXML_STATE_FINAL, + } +}; + +#endif + +#ifndef USCXML_NO_ELEM_INFO + +static const uscxml_transition _uscxml_EC83C2A5_transitions[6] = { + { /* transition number 0 with priority 0 + target: s1 + */ + /* source */ 1, + /* target */ { 0x04 /* 001000 */ }, + /* event */ NULL, + /* condition */ "Var1", + /* ontrans */ NULL, + /* type */ USCXML_TRANS_SPONTANEOUS, + /* conflicts */ { 0x3f /* 111111 */ }, + /* exit set */ { 0x3e /* 011111 */ } + }, + { /* transition number 1 with priority 1 + target: fail + */ + /* source */ 1, + /* target */ { 0x20 /* 000001 */ }, + /* event */ NULL, + /* condition */ "true", + /* ontrans */ NULL, + /* type */ USCXML_TRANS_SPONTANEOUS, + /* conflicts */ { 0x3f /* 111111 */ }, + /* exit set */ { 0x3e /* 011111 */ } + }, + { /* transition number 2 with priority 2 + target: s2 + */ + /* source */ 2, + /* target */ { 0x08 /* 000100 */ }, + /* event */ "error.execution", + /* condition */ NULL, + /* ontrans */ NULL, + /* type */ 0, + /* conflicts */ { 0x3f /* 111111 */ }, + /* exit set */ { 0x3e /* 011111 */ } + }, + { /* transition number 3 with priority 3 + target: fail + */ + /* source */ 2, + /* target */ { 0x20 /* 000001 */ }, + /* event */ "*", + /* condition */ NULL, + /* ontrans */ NULL, + /* type */ 0, + /* conflicts */ { 0x3f /* 111111 */ }, + /* exit set */ { 0x3e /* 011111 */ } + }, + { /* transition number 4 with priority 4 + target: pass + */ + /* source */ 3, + /* target */ { 0x10 /* 000010 */ }, + /* event */ NULL, + /* condition */ "Var1==Var2", + /* ontrans */ NULL, + /* type */ USCXML_TRANS_SPONTANEOUS, + /* conflicts */ { 0x3f /* 111111 */ }, + /* exit set */ { 0x3e /* 011111 */ } + }, + { /* transition number 5 with priority 5 + target: fail + */ + /* source */ 3, + /* target */ { 0x20 /* 000001 */ }, + /* event */ NULL, + /* condition */ NULL, + /* ontrans */ NULL, + /* type */ USCXML_TRANS_SPONTANEOUS, + /* conflicts */ { 0x3f /* 111111 */ }, + /* exit set */ { 0x3e /* 011111 */ } + } +}; + +#endif + +#ifndef USCXML_NO_ELEM_INFO + +#ifndef USCXML_MACHINE +# define USCXML_MACHINE _uscxml_EC83C2A5_machine +#endif +#define USCXML_MACHINE_0 _uscxml_EC83C2A5_machine +#define USCXML_MACHINE_MACHINENAME _uscxml_EC83C2A5_machine + +const uscxml_machine _uscxml_EC83C2A5_machine = { + /* flags */ 0, + /* nr_states */ 6, + /* nr_transitions */ 6, + /* name */ "machineName", + /* datamodel */ "ecmascript", + /* uuid */ "EC83C2A5BDF05B11A1F7E2C35039F65D", + /* states */ &_uscxml_EC83C2A5_states[0], + /* transitions */ &_uscxml_EC83C2A5_transitions[0], + /* parent */ NULL, + /* donedata */ &_uscxml_EC83C2A5_elem_donedatas[0], + /* script */ NULL +}; + +#endif + +#ifdef USCXML_VERBOSE +/** + * Print name of states contained in a (debugging). + */ +static void printStateNames(const uscxml_ctx* ctx, const char* a, size_t length) { + size_t i; + const char* seperator = ""; + for (i = 0; i < length; i++) { + if (BIT_HAS(i, a)) { + printf("%s%s", seperator, (USCXML_GET_STATE(i).name != NULL ? USCXML_GET_STATE(i).name : "UNK")); + seperator = ", "; + } + } + printf("\n"); +} + +/** + * Print bits set in a in a binary representation (debugging). + */ +static void printBitsetIndices(const char* a, size_t length) { + size_t i; + const char* seperator = ""; + for (i = 0; i < length; i++) { + if (BIT_HAS(i, a)) { + printf("%s%lu", seperator, i); + seperator = ", "; + } + } + printf("\n"); +} +#endif + +#ifndef USCXML_NO_BIT_OPERATIONS +/** + * Return true if there is a common bit in a and b. + */ +static int bit_has_and(const char* a, const char* b, size_t i) { + while(i--) { + if (a[i] & b[i]) + return 1; + } + return 0; +} + +/** + * Set all bits to 0, this corresponds to memset(a, 0, i), + * but does not require string.h or cstring. + */ +static void bit_clear_all(char* a, size_t i) { + while(i--) { + a[i] = 0; + } +} + +/** + * Return true if there is any bit set in a. + */ +static int bit_has_any(const char* a, size_t i) { + while(i--) { + if (a[i] > 0) + return 1; + } + return 0; +} + +/** + * Set all bits from given mask in dest, this is |= for bit arrays. + */ +static void bit_or(char* dest, const char* mask, size_t i) { + while(i--) { + dest[i] |= mask[i]; + } +} + +/** + * Copy all bits from source to dest, this corresponds to memcpy(a, b, i), + * but does not require string.h or cstring. + */ +static void bit_copy(char* dest, const char* source, size_t i) { + while(i--) { + dest[i] = source[i]; + } +} + +/** + * Unset bits from mask in dest. + */ +static void bit_and_not(char* dest, const char* mask, size_t i) { + while(i--) { + dest[i] &= ~mask[i]; + } +} + +/** + * Set bits from mask in dest. + */ +static void bit_and(char* dest, const char* mask, size_t i) { + while(i--) { + dest[i] &= mask[i]; + }; +} + +#define USCXML_NO_BIT_OPERATIONS +#endif + +#ifndef USCXML_NO_STEP_FUNCTION +int uscxml_step(uscxml_ctx* ctx) { + + USCXML_NR_TRANS_TYPE i, j, k; + USCXML_NR_STATES_TYPE nr_states_bytes = ((USCXML_NUMBER_STATES + 7) & ~7) >> 3; + USCXML_NR_TRANS_TYPE nr_trans_bytes = ((USCXML_NUMBER_TRANS + 7) & ~7) >> 3; + int err = USCXML_ERR_OK; + char conflicts [USCXML_MAX_NR_TRANS_BYTES]; + char trans_set [USCXML_MAX_NR_TRANS_BYTES]; + char target_set [USCXML_MAX_NR_STATES_BYTES]; + char exit_set [USCXML_MAX_NR_STATES_BYTES]; + char entry_set [USCXML_MAX_NR_STATES_BYTES]; + char tmp_states [USCXML_MAX_NR_STATES_BYTES]; + +#ifdef USCXML_VERBOSE + printf("Config: "); + printStateNames(ctx, ctx->config, USCXML_NUMBER_STATES); +#endif + + if (ctx->flags & USCXML_CTX_FINISHED) + return USCXML_ERR_DONE; + + if (ctx->flags & USCXML_CTX_TOP_LEVEL_FINAL) { + /* exit all remaining states */ + i = USCXML_NUMBER_STATES; + while(i-- > 0) { + if (BIT_HAS(i, ctx->config)) { + /* call all on exit handlers */ + if (USCXML_GET_STATE(i).on_exit != NULL) { + if unlikely((err = USCXML_GET_STATE(i).on_exit(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) + return err; + } + } + if (BIT_HAS(i, ctx->invocations)) { + if (USCXML_GET_STATE(i).invoke != NULL) + USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 1); + BIT_CLEAR(i, ctx->invocations); + } + } + ctx->flags |= USCXML_CTX_FINISHED; + return USCXML_ERR_DONE; + } + + bit_clear_all(target_set, nr_states_bytes); + bit_clear_all(trans_set, nr_trans_bytes); + if unlikely(ctx->flags == USCXML_CTX_PRISTINE) { + if (ctx->machine->script != NULL) + ctx->machine->script(ctx, &ctx->machine->states[0], NULL); + bit_or(target_set, ctx->machine->states[0].completion, nr_states_bytes); + ctx->flags |= USCXML_CTX_SPONTANEOUS | USCXML_CTX_INITIALIZED; + goto ESTABLISH_ENTRY_SET; + } + + if (ctx->flags & USCXML_CTX_SPONTANEOUS) { + ctx->event = NULL; + goto SELECT_TRANSITIONS; + } + if (ctx->dequeue_internal != NULL && (ctx->event = ctx->dequeue_internal(ctx)) != NULL) { + goto SELECT_TRANSITIONS; + } + + /* manage invocations */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + /* uninvoke */ + if (!BIT_HAS(i, ctx->config) && BIT_HAS(i, ctx->invocations)) { + if (USCXML_GET_STATE(i).invoke != NULL) + USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 1); + BIT_CLEAR(i, ctx->invocations) + } + /* invoke */ + if (BIT_HAS(i, ctx->config) && !BIT_HAS(i, ctx->invocations)) { + if (USCXML_GET_STATE(i).invoke != NULL) + USCXML_GET_STATE(i).invoke(ctx, &USCXML_GET_STATE(i), NULL, 0); + BIT_SET_AT(i, ctx->invocations) + } + } + + if (ctx->dequeue_external != NULL && (ctx->event = ctx->dequeue_external(ctx)) != NULL) { + goto SELECT_TRANSITIONS; + } + + if (ctx->dequeue_external == NULL) { + return USCXML_ERR_DONE; + } + return USCXML_ERR_IDLE; + +SELECT_TRANSITIONS: + bit_clear_all(conflicts, nr_trans_bytes); + bit_clear_all(exit_set, nr_states_bytes); + for (i = 0; i < USCXML_NUMBER_TRANS; i++) { + /* never select history or initial transitions automatically */ + if unlikely(USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) + continue; + + /* is the transition active? */ + if (BIT_HAS(USCXML_GET_TRANS(i).source, ctx->config)) { + /* is it non-conflicting? */ + if (!BIT_HAS(i, conflicts)) { + /* is it spontaneous with an event or vice versa? */ + if ((USCXML_GET_TRANS(i).event == NULL && ctx->event == NULL) || + (USCXML_GET_TRANS(i).event != NULL && ctx->event != NULL)) { + /* is it enabled? */ + if (ctx->is_enabled(ctx, &USCXML_GET_TRANS(i), ctx->event) > 0) { + /* remember that we found a transition */ + ctx->flags |= USCXML_CTX_TRANSITION_FOUND; + + /* transitions that are pre-empted */ + bit_or(conflicts, USCXML_GET_TRANS(i).conflicts, nr_trans_bytes); + + /* states that are directly targeted (resolve as entry-set later) */ + bit_or(target_set, USCXML_GET_TRANS(i).target, nr_states_bytes); + + /* states that will be left */ + bit_or(exit_set, USCXML_GET_TRANS(i).exit_set, nr_states_bytes); + + BIT_SET_AT(i, trans_set); + } + } + } + } + } + bit_and(exit_set, ctx->config, nr_states_bytes); + + if (ctx->flags & USCXML_CTX_TRANSITION_FOUND) { + ctx->flags |= USCXML_CTX_SPONTANEOUS; + ctx->flags &= ~USCXML_CTX_TRANSITION_FOUND; + } else { + ctx->flags &= ~USCXML_CTX_SPONTANEOUS; + } + +#ifdef USCXML_VERBOSE + printf("Targets: "); + printStateNames(ctx, target_set, USCXML_NUMBER_STATES); +#endif + +#ifdef USCXML_VERBOSE + printf("Exiting: "); + printStateNames(ctx, exit_set, USCXML_NUMBER_STATES); +#endif + +#ifdef USCXML_VERBOSE + printf("History: "); + printStateNames(ctx, ctx->history, USCXML_NUMBER_STATES); +#endif + +/* REMEMBER_HISTORY: */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW || + USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP) { + /* a history state whose parent is about to be exited */ + if unlikely(BIT_HAS(USCXML_GET_STATE(i).parent, exit_set)) { + bit_copy(tmp_states, USCXML_GET_STATE(i).completion, nr_states_bytes); + + /* set those states who were enabled */ + bit_and(tmp_states, ctx->config, nr_states_bytes); + + /* clear current history with completion mask */ + bit_and_not(ctx->history, USCXML_GET_STATE(i).completion, nr_states_bytes); + + /* set history */ + bit_or(ctx->history, tmp_states, nr_states_bytes); + } + } + } + +ESTABLISH_ENTRY_SET: + /* calculate new entry set */ + bit_copy(entry_set, target_set, nr_states_bytes); + + /* iterate for ancestors */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + if (BIT_HAS(i, entry_set)) { + bit_or(entry_set, USCXML_GET_STATE(i).ancestors, nr_states_bytes); + } + } + + /* iterate for descendants */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + if (BIT_HAS(i, entry_set)) { + switch (USCXML_STATE_MASK(USCXML_GET_STATE(i).type)) { + case USCXML_STATE_PARALLEL: { + bit_or(entry_set, USCXML_GET_STATE(i).completion, nr_states_bytes); + break; + } + case USCXML_STATE_HISTORY_SHALLOW: + case USCXML_STATE_HISTORY_DEEP: { + if (!bit_has_and(USCXML_GET_STATE(i).completion, ctx->history, nr_states_bytes) && + !BIT_HAS(USCXML_GET_STATE(i).parent, ctx->config)) { + /* nothing set for history, look for a default transition */ + for (j = 0; j < USCXML_NUMBER_TRANS; j++) { + if unlikely(ctx->machine->transitions[j].source == i) { + bit_or(entry_set, ctx->machine->transitions[j].target, nr_states_bytes); + if(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP && + !bit_has_and(ctx->machine->transitions[j].target, USCXML_GET_STATE(i).children, nr_states_bytes)) { + for (k = i + 1; k < USCXML_NUMBER_STATES; k++) { + if (BIT_HAS(k, ctx->machine->transitions[j].target)) { + bit_or(entry_set, ctx->machine->states[k].ancestors, nr_states_bytes); + break; + } + } + } + BIT_SET_AT(j, trans_set); + break; + } + /* Note: SCXML mandates every history to have a transition! */ + } + } else { + bit_copy(tmp_states, USCXML_GET_STATE(i).completion, nr_states_bytes); + bit_and(tmp_states, ctx->history, nr_states_bytes); + bit_or(entry_set, tmp_states, nr_states_bytes); + if (USCXML_GET_STATE(i).type == (USCXML_STATE_HAS_HISTORY | USCXML_STATE_HISTORY_DEEP)) { + /* a deep history state with nested histories -> more completion */ + for (j = i + 1; j < USCXML_NUMBER_STATES; j++) { + if (BIT_HAS(j, USCXML_GET_STATE(i).completion) && + BIT_HAS(j, entry_set) && + (ctx->machine->states[j].type & USCXML_STATE_HAS_HISTORY)) { + for (k = j + 1; k < USCXML_NUMBER_STATES; k++) { + /* add nested history to entry_set */ + if ((USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_HISTORY_DEEP || + USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_HISTORY_SHALLOW) && + BIT_HAS(k, ctx->machine->states[j].children)) { + /* a nested history state */ + BIT_SET_AT(k, entry_set); + } + } + } + } + } + } + break; + } + case USCXML_STATE_INITIAL: { + for (j = 0; j < USCXML_NUMBER_TRANS; j++) { + if (ctx->machine->transitions[j].source == i) { + BIT_SET_AT(j, trans_set); + BIT_CLEAR(i, entry_set); + bit_or(entry_set, ctx->machine->transitions[j].target, nr_states_bytes); + for (k = i + 1; k < USCXML_NUMBER_STATES; k++) { + if (BIT_HAS(k, ctx->machine->transitions[j].target)) { + bit_or(entry_set, ctx->machine->states[k].ancestors, nr_states_bytes); + } + } + } + } + break; + } + case USCXML_STATE_COMPOUND: { /* we need to check whether one child is already in entry_set */ + if (!bit_has_and(entry_set, USCXML_GET_STATE(i).children, nr_states_bytes) && + (!bit_has_and(ctx->config, USCXML_GET_STATE(i).children, nr_states_bytes) || + bit_has_and(exit_set, USCXML_GET_STATE(i).children, nr_states_bytes))) + { + bit_or(entry_set, USCXML_GET_STATE(i).completion, nr_states_bytes); + if (!bit_has_and(USCXML_GET_STATE(i).completion, USCXML_GET_STATE(i).children, nr_states_bytes)) { + /* deep completion */ + for (j = i + 1; j < USCXML_NUMBER_STATES; j++) { + if (BIT_HAS(j, USCXML_GET_STATE(i).completion)) { + bit_or(entry_set, ctx->machine->states[j].ancestors, nr_states_bytes); + break; /* completion of compound is single state */ + } + } + } + } + break; + } + } + } + } + +#ifdef USCXML_VERBOSE + printf("Transitions: "); + printBitsetIndices(trans_set, sizeof(char) * 8 * nr_trans_bytes); +#endif + +/* EXIT_STATES: */ + i = USCXML_NUMBER_STATES; + while(i-- > 0) { + if (BIT_HAS(i, exit_set) && BIT_HAS(i, ctx->config)) { + /* call all on exit handlers */ + if (USCXML_GET_STATE(i).on_exit != NULL) { + if unlikely((err = USCXML_GET_STATE(i).on_exit(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) + return err; + } + BIT_CLEAR(i, ctx->config); + } + } + +/* TAKE_TRANSITIONS: */ + for (i = 0; i < USCXML_NUMBER_TRANS; i++) { + if (BIT_HAS(i, trans_set) && (USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { + /* call executable content in transition */ + if (USCXML_GET_TRANS(i).on_transition != NULL) { + if unlikely((err = USCXML_GET_TRANS(i).on_transition(ctx, + &ctx->machine->states[USCXML_GET_TRANS(i).source], + ctx->event)) != USCXML_ERR_OK) + return err; + } + } + } + +#ifdef USCXML_VERBOSE + printf("Entering: "); + printStateNames(ctx, entry_set, USCXML_NUMBER_STATES); +#endif + +/* ENTER_STATES: */ + for (i = 0; i < USCXML_NUMBER_STATES; i++) { + if (BIT_HAS(i, entry_set) && !BIT_HAS(i, ctx->config)) { + /* these are no proper states */ + if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_DEEP || + USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_HISTORY_SHALLOW || + USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_INITIAL) + continue; + + BIT_SET_AT(i, ctx->config); + + /* initialize data */ + if (!BIT_HAS(i, ctx->initialized_data)) { + if unlikely(USCXML_GET_STATE(i).data != NULL && ctx->exec_content_init != NULL) { + ctx->exec_content_init(ctx, USCXML_GET_STATE(i).data); + } + BIT_SET_AT(i, ctx->initialized_data); + } + + if (USCXML_GET_STATE(i).on_entry != NULL) { + if unlikely((err = USCXML_GET_STATE(i).on_entry(ctx, &USCXML_GET_STATE(i), ctx->event)) != USCXML_ERR_OK) + return err; + } + + /* take history and initial transitions */ + for (j = 0; j < USCXML_NUMBER_TRANS; j++) { + if unlikely(BIT_HAS(j, trans_set) && + (ctx->machine->transitions[j].type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) && + ctx->machine->states[ctx->machine->transitions[j].source].parent == i) { + /* call executable content in transition */ + if (ctx->machine->transitions[j].on_transition != NULL) { + if unlikely((err = ctx->machine->transitions[j].on_transition(ctx, + &USCXML_GET_STATE(i), + ctx->event)) != USCXML_ERR_OK) + return err; + } + } + } + + /* handle final states */ + if unlikely(USCXML_STATE_MASK(USCXML_GET_STATE(i).type) == USCXML_STATE_FINAL) { + if unlikely(USCXML_GET_STATE(i).ancestors[0] == 0x01) { + ctx->flags |= USCXML_CTX_TOP_LEVEL_FINAL; + } else { + /* raise done event */ + const uscxml_elem_donedata* donedata = &ctx->machine->donedata[0]; + while(USCXML_ELEM_DONEDATA_IS_SET(donedata)) { + if unlikely(donedata->source == i) + break; + donedata++; + } + ctx->raise_done_event(ctx, &ctx->machine->states[USCXML_GET_STATE(i).parent], (USCXML_ELEM_DONEDATA_IS_SET(donedata) ? donedata : NULL)); + } + + /** + * are we the last final state to leave a parallel state?: + * 1. Gather all parallel states in our ancestor chain + * 2. Find all states for which these parallels are ancestors + * 3. Iterate all active final states and remove their ancestors + * 4. If a state remains, not all children of a parallel are final + */ + for (j = 0; j < USCXML_NUMBER_STATES; j++) { + if unlikely(USCXML_STATE_MASK(ctx->machine->states[j].type) == USCXML_STATE_PARALLEL && + BIT_HAS(j, USCXML_GET_STATE(i).ancestors)) { + bit_clear_all(tmp_states, nr_states_bytes); + for (k = 0; k < USCXML_NUMBER_STATES; k++) { + if unlikely(BIT_HAS(j, ctx->machine->states[k].ancestors) && BIT_HAS(k, ctx->config)) { + if (USCXML_STATE_MASK(ctx->machine->states[k].type) == USCXML_STATE_FINAL) { + bit_and_not(tmp_states, ctx->machine->states[k].ancestors, nr_states_bytes); + } else { + BIT_SET_AT(k, tmp_states); + } + } + } + if unlikely(!bit_has_any(tmp_states, nr_states_bytes)) { + ctx->raise_done_event(ctx, &ctx->machine->states[j], NULL); + } + } + } + + } + + } + } + + return USCXML_ERR_OK; +} + +#define USCXML_NO_STEP_FUNCTION +#endif + -- cgit v0.12