summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Radomski <sradomski@mintwerk.de>2016-02-14 14:14:28 (GMT)
committerStefan Radomski <sradomski@mintwerk.de>2016-02-14 14:14:28 (GMT)
commit8e62f3801b98bf4b7f7f85b848b2fe6339c99162 (patch)
treea6c593bc3e59a864ddb69b6160e3db610fe995d6
parent7b428e5435f83a7e7db37094a9197afa2dd09bf5 (diff)
downloaduscxml-8e62f3801b98bf4b7f7f85b848b2fe6339c99162.zip
uscxml-8e62f3801b98bf4b7f7f85b848b2fe6339c99162.tar.gz
uscxml-8e62f3801b98bf4b7f7f85b848b2fe6339c99162.tar.bz2
More work on ANSI C transformation
-rw-r--r--docs/NATIVE_CODE.md138
-rw-r--r--src/uscxml/plugins/DataModel.h13
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp110
-rw-r--r--src/uscxml/transform/ChartToC.cpp67
-rw-r--r--test/src/test-c-machine.cpp125
-rw-r--r--test/src/test-c-machine.scxml.c29
6 files changed, 313 insertions, 169 deletions
diff --git a/docs/NATIVE_CODE.md b/docs/NATIVE_CODE.md
index 91af599..c6f2416 100644
--- a/docs/NATIVE_CODE.md
+++ b/docs/NATIVE_CODE.md
@@ -9,9 +9,8 @@ native description of the state-chart.
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 <tt>uscxml-transform</tt>:
+subset of virtually any more modern C/C++ dialect. First, you need to transform
+your SCXML state-chart onto ANSI C by invoking <tt>uscxml-transform</tt>:
$ uscxml-transform -tc -i INPUT_FILE -o OUTPUT_FILE
@@ -47,9 +46,21 @@ allow you to influence important characteristics of you state-machine.
state and <tt>transitions[USCXML_MAX_NR_TRANS_BYTES]</tt> for one bit
per transition.
+ * <tt>**USCXML_ELEM_X_IS_SET**</tt>:
+
+ These macros are defined for <tt>DATA</tt>, <tt>PARAM</tt> and
+ <tt>DONEDATA</tt> and allow to iterate instances. For all of the
+ corresponding SCXML elements, a callback might be supplied with a set
+ of instances (e.g. <tt>invoke</tt> takes a set of <tt>&lt;param></tt>
+ elements). They are contained in a continuous memory region and can be
+ iterated by merely increasing the respective pointer. The macros allow
+ to check whether the pointer is still valid or whether there are no
+ more instances of the given structure.
+
* 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** (<tt>struct</tt>) 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
@@ -64,7 +75,8 @@ 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:
+ in user-supplied application code, the tranformation will define three
+ additional macros:
#ifndef USCXML_MACHINE
# define USCXML_MACHINE _uscxml_BC220711_machine
@@ -89,42 +101,41 @@ 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.
+a set of functions, you will need to allocate and clear memory for a
+<tt>uscxml_ctx</tt> structure, set its machine field to a
+<tt>uscxml_machine</tt> structure and register user-supplied callbacks.
+
+### Context Callbacks
-### Fully Compliant
+An SCXML interpreter does more than to perform a series of microsteps for an
+event over a set of states and transitions and there are quite a few
+responsibilities not implemented in the generated ANSI C code. These will be
+delegated to user-supplied code via callbacks if they are required for the interpretation of a given SCXML file.
-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:
+There is already a scaffolding providing most of the callbacks implemented in
+the [test-c-machine.c](../test/src/test-c-machine.c) test file and you can just
+isolate the <tt>StateMachine</tt> class contained within. It does everything
+but custom invocations but requires linking with <tt>libuscxml</tt> for the
+datamodel implementations and several other functions. Depending on the number
+of SCXML language features you employ, you can get away with providing
+considerably fewer callbacks as detailled below.
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:
+ internal and an external one. These queues can grow to arbitrary size and
+ we made a decision, not to employ <tt>malloc</tt> for heap allocations in
+ the generated ANSI-C source. As such, it is the responsibility of the
+ user-supplied code to manage the queues via the following callbacks:
- 1. <tt>**uscxml_ctx.dequeue_internal**</tt>: This callback is invoked
- whenever the interpreter needs an event from the internal event queue. It
- is passed an instance of a <tt>uscxml_ctx</tt> structure and is supposed to
- return an opaque pointer to an event. If the internal queue is empty,
- <tt>NULL</tt> is to be returned.
+ | Callback | Comments | Required For |
+ |-|-|-|
+ | <tt>**dequeue_internal**</tt> | This callback is invoked whenever the interpreter needs an event from the internal event queue. It is passed an instance of a <tt>uscxml_ctx</tt> structure and is supposed to return an opaque pointer to an event. If the internal queue is empty, <tt>NULL</tt> is to be returned. | Dequeuing *internal* events |
+ | <tt>**dequeue_external**</tt> | This callback is functionally equivalent to <tt>uscxml_ctx.dequeue_internal</tt> but invoked, when an external event is to be dequeued. | Dequeuing *external* events |
+ | <tt>**exec_content_send**</tt> | Whenever there is an <tt>&lt;send></tt> element encountered in executable content, the generated ANSI C code will invoke this callback with a context and an <tt>uscxml_elem_send</tt> instance and the user code registered at the callback is expected to handle the send request as per SCXML recommendation. | Delivering events via <tt>&lt;send></tt> |
+ | <tt>**exec_content_raise**</tt> | This callback is invoked for any <tt>&lt;raise></tt> element processed as part of executable content and is expected to deliver an event to the internal event queue. | Delivering events via <tt>&lt;raise></tt> |
- 2. <tt>**uscxml_ctx.dequeue_external**</tt>: This callback is functionally
- equivalent to <tt>uscxml_ctx.dequeue_internal</tt> but invoked, when an
- external event is to be dequeued.
-
- 3. <tt>**uscxml_ctx.exec_content_send**</tt>: Whenever there is an
- <tt>&lt;send></tt> element encountered in executable content, the generated
- ANSI C code will invoke this callback with a context and an
- <tt>uscxml_elem_send</tt> instance and the user code registered at the
- callback is expected to handle the send request as per recommendation.
-
- 4. <tt>**uscxml_ctx.exec_content_raise**</tt>: This callback is invoked for
- any <tt>&lt;raise></tt> element processed as part of executable content and
- is expected to deliver an event to the internal event queue.
+ The events themselves are represented as opaque pointers and the generated ANSI-C code will never access any of its members.
2. **Transition Matching / Enabling**
@@ -132,11 +143,62 @@ not implemented in the generated ANSI C code:
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 <tt>**uscxml_ctx.is_enabled**</tt> callback.
- It receives a context, a <tt>uscxml_transition</tt> structure and the
- opaque event pointer and will have to return <tt>0</tt> for when the
- transition is not matched and enabled by the given event and <tt>1</tt> if
- it is.
+ enabled.
+
+ | Callback | Comments | Required For |
+ |-|-|-|
+ | <tt>**is_matched**</tt> | This callback receives a context, an <tt>uscxml_transition</tt> structure and the opaque event pointer. It is expected to return <tt>0</tt> for when the transition is not matched by the given event and <tt>1</tt> if it is. You can assume that non-spontaneous transitions are not checked for the null-event and vice versa. | Event name *matching* of a transition. |
+ | <tt>**is_enabled**</tt> | This callback receives a context and a <tt>uscxml_transition</tt> structure. It is expected to return <tt>0</tt> for when the transition is not enabled and <tt>1</tt> if it is. Only transitions with an actual <tt>condition</tt> attribute will be checked. | Determining *enabled* status of a transition. |
+
+3. **Invocations**
+
+ The transformation will generate machine structures for all SCXML
+ state-charts found within a document, but will make no attempt to invoke
+ them automatically. Instead, the generated ANSI-C code will call upon the respective callback in the <tt>uscxml_ctx</tt> structure:
+
+ | Callback | Comments | Required For |
+ |-|-|-|
+ | <tt>**invoke**</tt> | The call back is provided with a context and an <tt>uscxml_elem_invoke</tt> structure. This structure will contain all the information pertaining to the <tt>&lt;invoke></tt> element, with an additional optional member <tt>machine</tt>, which points to the <tt>uscxml_machine</tt> structure in case another, nested SCXML machine is to be invoked. It is your responsibility to create a <tt>uscxml_ctx</tt> for this new machine and run it or start any other type of invocation specified in the given structure. | Invoking external components via <tt>&lt;invoke></tt> |
+
+4. **Executable Content**
+
+ In general, every instance of an element for executable content has a respective callback in the <tt>uscxml_ctx</tt> structure. There are a few examples, wherein an element is transformed onto control flow that will invoke multiple callbacks:
+
+ | Callback | Comments | Required For |
+ |-|-|-|
+ | <tt>**exec_content_log**</tt> | | <tt>&lt;log></tt> |
+ | <tt>**exec_content_raise**</tt> | | <tt>&lt;raise></tt> |
+ | <tt>**exec_content_send**</tt> | | <tt>&lt;send></tt> |
+ | <tt>**is_true**</tt> | | <tt>&lt;if> / &lt;elseif> / &lt;else></tt> |
+ | <tt>**exec_content_foreach_init**</tt> <tt>**exec_content_foreach_next**</tt> <tt>**exec_content_foreach_done**</tt> | | <tt>&lt;foreach></tt> |
+ | <tt>**exec_content_assign**</tt> | | <tt>&lt;assign></tt> |
+ | <tt>**exec_content_init**</tt> | | <tt>&lt;data></tt> |
+ | <tt>**exec_content_cancel**</tt> | | <tt>&lt;cancel></tt> |
+ | <tt>**exec_content_script**</tt> | | <tt>&lt;script></tt> |
+
+5. **Done Events**
+
+ Finally, there is a callback that is invoked if a <tt>&lt;final></tt> state is entered.
+
+ | Callback | Comments | Required For |
+ |-|-|-|
+ | <tt>**raise_done_event**</tt> | The callback is provided with a context, the state for which a done event is to be raised and a <tt>uscxml_elem_donedata</tt> structure. | <tt>&lt;final></tt> |
+
+
+### Inline SCXML
+
+An alternative to writing an external SCXML file is to embed the document into the actual C code as a comment:
+
+ /** INLINE SCXML BEGIN
+ <scxml name="test-inline" datamodel="native">
+ <state id="foo">
+ <onentry>
+ enteredFoo();
+ </onentry>
+ </state>
+ </scxml>
+ INLINE SCXML END */
+If you pass an arbitrary input file to <tt>uscxml_transform</tt>, it will realize that it does not constitute a proper SCXML document and attempt to isolate an actual SCXML state-chart by searching for the string literals <tt>INLINE SCXML BEGIN</tt> and <tt>INLINE SCXML END</tt>. Everything in between is isolated and treated as if it was a proper SCXML document.
-### Light-Weight
+Here, you can also see a variation with the <tt>datamode="native"</tt> attribute. If this is given, the transformation will write any text child of executable content as an unescaped, verbatim string literal into the respective function, allowing you to address any of your C functions and variables directly. \ No newline at end of file
diff --git a/src/uscxml/plugins/DataModel.h b/src/uscxml/plugins/DataModel.h
index df0143d..f229a94 100644
--- a/src/uscxml/plugins/DataModel.h
+++ b/src/uscxml/plugins/DataModel.h
@@ -91,6 +91,19 @@ public:
virtual bool isDeclared(const std::string& expr) = 0;
+ /**
+ * test147:
+ * <data id="Var1" expr="0"/>
+ *
+ * test150:
+ * <data id="Var3">
+ * [1,2,3]
+ * </data>
+ *
+ * test277:
+ * <data id="Var1" expr="return"/>
+ *
+ */
virtual void assign(const Arabica::DOM::Element<std::string>& assignElem,
const Arabica::DOM::Node<std::string>& node,
const std::string& content) = 0;
diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
index fd4da3c..86bafd3 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
@@ -625,11 +625,60 @@ JSValueRef JSCDataModel::evalAsValue(const std::string& expr, bool dontThrow) {
return result;
}
+JSValueRef JSCDataModel::getNodeAsValue(const Node<std::string>& node) {
+ switch (node.getNodeType()) {
+ case Node_base::ELEMENT_NODE: {
+ TO_JSC_DOMVALUE(Element);
+ }
+ case Node_base::TEXT_NODE: {
+ TO_JSC_DOMVALUE(Text);
+ }
+ case Node_base::CDATA_SECTION_NODE: {
+ TO_JSC_DOMVALUE(CDATASection);
+ }
+ case Node_base::DOCUMENT_NODE: {
+ TO_JSC_DOMVALUE(Document);
+ }
+ default: {
+ TO_JSC_DOMVALUE(Node);
+ }
+ }
+}
+
+void JSCDataModel::assign(const std::string& location, const Data& data) {
+
+ // flags on attribute are ignored?
+ if (location.compare("_sessionid") == 0) // test 322
+ ERROR_EXECUTION_THROW("Cannot assign to _sessionId");
+ if (location.compare("_name") == 0)
+ ERROR_EXECUTION_THROW("Cannot assign to _name");
+ if (location.compare("_ioprocessors") == 0) // test 326
+ ERROR_EXECUTION_THROW("Cannot assign to _ioprocessors");
+ if (location.compare("_invokers") == 0)
+ ERROR_EXECUTION_THROW("Cannot assign to _invokers");
+ if (location.compare("_event") == 0)
+ ERROR_EXECUTION_THROW("Cannot assign to _event");
+
+ JSValueRef exception = NULL;
+ if (data.node) {
+ JSObjectSetProperty(_ctx, JSContextGetGlobalObject(_ctx), JSStringCreateWithUTF8CString(location.c_str()), getNodeAsValue(data.node), 0, &exception);
+ } else {
+ evalAsValue(location + " = " + Data::toJSON(data));
+ }
+
+ /**
+ * test157: We need to evluate, as this will not throw for 'continue' = Var[5] in
+ */
+// JSObjectSetProperty(_ctx, JSContextGetGlobalObject(_ctx), JSStringCreateWithUTF8CString(location.c_str()), getDataAsValue(data), 0, &exception);
+
+ if (exception)
+ handleException(exception);
+}
+
void JSCDataModel::assign(const Element<std::string>& assignElem,
const Node<std::string>& node,
const std::string& content) {
std::string key;
- JSValueRef exception = NULL;
if (HAS_ATTR(assignElem, "id")) {
key = ATTR(assignElem, "id");
} else if (HAS_ATTR(assignElem, "location")) {
@@ -638,63 +687,30 @@ void JSCDataModel::assign(const Element<std::string>& assignElem,
if (key.length() == 0) {
ERROR_EXECUTION_THROW("Assign element has neither id nor location");
}
- // flags on attribute are ignored?
- if (key.compare("_sessionid") == 0) // test 322
- ERROR_EXECUTION_THROW("Cannot assign to _sessionId");
- if (key.compare("_name") == 0)
- ERROR_EXECUTION_THROW("Cannot assign to _name");
- if (key.compare("_ioprocessors") == 0) // test 326
- ERROR_EXECUTION_THROW("Cannot assign to _ioprocessors");
- if (key.compare("_invokers") == 0)
- ERROR_EXECUTION_THROW("Cannot assign to _invokers");
- if (key.compare("_event") == 0)
- ERROR_EXECUTION_THROW("Cannot assign to _event");
if (HAS_ATTR(assignElem, "expr")) {
- evalAsValue(key + " = " + ATTR(assignElem, "expr"));
+ assign(key, Data(ATTR(assignElem, "expr"), Data::INTERPRETED));
} else if (node) {
- JSObjectSetProperty(_ctx, JSContextGetGlobalObject(_ctx), JSStringCreateWithUTF8CString(key.c_str()), getNodeAsValue(node), 0, &exception);
- if (exception)
- handleException(exception);
+ Data d;
+ d.node = node;
+ assign(key, d);
} else if (content.size() > 0) {
try {
- evalAsValue(key + " = " + content);
- } catch (...) {
- evalAsValue(key + " = " + "\"" + InterpreterImpl::spaceNormalize(content) + "\"");
+ Data d = Data::fromJSON(content);
+ if (d.empty())
+ throw Event();
+ assign(key, Data(d, Data::INTERPRETED));
+ } catch (Event e) {
+ assign(key, Data("\"" + InterpreterImpl::spaceNormalize(content) + "\"", Data::INTERPRETED));
}
} else {
+ JSValueRef exception = NULL;
JSObjectSetProperty(_ctx, JSContextGetGlobalObject(_ctx), JSStringCreateWithUTF8CString(key.c_str()), JSValueMakeUndefined(_ctx), 0, &exception);
if (exception)
handleException(exception);
}
}
-JSValueRef JSCDataModel::getNodeAsValue(const Node<std::string>& node) {
- switch (node.getNodeType()) {
- case Node_base::ELEMENT_NODE: {
- TO_JSC_DOMVALUE(Element);
- }
- case Node_base::TEXT_NODE: {
- TO_JSC_DOMVALUE(Text);
- }
- case Node_base::CDATA_SECTION_NODE: {
- TO_JSC_DOMVALUE(CDATASection);
- }
- case Node_base::DOCUMENT_NODE: {
- TO_JSC_DOMVALUE(Document);
- }
- default: {
- TO_JSC_DOMVALUE(Node);
- }
- }
-}
-
-void JSCDataModel::assign(const std::string& location, const Data& data) {
- std::stringstream ssJSON;
- ssJSON << data;
- evalAsValue(location + " = " + ssJSON.str());
-}
-
void JSCDataModel::init(const Element<std::string>& dataElem,
const Node<std::string>& node,
const std::string& content) {
@@ -708,7 +724,9 @@ void JSCDataModel::init(const Element<std::string>& dataElem,
} else if (HAS_ATTR(dataElem, "location")) {
key = ATTR(dataElem, "location");
}
- evalAsValue(key + " = undefined", true);
+ if (key.size() > 0) {
+ evalAsValue(key + " = undefined", true);
+ }
throw e;
}
}
diff --git a/src/uscxml/transform/ChartToC.cpp b/src/uscxml/transform/ChartToC.cpp
index c6be393..7cc50fc 100644
--- a/src/uscxml/transform/ChartToC.cpp
+++ b/src/uscxml/transform/ChartToC.cpp
@@ -690,13 +690,15 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << "typedef struct uscxml_elem_send uscxml_elem_send;" << std::endl;
stream << "typedef struct uscxml_elem_param uscxml_elem_param;" << std::endl;
stream << "typedef struct uscxml_elem_data uscxml_elem_data;" << std::endl;
+ stream << "typedef struct uscxml_elem_assign uscxml_elem_assign;" << std::endl;
stream << "typedef struct uscxml_elem_donedata uscxml_elem_donedata;" << std::endl;
stream << "typedef struct uscxml_elem_foreach uscxml_elem_foreach;" << std::endl;
stream << std::endl;
stream << "typedef void* (*dequeue_internal_t)(const uscxml_ctx* ctx);" << std::endl;
stream << "typedef void* (*dequeue_external_t)(const uscxml_ctx* ctx);" << std::endl;
- stream << "typedef int (*is_enabled_t)(const uscxml_ctx* ctx, const uscxml_transition* transition, const void* event);" << std::endl;
+ stream << "typedef int (*is_enabled_t)(const uscxml_ctx* ctx, const uscxml_transition* transition);" << std::endl;
+ stream << "typedef int (*is_matched_t)(const uscxml_ctx* ctx, const uscxml_transition* transition, const void* event);" << std::endl;
stream << "typedef int (*is_true_t)(const uscxml_ctx* ctx, const char* expr);" << std::endl;
stream << "typedef int (*exec_content_t)(const uscxml_ctx* ctx, const uscxml_state* state, const void* event);" << std::endl;
stream << "typedef int (*raise_done_event_t)(const uscxml_ctx* ctx, const uscxml_state* state, const uscxml_elem_donedata* donedata);" << std::endl;
@@ -709,7 +711,7 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << "typedef int (*exec_content_foreach_init_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach);" << std::endl;
stream << "typedef int (*exec_content_foreach_next_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach);" << std::endl;
stream << "typedef int (*exec_content_foreach_done_t)(const uscxml_ctx* ctx, const uscxml_elem_foreach* foreach);" << std::endl;
- stream << "typedef int (*exec_content_assign_t)(const uscxml_ctx* ctx, const char* location, const char* expr);" << std::endl;
+ stream << "typedef int (*exec_content_assign_t)(const uscxml_ctx* ctx, const uscxml_elem_assign* assign);" << std::endl;
stream << "typedef int (*exec_content_init_t)(const uscxml_ctx* ctx, const uscxml_elem_data* data);" << std::endl;
stream << "typedef int (*exec_content_cancel_t)(const uscxml_ctx* ctx, const char* sendid, const char* sendidexpr);" << std::endl;
stream << "typedef int (*exec_content_finalize_t)(const uscxml_ctx* ctx, const uscxml_elem_invoke* invoker, const void* event);" << std::endl;
@@ -748,6 +750,16 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << std::endl;
stream << "/**" << std::endl;
+ stream << " * All information pertaining to an <assign> element." << std::endl;
+ stream << " */" << std::endl;
+ stream << "struct uscxml_elem_assign {" << std::endl;
+ stream << " const char* location;" << std::endl;
+ stream << " const char* expr;" << std::endl;
+ stream << " const char* content;" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ stream << "/**" << std::endl;
stream << " * All information pertaining to any state element." << std::endl;
stream << " */" << std::endl;
stream << "struct uscxml_state {" << std::endl;
@@ -872,6 +884,7 @@ void ChartToC::writeTypes(std::ostream& stream) {
stream << " dequeue_internal_t dequeue_internal;" << std::endl;
stream << " dequeue_external_t dequeue_external;" << std::endl;
stream << " is_enabled_t is_enabled;" << std::endl;
+ stream << " is_matched_t is_matched;" << std::endl;
stream << " is_true_t is_true;" << std::endl;
stream << " raise_done_event_t raise_done_event;" << std::endl;
stream << std::endl;
@@ -1264,20 +1277,7 @@ void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<s
stream << padding;
stream << "if likely(ctx->exec_content_assign != NULL) {" << std::endl;
stream << padding;
- stream << " if ((ctx->exec_content_assign(ctx, ";
- stream << (HAS_ATTR(elem, "location") ? "\"" + escape(ATTR(elem, "location")) + "\"" : "NULL") << ", ";
- if (HAS_ATTR(elem, "expr")) {
- stream << "\"" + escape(ATTR(elem, "expr")) + "\"";
- } else {
- NodeSet<std::string> assignTexts = filterChildType(Node_base::TEXT_NODE, elem);
- if (assignTexts.size() > 0) {
- stream << "\"";
- writeExecContent(stream, assignTexts[0], 0);
- stream << "\"";
- } else {
- stream << "NULL";
- }
- }
+ stream << " if ((ctx->exec_content_assign(ctx, &" << _prefix << "_elem_assigns[" << ATTR(elem, "documentOrder") << "]";
stream << ")) != USCXML_ERR_OK) return err;" << std::endl;
stream << padding << "} else {" << std::endl;
stream << padding << " return USCXML_ERR_MISSING_CALLBACK;" << std::endl;
@@ -1485,6 +1485,38 @@ void ChartToC::writeElementInfo(std::ostream& stream) {
stream << std::endl;
}
+ NodeSet<std::string> assigns = DOMUtils::inDocumentOrder(_nsInfo.xmlNSPrefix + "assign", _scxml);
+ if (assigns.size() > 0) {
+ _hasElement.insert("assign");
+ stream << "static const uscxml_elem_assign " << _prefix << "_elem_assigns[" << assigns.size() << "] = {" << std::endl;
+ stream << " /* location, expr, content */" << std::endl;
+
+ for (size_t i = 0; i < assigns.size(); i++) {
+ Element<std::string> assign(assigns[i]);
+
+ stream << " { ";
+ stream << (HAS_ATTR(assign, "location") ? "\"" + escape(ATTR(assign, "location")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(assign, "expr") ? "\"" + escape(ATTR(assign, "expr")) + "\"" : "NULL") << ", ";
+
+ NodeSet<std::string> assignTexts = filterChildType(Node_base::TEXT_NODE, assign);
+ if (assignTexts.size() > 0) {
+ if (boost::trim_copy(assignTexts[0].getNodeValue()).length() > 0) {
+ std::string escaped = escape(assignTexts[0].getNodeValue());
+ stream << "\"" << escaped << "\"";
+ }
+ } else {
+ stream << "NULL";
+ }
+ stream << " }," << std::endl;
+
+ assign.setAttribute("documentOrder", toStr(i));
+ }
+
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ }
+
NodeSet<std::string> datas = DOMUtils::inDocumentOrder(_nsInfo.xmlNSPrefix + "data", _scxml);
if (datas.size() > 0) {
_hasElement.insert("data");
@@ -2169,7 +2201,8 @@ void ChartToC::writeFSM(std::ostream& stream) {
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 << " if ((ctx->event == NULL || ctx->is_matched(ctx, &USCXML_GET_TRANS(i), ctx->event) > 0) &&" << std::endl;
+ stream << " (USCXML_GET_TRANS(i).condition == NULL || ctx->is_enabled(ctx, &USCXML_GET_TRANS(i)) > 0)) {" << std::endl;
stream << " /* remember that we found a transition */" << std::endl;
stream << " ctx->flags |= USCXML_CTX_TRANSITION_FOUND;" << std::endl;
stream << std::endl;
diff --git a/test/src/test-c-machine.cpp b/test/src/test-c-machine.cpp
index 2879ef9..b4864e2 100644
--- a/test/src/test-c-machine.cpp
+++ b/test/src/test-c-machine.cpp
@@ -66,6 +66,7 @@ public:
// register callbacks with scxml context
ctx.is_enabled = &isEnabled;
+ ctx.is_matched = &isMatched;
ctx.is_true = &isTrue;
ctx.raise_done_event = &raiseDoneEvent;
ctx.invoke = &invoke;
@@ -250,31 +251,15 @@ public:
// callbacks for scxml context
- static int isEnabled(const uscxml_ctx* ctx, const uscxml_transition* t, const void* e) {
+ static int isMatched(const uscxml_ctx* ctx, const uscxml_transition* t, const void* e) {
Event* event = (Event*)e;
- if (event == NULL) {
- if (t->event == NULL) {
- // spontaneous transition, null event
- if (t->condition != NULL)
- return isTrue(ctx, t->condition);
- return true;
- } else {
- // spontaneous transition, but real event
- return false;
- }
- }
-
- // real event but spontaneous transition
- if (t->event == NULL)
- return false;
+ return (nameMatch(t->event, event->name.c_str()));
+ }
- // real transition, real event
- if (nameMatch(t->event, event->name.c_str())) {
- if (t->condition != NULL)
- return isTrue(ctx, t->condition);
- return true;
- }
- return false;
+ static int isEnabled(const uscxml_ctx* ctx, const uscxml_transition* t) {
+ if (t->condition != NULL)
+ return isTrue(ctx, t->condition);
+ return 1;
}
static int isTrue(const uscxml_ctx* ctx, const char* expr) {
@@ -318,7 +303,7 @@ public:
} else if (invocation->idlocation != NULL) {
// test224
invokedMachine->invokeId = (invocation->sourcename != NULL ? std::string(invocation->sourcename) + "." : "") + UUID::getUUID();
- ctx->exec_content_assign(ctx, invocation->idlocation, std::string("\"" + invokedMachine->invokeId + "\"").c_str());
+ USER_DATA(ctx)->dataModel.assign(invocation->idlocation, Data(invokedMachine->invokeId, Data::VERBATIM));
} else {
delete invokedMachine;
return USCXML_ERR_UNSUPPORTED;
@@ -374,6 +359,19 @@ public:
static int execContentSend(const uscxml_ctx* ctx, const uscxml_elem_send* send) {
SendRequest* e = new SendRequest();
+ std::string sendid;
+ if (send->id != NULL) {
+ sendid = send->id;
+ } else {
+ sendid = UUID::getUUID();
+ if (send->idlocation != NULL) {
+ USER_DATA(ctx)->dataModel.assign(send->idlocation, Data(sendid, Data::VERBATIM));
+ } else {
+ e->hideSendId = true;
+ }
+ }
+ e->sendid = sendid;
+
std::string target;
if (send->target != NULL) {
e->target = send->target;
@@ -384,8 +382,8 @@ public:
}
if (e->target.size() > 0 && (e->target[0] != '#' || e->target[1] != '_')) {
- delete e;
- execContentRaise(ctx, "error.execution");
+ e->name = "error.execution";
+ execContentRaise(ctx, e);
return USCXML_ERR_INVALID_TARGET;
}
@@ -401,15 +399,15 @@ public:
e->type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
}
} catch (Event exc) {
- execContentRaise(ctx, exc.name.c_str());
- delete e;
+ e->name = "error.execution";
+ execContentRaise(ctx, e);
return USCXML_ERR_EXEC_CONTENT;
}
// only one somewhat supported
if (e->type != "http://www.w3.org/TR/scxml/#SCXMLEventProcessor") {
- delete e;
- execContentRaise(ctx, "error.execution");
+ e->name = "error.execution";
+ execContentRaise(ctx, e);
return USCXML_ERR_INVALID_TARGET;
}
@@ -469,19 +467,6 @@ public:
}
}
- std::string sendid;
- if (send->id != NULL) {
- sendid = send->id;
- e->sendid = sendid;
- } else {
- sendid = UUID::getUUID();
- if (send->idlocation != NULL) {
- USER_DATA(ctx)->dataModel.assign(send->idlocation, Data(sendid, Data::VERBATIM));
- } else {
- e->hideSendId = true;
- }
- }
-
size_t delayMs = 0;
std::string delay;
if (send->delayexpr != NULL) {
@@ -518,10 +503,7 @@ public:
return USCXML_ERR_OK;
}
- static int execContentRaise(const uscxml_ctx* ctx, const char* event) {
- Event* e = new Event();
- e->name = event;
-
+ static int execContentRaise(const uscxml_ctx* ctx, Event* e) {
if (boost::starts_with(e->name, "error.")) {
e->eventType = Event::PLATFORM;
} else {
@@ -531,6 +513,12 @@ public:
return USCXML_ERR_OK;
}
+ static int execContentRaise(const uscxml_ctx* ctx, const char* event) {
+ Event* e = new Event();
+ e->name = event;
+ return execContentRaise(ctx, e);
+ }
+
static int execContentCancel(const uscxml_ctx* ctx, const char* sendid, const char* sendidexpr) {
std::string eventId;
if (sendid != NULL) {
@@ -567,16 +555,22 @@ public:
return USCXML_ERR_OK;
}
- static int execContentAssign(const uscxml_ctx* ctx, const char* location, const char* expr) {
- std::string key = location;
+ static int execContentAssign(const uscxml_ctx* ctx, const uscxml_elem_assign* assign) {
+ std::string key = assign->location;
if (key == "_sessionid" || key == "_name" || key == "_ioprocessors" || key == "_invokers" || key == "_event") {
execContentRaise(ctx, "error.execution");
return USCXML_ERR_EXEC_CONTENT;
}
try {
- Data d = USER_DATA(ctx)->dataModel.getStringAsData(expr);
- USER_DATA(ctx)->dataModel.assign(key, d);
+// Data d = USER_DATA(ctx)->dataModel.getStringAsData(expr);
+ if (assign->expr != NULL) {
+ Data d = Data(assign->expr, Data::INTERPRETED);
+ USER_DATA(ctx)->dataModel.assign(key, d);
+ } else if (assign->content != NULL) {
+ Data d = Data(assign->content, Data::INTERPRETED);
+ USER_DATA(ctx)->dataModel.assign(key, d);
+ }
} catch (Event e) {
execContentRaise(ctx, e.name.c_str());
return USCXML_ERR_EXEC_CONTENT;
@@ -639,27 +633,32 @@ public:
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 if (data->content != NULL || data->src != NULL) {
+ if (data->content) {
+ content << data->content;
} else {
- sourceURL.toAbsoluteCwd();
+ URL sourceURL(data->src);
+ if (USER_DATA(ctx)->baseURL.size() > 0) {
+ sourceURL.toAbsolute(USER_DATA(ctx)->baseURL);
+ } else {
+ sourceURL.toAbsoluteCwd();
+ }
+ content << sourceURL;
}
- content << sourceURL;
- // d = Data(content.str(), Data::INTERPRETED);
+ /**
+ * first attempt to parse as structured data, we will try
+ * as space normalized string literals if this fails below
+ */
d = USER_DATA(ctx)->dataModel.getStringAsData(content.str());
+
} else {
d = Data("undefined", Data::INTERPRETED);
}
// this might fail with an unquoted string literal in content
USER_DATA(ctx)->dataModel.init(data->id, d);
+
} catch (Event e) {
if (content.str().size() > 0) {
try {
diff --git a/test/src/test-c-machine.scxml.c b/test/src/test-c-machine.scxml.c
index cbbfbfe..6378096 100644
--- a/test/src/test-c-machine.scxml.c
+++ b/test/src/test-c-machine.scxml.c
@@ -165,12 +165,14 @@ 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_assign uscxml_elem_assign;
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_enabled_t)(const uscxml_ctx* ctx, const uscxml_transition* transition);
+typedef int (*is_matched_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);
@@ -182,7 +184,7 @@ typedef int (*exec_content_send_t)(const uscxml_ctx* ctx, const uscxml_elem_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_assign_t)(const uscxml_ctx* ctx, const uscxml_elem_assign* assign);
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);
@@ -218,6 +220,15 @@ struct uscxml_elem_data {
};
/**
+ * All information pertaining to an <assign> element.
+ */
+struct uscxml_elem_assign {
+ const char* location;
+ const char* expr;
+ const char* content;
+};
+
+/**
* All information pertaining to any state element.
*/
struct uscxml_state {
@@ -335,6 +346,7 @@ struct uscxml_ctx {
dequeue_internal_t dequeue_internal;
dequeue_external_t dequeue_external;
is_enabled_t is_enabled;
+ is_matched_t is_matched;
is_true_t is_true;
raise_done_event_t raise_done_event;
@@ -360,6 +372,12 @@ extern const uscxml_machine _uscxml_EC83C2A5_machine;
#ifndef USCXML_NO_ELEM_INFO
+static const uscxml_elem_assign _uscxml_EC83C2A5_elem_assigns[2] = {
+ /* location, expr, content */
+ { "_ioprocessors", "'otherName'", NULL },
+ { "Var2", "_ioprocessors", NULL },
+};
+
static const uscxml_elem_data _uscxml_EC83C2A5_elem_datas[3] = {
/* id, src, expr, content */
{ "Var1", NULL, "_ioprocessors", NULL },
@@ -383,7 +401,7 @@ static const uscxml_elem_donedata _uscxml_EC83C2A5_elem_donedatas[1] = {
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;
+ if ((ctx->exec_content_assign(ctx, &_uscxml_EC83C2A5_elem_assigns[0])) != USCXML_ERR_OK) return err;
} else {
return USCXML_ERR_MISSING_CALLBACK;
}
@@ -403,7 +421,7 @@ static int _uscxml_EC83C2A5_s1_on_entry(const uscxml_ctx* ctx, const uscxml_stat
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;
+ if ((ctx->exec_content_assign(ctx, &_uscxml_EC83C2A5_elem_assigns[1])) != USCXML_ERR_OK) return err;
} else {
return USCXML_ERR_MISSING_CALLBACK;
}
@@ -836,7 +854,8 @@ SELECT_TRANSITIONS:
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) {
+ if ((ctx->event == NULL || ctx->is_matched(ctx, &USCXML_GET_TRANS(i), ctx->event) > 0) &&
+ (USCXML_GET_TRANS(i).condition == NULL || ctx->is_enabled(ctx, &USCXML_GET_TRANS(i)) > 0)) {
/* remember that we found a transition */
ctx->flags |= USCXML_CTX_TRANSITION_FOUND;