summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt23
-rw-r--r--apps/uscxml-transform.cpp21
-rw-r--r--src/bindings/swig/wrapped/WrappedDataModel.h4
-rw-r--r--src/uscxml/Convenience.cpp115
-rw-r--r--src/uscxml/Convenience.h27
-rw-r--r--src/uscxml/DOMUtils.cpp37
-rw-r--r--src/uscxml/DOMUtils.h28
-rw-r--r--src/uscxml/Factory.cpp2
-rw-r--r--src/uscxml/Factory.h3
-rw-r--r--src/uscxml/Interpreter.h60
-rw-r--r--src/uscxml/InterpreterInfo.h103
-rw-r--r--src/uscxml/plugins/DataModel.h8
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp14
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h2
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp2
-rw-r--r--src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h2
-rw-r--r--src/uscxml/plugins/datamodel/lua/LuaDataModel.cpp2
-rw-r--r--src/uscxml/plugins/datamodel/lua/LuaDataModel.h2
-rw-r--r--src/uscxml/plugins/datamodel/null/NULLDataModel.cpp2
-rw-r--r--src/uscxml/plugins/datamodel/null/NULLDataModel.h2
-rw-r--r--src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp2
-rw-r--r--src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h2
-rw-r--r--src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp2
-rw-r--r--src/uscxml/plugins/datamodel/promela/PromelaDataModel.h2
-rw-r--r--src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp2
-rw-r--r--src/uscxml/plugins/datamodel/xpath/XPathDataModel.h10
-rw-r--r--src/uscxml/transform/ChartToC.cpp1413
-rw-r--r--src/uscxml/transform/ChartToC.h89
-rw-r--r--src/uscxml/transform/ChartToCPP.cpp.todo546
-rw-r--r--src/uscxml/transform/ChartToCPP.h.todo72
-rw-r--r--src/uscxml/transform/Transformer.h2
-rw-r--r--test/CMakeLists.txt27
-rw-r--r--test/ctest/CTestCustom.ctest.in38
-rw-r--r--test/src/test-c-machine.cpp429
-rw-r--r--test/src/test-c-machine.machine.c532
-rw-r--r--test/src/test-misc.cpp3
-rw-r--r--test/src/test-w3c.cpp68
-rw-r--r--test/w3c/run_generated_c_test.cmake47
38 files changed, 2965 insertions, 780 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8a6067..bee9942 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -367,6 +367,8 @@ else()
OPTION(BUILD_TESTS_FSM_LUA "Create FSM converted W3C Lua tests" ON)
OPTION(BUILD_TESTS_FSM_PROLOG "Create FSM converted W3C Prolog tests" ON)
OPTION(BUILD_TESTS_FSM_PROMELA "Create FSM converted W3C Promela tests" ON)
+ OPTION(BUILD_TESTS_GENERATED_C "Create tests for generated C machines" ON)
+
endif()
OPTION(ENABLE_GCOV "Compile with gcov support" OFF)
@@ -746,14 +748,23 @@ if (APPLE OR IOS)
OPTION(DISABLE_AUDIOTOOLBOX "Ignore AudioToolbox" OFF)
if (NOT DISABLE_AUDIOTOOLBOX AND NOT DISABLE_ALL)
find_library(AUDIOTOOLBOX AudioToolbox REQUIRED)
- list (APPEND USCXML_OPT_LIBS ${AUDIOTOOLBOX}/AudioToolbox)
+ if (MACOSX_VERSION VERSION_GREATER "10.10.99")
+ # header files were moved it seems
+ list (APPEND USCXML_OPT_LIBS ${AUDIOTOOLBOX})
+ else()
+ list (APPEND USCXML_OPT_LIBS ${AUDIOTOOLBOX}/AudioToolbox)
+ endif()
set(AUDIOTOOLBOX_FOUND ON)
else()
set(AUDIOTOOLBOX_FOUND OFF)
endif()
find_library(FOUNDATION_LIBRARY Foundation)
- list (APPEND USCXML_OPT_LIBS ${FOUNDATION_LIBRARY}/Foundation)
-
+ if (MACOSX_VERSION VERSION_GREATER "10.10.99")
+ list (APPEND USCXML_OPT_LIBS ${FOUNDATION_LIBRARY})
+ else()
+ list (APPEND USCXML_OPT_LIBS ${FOUNDATION_LIBRARY}/Foundation)
+ endif()
+
if (IOS)
find_library(WTF_LIBRARY WTF)
find_library(ICU_LIBRARY icucore REQUIRED)
@@ -1009,7 +1020,11 @@ if (NOT DISABLE_OPENAL AND NOT DISABLE_ALL)
if (OPENAL_FOUND)
list (APPEND USCXML_INCLUDE_DIRS ${OPENAL_INCLUDE_DIR})
if (APPLE OR IOS)
- list (APPEND USCXML_OPT_LIBS ${OPENAL_LIBRARY}/OpenAL)
+ if (MACOSX_VERSION VERSION_GREATER "10.10.99")
+ list (APPEND USCXML_OPT_LIBS ${OPENAL_LIBRARY})
+ else()
+ list (APPEND USCXML_OPT_LIBS ${OPENAL_LIBRARY}/OpenAL)
+ endif()
else()
list (APPEND USCXML_OPT_LIBS ${OPENAL_LIBRARY})
endif()
diff --git a/apps/uscxml-transform.cpp b/apps/uscxml-transform.cpp
index 77537f5..95bb60b 100644
--- a/apps/uscxml-transform.cpp
+++ b/apps/uscxml-transform.cpp
@@ -1,6 +1,7 @@
#include "uscxml/config.h"
#include "uscxml/Interpreter.h"
#include "uscxml/transform/ChartToFlatSCXML.h"
+#include "uscxml/transform/ChartToC.h"
#include "uscxml/transform/ChartToTex.h"
#include "uscxml/transform/ChartToMinimalSCXML.h"
#include "uscxml/transform/ChartToPromela.h"
@@ -62,15 +63,16 @@ void printUsageAndExit(const char* progName) {
printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progStr.c_str());
printf("Usage\n");
printf("\t%s", progStr.c_str());
- printf(" [-t pml|flat|min|tex] [-a {OPTIONS}] [-v] [-lN]");
+ printf(" [-t c|pml|flat|min|tex] [-a {OPTIONS}] [-v] [-lN]");
#ifdef BUILD_AS_PLUGINS
printf(" [-p pluginPath]");
#endif
printf(" [-i URL] [-o FILE]");
printf("\n");
printf("Options\n");
- printf("\t-t flat : flatten to SCXML state-machine\n");
- printf("\t-t pml : convert to spin/promela program\n");
+ printf("\t-t c : convert to C program\n");
+ printf("\t-t pml : convert to spin/promela program\n");
+ printf("\t-t flat : flatten to SCXML state-machine\n");
printf("\t-t min : minimize SCXML state-chart\n");
printf("\t-t tex : write global state transition table as tex file\n");
printf("\t-a {OPTIONS} : annotate SCXML elements with comma seperated options\n");
@@ -220,6 +222,7 @@ int main(int argc, char** argv) {
if (outType != "flat" &&
outType != "scxml" &&
outType != "pml" &&
+ outType != "c" &&
outType != "min" &&
outType != "tex" &&
std::find(annotations.begin(), annotations.end(), "priority") == annotations.end() &&
@@ -264,6 +267,18 @@ int main(int argc, char** argv) {
}
}
+ if (outType == "c") {
+ if (outputFile.size() == 0 || outputFile == "-") {
+ ChartToC::transform(interpreter).writeTo(std::cout);
+ } else {
+ std::ofstream outStream;
+ outStream.open(outputFile.c_str());
+ ChartToC::transform(interpreter).writeTo(outStream);
+ outStream.close();
+ }
+ exit(EXIT_SUCCESS);
+ }
+
if (outType == "pml") {
if (outputFile.size() == 0 || outputFile == "-") {
ChartToPromela::transform(interpreter).writeTo(std::cout);
diff --git a/src/bindings/swig/wrapped/WrappedDataModel.h b/src/bindings/swig/wrapped/WrappedDataModel.h
index f33f5da..6927045 100644
--- a/src/bindings/swig/wrapped/WrappedDataModel.h
+++ b/src/bindings/swig/wrapped/WrappedDataModel.h
@@ -52,6 +52,10 @@ public:
return new WrappedDataModel();
}
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter) {
+ return boost::shared_ptr<DataModelImpl>(create(_interpreter));
+ }
+
virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter) {
_interpreter = interpreter->shared_from_this();
return boost::shared_ptr<DataModelImpl>(create(_interpreter));
diff --git a/src/uscxml/Convenience.cpp b/src/uscxml/Convenience.cpp
index 6013575..5d81db7 100644
--- a/src/uscxml/Convenience.cpp
+++ b/src/uscxml/Convenience.cpp
@@ -81,48 +81,97 @@ bool envVarIEquals(const char* name, const char* value) {
return iequals(envVarValue, value);
}
-std::string unescape(const std::string& a) {
+std::string escape(const std::string& a) {
std::stringstream b;
// see http://en.cppreference.com/w/cpp/language/escape
std::string::const_iterator it = a.begin();
while (it != a.end()) {
char c = *it++;
- if (c == '\\' && it != a.end()) {
- switch (*it++) {
- case '\\':
- c = '\\';
- break;
- case '0':
- c = '\0';
- break;
- case 'a':
- c = '\a';
- break;
- case 'b':
- c = '\b';
- break;
- case 'f':
- c = '\f';
- break;
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case 't':
- c = '\t';
- break;
- case 'v':
- c = '\v';
- break;
- }
- }
- b << c;
+ switch (c) {
+ case '\\':
+ b << '\\' << '\\';
+ break;
+ case '\0':
+ b << '\\' << '0';
+ break;
+ case '"':
+ b << '\\' << '"';
+ break;
+ case '\a':
+ b << '\\' << 'a';
+ break;
+ case '\b':
+ b << '\\' << 'b';
+ break;
+ case '\f':
+ b << '\\' << 'f';
+ break;
+ case '\n':
+ b << '\\' << 'n';
+ break;
+ case '\r':
+ b << '\\' << 'r';
+ break;
+ case '\t':
+ b << '\\' << 't';
+ break;
+ case '\v':
+ b << '\\' << 'v';
+ break;
+ default:
+ b << c;
+ }
}
return b.str();
}
+std::string unescape(const std::string& a) {
+ std::stringstream b;
+ // see http://en.cppreference.com/w/cpp/language/escape
+
+ std::string::const_iterator it = a.begin();
+ while (it != a.end()) {
+ char c = *it++;
+ if (c == '\\' && it != a.end()) {
+ switch (*it++) {
+ case '\\':
+ c = '\\';
+ break;
+ case '0':
+ c = '\0';
+ break;
+ case '"':
+ c = '"';
+ break;
+ case 'a':
+ c = '\a';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ }
+ }
+ b << c;
+ }
+
+ return b.str();
+}
+
}
diff --git a/src/uscxml/Convenience.h b/src/uscxml/Convenience.h
index cbc38d9..88afa8d 100644
--- a/src/uscxml/Convenience.h
+++ b/src/uscxml/Convenience.h
@@ -20,6 +20,7 @@
#ifndef CONVENIENCE_H_LU7GZ6CB
#define CONVENIENCE_H_LU7GZ6CB
+#include "uscxml/Common.h"
#include <inttypes.h>
#include <stdlib.h>
#include <boost/detail/endian.hpp>
@@ -43,6 +44,31 @@ template <typename T> T strTo(std::string tmp) {
return output;
}
+class USCXML_API NumAttr {
+public:
+ NumAttr(const std::string& str) {
+ size_t valueStart = str.find_first_of("0123456789.");
+ if (valueStart != std::string::npos) {
+ size_t valueEnd = str.find_last_of("0123456789.");
+ if (valueEnd != std::string::npos) {
+ value = str.substr(valueStart, (valueEnd - valueStart) + 1);
+ size_t unitStart = str.find_first_not_of(" \t", valueEnd + 1);
+ if (unitStart != std::string::npos) {
+ size_t unitEnd = str.find_last_of(" \t");
+ if (unitEnd != std::string::npos && unitEnd > unitStart) {
+ unit = str.substr(unitStart, unitEnd - unitStart);
+ } else {
+ unit = str.substr(unitStart, str.length() - unitStart);
+ }
+ }
+ }
+ }
+ }
+
+ std::string value;
+ std::string unit;
+};
+
bool isNumeric(const char* pszInput, int nNumberBase);
bool isInteger( const char* pszInput, int nNumberBase);
bool iequals(const std::string& a, const std::string& b);
@@ -51,6 +77,7 @@ bool stringIsTrue(const std::string& value);
bool envVarIsTrue(const char* name);
bool envVarIEquals(const char* name, const char* value);
+std::string escape(const std::string& a);
std::string unescape(const std::string& a);
// see http://www.cplusplus.com/forum/general/27544/
diff --git a/src/uscxml/DOMUtils.cpp b/src/uscxml/DOMUtils.cpp
index 5d72d5f..4805927 100644
--- a/src/uscxml/DOMUtils.cpp
+++ b/src/uscxml/DOMUtils.cpp
@@ -30,6 +30,43 @@ bool DOMUtils::attributeIsTrue(const::std::string& value) {
return stringIsTrue(value.c_str());
}
+std::string DOMUtils::idForNode(const Arabica::DOM::Node<std::string>& node) {
+ std::string nodeId;
+ std::string seperator;
+ Arabica::DOM::Node<std::string> curr = node;
+ while(curr) {
+ switch (curr.getNodeType()) {
+ case Arabica::DOM::Node_base::ELEMENT_NODE: {
+ Arabica::DOM::Element<std::string> elem = Arabica::DOM::Element<std::string>(curr);
+ if (HAS_ATTR(elem, "id") && !UUID::isUUID(ATTR(elem, "id"))) {
+ nodeId.insert(0, ATTR(elem, "id") + seperator);
+ seperator = "_";
+ return nodeId;
+ } else {
+ Arabica::DOM::Node<std::string> sibling = curr.getPreviousSibling();
+ int index = 0;
+ while(sibling) {
+ if (sibling.getNodeType() == Arabica::DOM::Node_base::ELEMENT_NODE) {
+ if (iequals(TAGNAME_CAST(sibling), TAGNAME(elem))) {
+ index++;
+ }
+ }
+ sibling = sibling.getPreviousSibling();
+ }
+ nodeId.insert(0, TAGNAME(elem) + toStr(index) + seperator);
+ seperator = "_";
+ }
+ break;
+ }
+ case Arabica::DOM::Node_base::DOCUMENT_NODE:
+ return nodeId;
+ }
+
+ curr = curr.getParentNode();
+ }
+ return nodeId;
+}
+
std::string DOMUtils::xPathForNode(const Arabica::DOM::Node<std::string>& node, const std::string& ns) {
std::string xPath;
std::string nsPrefix;
diff --git a/src/uscxml/DOMUtils.h b/src/uscxml/DOMUtils.h
index 0691952..fcd80d9 100644
--- a/src/uscxml/DOMUtils.h
+++ b/src/uscxml/DOMUtils.h
@@ -44,35 +44,11 @@ class USCXML_API DOMUtils {
public:
static std::string xPathForNode(const Arabica::DOM::Node<std::string>& node, const std::string& ns = "");
static std::list<Arabica::DOM::Node<std::string> > getElementsByType(const Arabica::DOM::Node<std::string>& root, Arabica::DOM::Node_base::Type type);
- // deprecated, use stringIsTrue from Convenience.h instead
+ static std::string idForNode(const Arabica::DOM::Node<std::string>& node);
+ // deprecated, use stringIsTrue from Convenience.h instead
DEPRECATED static bool attributeIsTrue(const::std::string& value);
};
-class USCXML_API NumAttr {
-public:
- NumAttr(const std::string& str) {
- size_t valueStart = str.find_first_of("0123456789.");
- if (valueStart != std::string::npos) {
- size_t valueEnd = str.find_last_of("0123456789.");
- if (valueEnd != std::string::npos) {
- value = str.substr(valueStart, (valueEnd - valueStart) + 1);
- size_t unitStart = str.find_first_not_of(" \t", valueEnd + 1);
- if (unitStart != std::string::npos) {
- size_t unitEnd = str.find_last_of(" \t");
- if (unitEnd != std::string::npos && unitEnd > unitStart) {
- unit = str.substr(unitStart, unitEnd - unitStart);
- } else {
- unit = str.substr(unitStart, str.length() - unitStart);
- }
- }
- }
- }
- }
-
- std::string value;
- std::string unit;
-};
-
class ScriptEntityResolver : public Arabica::SAX::EntityResolver<std::string> {
virtual InputSourceT resolveEntity(const std::string& publicId, const std::string& systemId) {
Arabica::SAX::InputSource<std::string> is;
diff --git a/src/uscxml/Factory.cpp b/src/uscxml/Factory.cpp
index 968aabd..9ff2148 100644
--- a/src/uscxml/Factory.cpp
+++ b/src/uscxml/Factory.cpp
@@ -569,7 +569,7 @@ bool Factory::hasDataModel(const std::string& type) {
return false;
}
-boost::shared_ptr<DataModelImpl> Factory::createDataModel(const std::string& type, InterpreterImpl* interpreter) {
+boost::shared_ptr<DataModelImpl> Factory::createDataModel(const std::string& type, InterpreterInfo* interpreter) {
// do we have this type ourself?
if (_dataModelAliases.find(type) != _dataModelAliases.end()) {
diff --git a/src/uscxml/Factory.h b/src/uscxml/Factory.h
index 45f8803..4fdd322 100644
--- a/src/uscxml/Factory.h
+++ b/src/uscxml/Factory.h
@@ -21,6 +21,7 @@
#define FACTORY_H_5WKLGPRB
#include "uscxml/Common.h"
+#include "uscxml/InterpreterInfo.h"
#include "uscxml/plugins/ExecutableContent.h"
#include "uscxml/plugins/EventHandler.h"
@@ -54,7 +55,7 @@ public:
void registerInvoker(InvokerImpl* invoker);
void registerExecutableContent(ExecutableContentImpl* executableContent);
- boost::shared_ptr<DataModelImpl> createDataModel(const std::string& type, InterpreterImpl* interpreter);
+ boost::shared_ptr<DataModelImpl> createDataModel(const std::string& type, InterpreterInfo* interpreter);
boost::shared_ptr<IOProcessorImpl> createIOProcessor(const std::string& type, InterpreterImpl* interpreter);
boost::shared_ptr<InvokerImpl> createInvoker(const std::string& type, InterpreterImpl* interpreter);
boost::shared_ptr<ExecutableContentImpl> createExecutableContent(const std::string& localName, const std::string& nameSpace, InterpreterImpl* interpreter);
diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h
index 4e450aa..945f6f2 100644
--- a/src/uscxml/Interpreter.h
+++ b/src/uscxml/Interpreter.h
@@ -28,6 +28,8 @@
#include <set>
#include <map>
+#include "uscxml/InterpreterInfo.h"
+
#include <XPath/XPath.hpp>
#include <DOM/Document.hpp>
@@ -150,62 +152,6 @@ public:
};
-class USCXML_API NameSpaceInfo {
-public:
- NameSpaceInfo() : nsContext(NULL) {
- init(std::map<std::string, std::string>());
- }
-
- NameSpaceInfo(const std::map<std::string, std::string>& nsInfo) : nsContext(NULL) {
- init(nsInfo);
- }
-
- NameSpaceInfo(const NameSpaceInfo& other) : nsContext(NULL) {
- init(other.nsInfo);
- }
-
- virtual ~NameSpaceInfo() {
- if (nsContext)
- delete nsContext;
- }
-
- NameSpaceInfo& operator=( const NameSpaceInfo& other ) {
- init(other.nsInfo);
- return *this;
- }
-
- void setPrefix(Arabica::DOM::Element<std::string> element) {
- if (nsURL.size() > 0)
- element.setPrefix(nsToPrefix[nsURL]);
- }
-
- void setPrefix(Arabica::DOM::Attr<std::string> attribute) {
- if (nsURL.size() > 0)
- attribute.setPrefix(nsToPrefix[nsURL]);
- }
-
- std::string getXMLPrefixForNS(const std::string& ns) const {
- if (nsToPrefix.find(ns) != nsToPrefix.end() && nsToPrefix.at(ns).size())
- return nsToPrefix.at(ns) + ":";
- return "";
- }
-
- const Arabica::XPath::StandardNamespaceContext<std::string>* getNSContext() {
- return nsContext;
- }
-
- std::string nsURL; // ough to be "http://www.w3.org/2005/07/scxml" but maybe empty
- std::string xpathPrefix; // prefix mapped for xpath, "scxml" is _xmlNSPrefix is empty but _nsURL set
- std::string xmlNSPrefix; // the actual prefix for elements in the xml file
- std::map<std::string, std::string> nsToPrefix; // prefixes for a given namespace
- std::map<std::string, std::string> nsInfo; // all xmlns mappings
-
-private:
- Arabica::XPath::StandardNamespaceContext<std::string>* nsContext;
-
- void init(const std::map<std::string, std::string>& nsInfo);
-};
-
enum InterpreterState {
USCXML_DESTROYED = -2, ///< destructor ran - users should never see this one
USCXML_FINISHED = -1, ///< machine reached a final configuration and is done
@@ -216,7 +162,7 @@ enum InterpreterState {
};
USCXML_API std::ostream& operator<< (std::ostream& os, const InterpreterState& interpreterState);
-class USCXML_API InterpreterImpl : public boost::enable_shared_from_this<InterpreterImpl> {
+class USCXML_API InterpreterImpl : public InterpreterInfo, public boost::enable_shared_from_this<InterpreterImpl> {
public:
typedef std::set<InterpreterMonitor*>::iterator monIter_t;
diff --git a/src/uscxml/InterpreterInfo.h b/src/uscxml/InterpreterInfo.h
new file mode 100644
index 0000000..490e414
--- /dev/null
+++ b/src/uscxml/InterpreterInfo.h
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * @author 2012-2015 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef INTERPRETERINFO_H_CED68EFF
+#define INTERPRETERINFO_H_CED68EFF
+
+#include <iostream>
+
+#include "uscxml/plugins/IOProcessor.h"
+#include "uscxml/plugins/Invoker.h"
+
+#include <XPath/XPath.hpp>
+#include <DOM/Document.hpp>
+
+
+namespace uscxml {
+
+class USCXML_API NameSpaceInfo {
+public:
+ NameSpaceInfo() : nsContext(NULL) {
+ init(std::map<std::string, std::string>());
+ }
+
+ NameSpaceInfo(const std::map<std::string, std::string>& nsInfo) : nsContext(NULL) {
+ init(nsInfo);
+ }
+
+ NameSpaceInfo(const NameSpaceInfo& other) : nsContext(NULL) {
+ init(other.nsInfo);
+ }
+
+ virtual ~NameSpaceInfo() {
+ if (nsContext)
+ delete nsContext;
+ }
+
+ NameSpaceInfo& operator=( const NameSpaceInfo& other ) {
+ init(other.nsInfo);
+ return *this;
+ }
+
+ void setPrefix(Arabica::DOM::Element<std::string> element) {
+ if (nsURL.size() > 0)
+ element.setPrefix(nsToPrefix[nsURL]);
+ }
+
+ void setPrefix(Arabica::DOM::Attr<std::string> attribute) {
+ if (nsURL.size() > 0)
+ attribute.setPrefix(nsToPrefix[nsURL]);
+ }
+
+ std::string getXMLPrefixForNS(const std::string& ns) const {
+ if (nsToPrefix.find(ns) != nsToPrefix.end() && nsToPrefix.at(ns).size())
+ return nsToPrefix.at(ns) + ":";
+ return "";
+ }
+
+ const Arabica::XPath::StandardNamespaceContext<std::string>* getNSContext() {
+ return nsContext;
+ }
+
+ std::string nsURL; // ough to be "http://www.w3.org/2005/07/scxml" but maybe empty
+ std::string xpathPrefix; // prefix mapped for xpath, "scxml" is _xmlNSPrefix is empty but _nsURL set
+ std::string xmlNSPrefix; // the actual prefix for elements in the xml file
+ std::map<std::string, std::string> nsToPrefix; // prefixes for a given namespace
+ std::map<std::string, std::string> nsInfo; // all xmlns mappings
+
+private:
+ Arabica::XPath::StandardNamespaceContext<std::string>* nsContext;
+
+ void init(const std::map<std::string, std::string>& nsInfo);
+};
+
+class USCXML_API InterpreterInfo {
+public:
+ virtual NameSpaceInfo getNameSpaceInfo() const = 0;
+ virtual const std::string& getName() = 0;
+ virtual const std::string& getSessionId() = 0;
+ virtual const std::map<std::string, IOProcessor>& getIOProcessors() = 0;
+ virtual bool isInState(const std::string& stateId) = 0;
+ virtual Arabica::DOM::Document<std::string> getDocument() const = 0;
+ virtual const std::map<std::string, Invoker>& getInvokers() = 0;
+};
+
+}
+
+#endif /* end of include guard: INTERPRETERINFO_H_CED68EFF */
diff --git a/src/uscxml/plugins/DataModel.h b/src/uscxml/plugins/DataModel.h
index 05f89d5..563186e 100644
--- a/src/uscxml/plugins/DataModel.h
+++ b/src/uscxml/plugins/DataModel.h
@@ -48,7 +48,7 @@ public:
class USCXML_API DataModelImpl {
public:
virtual ~DataModelImpl() {}
- virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter) = 0;
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter) = 0;
virtual std::list<std::string> getNames() = 0;
virtual bool validate(const std::string& location, const std::string& schema) = 0;
@@ -93,7 +93,7 @@ public:
const std::string& content) = 0;
virtual void init(const std::string& location, const Data& data) = 0;
- virtual void setInterpreter(InterpreterImpl* interpreter) {
+ virtual void setInterpreter(InterpreterInfo* interpreter) {
_interpreter = interpreter;
}
@@ -103,7 +103,7 @@ public:
}
protected:
- InterpreterImpl* _interpreter;
+ InterpreterInfo* _interpreter;
};
class USCXML_API DataModel {
@@ -214,7 +214,7 @@ public:
return _impl->andExpressions(expressions);
}
- virtual void setInterpreter(InterpreterImpl* interpreter) {
+ virtual void setInterpreter(InterpreterInfo* interpreter) {
_impl->setInterpreter(interpreter);
}
diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
index dc0e281..551b590 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.cpp
@@ -189,18 +189,18 @@ JSClassDefinition JSCDataModel::jsExtensionClassDef = { 0, 0, "Extension", 0, 0,
JSClassDefinition JSCDataModel::jsIOProcessorsClassDef = { 0, 0, "ioProcessors", 0, 0, 0, 0, 0, jsIOProcessorHasProp, jsIOProcessorGetProp, 0, 0, jsIOProcessorListProps, 0, 0, 0, 0 };
JSClassDefinition JSCDataModel::jsInvokersClassDef = { 0, 0, "invokers", 0, 0, 0, 0, 0, jsInvokerHasProp, jsInvokerGetProp, 0, 0, jsInvokerListProps, 0, 0, 0, 0 };
-boost::shared_ptr<DataModelImpl> JSCDataModel::create(InterpreterImpl* interpreter) {
+boost::shared_ptr<DataModelImpl> JSCDataModel::create(InterpreterInfo* interpreter) {
boost::shared_ptr<JSCDataModel> dm = boost::shared_ptr<JSCDataModel>(new JSCDataModel());
dm->_ctx = JSGlobalContextCreate(NULL);
dm->_interpreter = interpreter;
- dm->_dom = new JSCDOM();
- dm->_dom->xpath = new XPath<std::string>();
- dm->_dom->xpath->setNamespaceContext(*interpreter->getNameSpaceInfo().getNSContext());
- dm->_dom->storage = new Storage(URL::getResourceDir() + PATH_SEPERATOR + interpreter->getName() + ".storage");
- dm->_dom->nsInfo = new NameSpaceInfo(interpreter->getNameSpaceInfo());
-
+ dm->_dom = new JSCDOM();
+ dm->_dom->xpath = new XPath<std::string>();
+ dm->_dom->xpath->setNamespaceContext(*interpreter->getNameSpaceInfo().getNSContext());
+ dm->_dom->storage = new Storage(URL::getResourceDir() + PATH_SEPERATOR + interpreter->getName() + ".storage");
+ dm->_dom->nsInfo = new NameSpaceInfo(interpreter->getNameSpaceInfo());
+
// introduce global functions as objects for private data
JSClassRef jsInClassRef = JSClassCreate(&jsInClassDef);
JSObjectRef jsIn = JSObjectMake(dm->_ctx, jsInClassRef, dm.get());
diff --git a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
index c2ba01c..944d185 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
+++ b/src/uscxml/plugins/datamodel/ecmascript/JavaScriptCore/JSCDataModel.h
@@ -40,7 +40,7 @@ class JSCDataModel : public DataModelImpl {
public:
JSCDataModel();
virtual ~JSCDataModel();
- virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter);
virtual void addExtension(DataModelExtension* ext);
diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
index 5f18414..99df4a7 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
+++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.cpp
@@ -153,7 +153,7 @@ v8::Handle<v8::Value> V8DataModel::jsExtension(const v8::Arguments& args) {
return v8::Undefined();
}
-boost::shared_ptr<DataModelImpl> V8DataModel::create(InterpreterImpl* interpreter) {
+boost::shared_ptr<DataModelImpl> V8DataModel::create(InterpreterInfo* interpreter) {
boost::shared_ptr<V8DataModel> dm = boost::shared_ptr<V8DataModel>(new V8DataModel());
dm->_interpreter = interpreter;
v8::Locker locker;
diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h
index 9e2ec71..35cc12d 100644
--- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h
+++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DataModel.h
@@ -41,7 +41,7 @@ class V8DataModel : public DataModelImpl {
public:
V8DataModel();
virtual ~V8DataModel();
- virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter);
virtual std::list<std::string> getNames() {
std::list<std::string> names;
diff --git a/src/uscxml/plugins/datamodel/lua/LuaDataModel.cpp b/src/uscxml/plugins/datamodel/lua/LuaDataModel.cpp
index 6e9b237..c55e143 100644
--- a/src/uscxml/plugins/datamodel/lua/LuaDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/lua/LuaDataModel.cpp
@@ -139,7 +139,7 @@ static int luaInFunction(lua_State * l) {
return 1;
}
-boost::shared_ptr<DataModelImpl> LuaDataModel::create(InterpreterImpl* interpreter) {
+boost::shared_ptr<DataModelImpl> LuaDataModel::create(InterpreterInfo* interpreter) {
boost::shared_ptr<LuaDataModel> dm = boost::shared_ptr<LuaDataModel>(new LuaDataModel());
dm->_interpreter = interpreter;
dm->_luaState = luaL_newstate();
diff --git a/src/uscxml/plugins/datamodel/lua/LuaDataModel.h b/src/uscxml/plugins/datamodel/lua/LuaDataModel.h
index 39990c6..bce1d62 100644
--- a/src/uscxml/plugins/datamodel/lua/LuaDataModel.h
+++ b/src/uscxml/plugins/datamodel/lua/LuaDataModel.h
@@ -44,7 +44,7 @@ class LuaDataModel : public DataModelImpl {
public:
LuaDataModel();
virtual ~LuaDataModel();
- virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter);
virtual std::list<std::string> getNames() {
std::list<std::string> names;
diff --git a/src/uscxml/plugins/datamodel/null/NULLDataModel.cpp b/src/uscxml/plugins/datamodel/null/NULLDataModel.cpp
index ae75c88..c9f0d5a 100644
--- a/src/uscxml/plugins/datamodel/null/NULLDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/null/NULLDataModel.cpp
@@ -43,7 +43,7 @@ bool pluginConnect(pluma::Host& host) {
NULLDataModel::NULLDataModel() {
}
-boost::shared_ptr<DataModelImpl> NULLDataModel::create(InterpreterImpl* interpreter) {
+boost::shared_ptr<DataModelImpl> NULLDataModel::create(InterpreterInfo* interpreter) {
boost::shared_ptr<NULLDataModel> dm = boost::shared_ptr<NULLDataModel>(new NULLDataModel());
dm->_interpreter = interpreter;
return dm;
diff --git a/src/uscxml/plugins/datamodel/null/NULLDataModel.h b/src/uscxml/plugins/datamodel/null/NULLDataModel.h
index da0374e..db5fa28 100644
--- a/src/uscxml/plugins/datamodel/null/NULLDataModel.h
+++ b/src/uscxml/plugins/datamodel/null/NULLDataModel.h
@@ -38,7 +38,7 @@ class NULLDataModel : public DataModelImpl {
public:
NULLDataModel();
virtual ~NULLDataModel();
- virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter);
virtual std::list<std::string> getNames() {
std::list<std::string> names;
diff --git a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp
index 21b390a..09b5e15 100644
--- a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.cpp
@@ -89,7 +89,7 @@ SWIDataModel::~SWIDataModel() {
RETHROW_PLEX_AS_EVENT;
}
-boost::shared_ptr<DataModelImpl> SWIDataModel::create(InterpreterImpl* interpreter) {
+boost::shared_ptr<DataModelImpl> SWIDataModel::create(InterpreterInfo* interpreter) {
try {
boost::shared_ptr<SWIDataModel> dm = boost::shared_ptr<SWIDataModel>(new SWIDataModel());
dm->_interpreter = interpreter;
diff --git a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h
index b53afba..398e24c 100644
--- a/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h
+++ b/src/uscxml/plugins/datamodel/prolog/swi/SWIDataModel.h
@@ -53,7 +53,7 @@ public:
SWIDataModel();
virtual ~SWIDataModel();
- virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter);
virtual std::list<std::string> getNames() {
std::list<std::string> names;
diff --git a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp
index 2ca2bb3..6e82b55 100644
--- a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.cpp
@@ -59,7 +59,7 @@ PromelaDataModel::PromelaDataModel() {
PromelaDataModel::~PromelaDataModel() {
}
-boost::shared_ptr<DataModelImpl> PromelaDataModel::create(InterpreterImpl* interpreter) {
+boost::shared_ptr<DataModelImpl> PromelaDataModel::create(InterpreterInfo* interpreter) {
boost::shared_ptr<PromelaDataModel> dm = boost::shared_ptr<PromelaDataModel>(new PromelaDataModel());
dm->_interpreter = interpreter;
diff --git a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.h b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.h
index 25fe536..060560d 100644
--- a/src/uscxml/plugins/datamodel/promela/PromelaDataModel.h
+++ b/src/uscxml/plugins/datamodel/promela/PromelaDataModel.h
@@ -33,7 +33,7 @@ class PromelaDataModel : public DataModelImpl {
public:
PromelaDataModel();
virtual ~PromelaDataModel();
- virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter);
virtual std::list<std::string> getNames() {
std::list<std::string> names;
diff --git a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp
index 8822579..de8eecb 100644
--- a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp
+++ b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.cpp
@@ -46,7 +46,7 @@ bool pluginConnect(pluma::Host& host) {
XPathDataModel::XPathDataModel() {
}
-boost::shared_ptr<DataModelImpl> XPathDataModel::create(InterpreterImpl* interpreter) {
+boost::shared_ptr<DataModelImpl> XPathDataModel::create(InterpreterInfo* interpreter) {
boost::shared_ptr<XPathDataModel> dm = boost::shared_ptr<XPathDataModel>(new XPathDataModel());
dm->_interpreter = interpreter;
diff --git a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.h b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.h
index fe454b3..9a71b79 100644
--- a/src/uscxml/plugins/datamodel/xpath/XPathDataModel.h
+++ b/src/uscxml/plugins/datamodel/xpath/XPathDataModel.h
@@ -39,14 +39,14 @@ public:
XPathFunctionIn(int minArgs,
int maxArgs,
const std::vector<Arabica::XPath::XPathExpression<std::string> >& args,
- InterpreterImpl* interpreter) :
+ InterpreterInfo* interpreter) :
Arabica::XPath::BooleanXPathFunction<std::string>(minArgs, maxArgs, args),
_interpreter(interpreter) {}
protected:
bool doEvaluate(const Arabica::DOM::Node<std::string>& context,
const Arabica::XPath::ExecutionContext<std::string>& executionContext) const;
- InterpreterImpl* _interpreter;
+ InterpreterInfo* _interpreter;
};
class XPathFunctionResolver : public Arabica::XPath::FunctionResolver<std::string> {
@@ -59,12 +59,12 @@ public:
const std::vector<Arabica::XPath::XPathExpression<std::string> >& argExprs) const;
virtual std::vector<std::pair<std::string, std::string> > validNames() const;
- void setInterpreter(InterpreterImpl* interpreter) {
+ void setInterpreter(InterpreterInfo* interpreter) {
_interpreter = interpreter;
}
protected:
Arabica::XPath::StandardXPathFunctionResolver<std::string> _xpathFuncRes;
- InterpreterImpl* _interpreter;
+ InterpreterInfo* _interpreter;
};
class NodeSetVariableResolver : public Arabica::XPath::VariableResolver<std::string> {
@@ -83,7 +83,7 @@ class XPathDataModel : public DataModelImpl {
public:
XPathDataModel();
virtual ~XPathDataModel();
- virtual boost::shared_ptr<DataModelImpl> create(InterpreterImpl* interpreter);
+ virtual boost::shared_ptr<DataModelImpl> create(InterpreterInfo* interpreter);
virtual std::list<std::string> getNames() {
std::list<std::string> names;
diff --git a/src/uscxml/transform/ChartToC.cpp b/src/uscxml/transform/ChartToC.cpp
new file mode 100644
index 0000000..574478d
--- /dev/null
+++ b/src/uscxml/transform/ChartToC.cpp
@@ -0,0 +1,1413 @@
+/**
+ * @file
+ * @author 2012-2015 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#include "uscxml/transform/ChartToFSM.h"
+#include "uscxml/transform/ChartToC.h"
+#include "uscxml/debug/Complexity.h"
+#include <DOM/io/Stream.hpp>
+#include <iostream>
+#include "uscxml/UUID.h"
+#include "uscxml/DOMUtils.h"
+#include <math.h>
+#include <boost/algorithm/string.hpp>
+#include <glog/logging.h>
+
+#include <algorithm>
+#include <iomanip>
+
+namespace uscxml {
+
+using namespace Arabica::DOM;
+using namespace Arabica::XPath;
+
+Transformer ChartToC::transform(const Interpreter& other) {
+ ChartToC* c2c = new ChartToC(other);
+
+ return boost::shared_ptr<TransformerImpl>(c2c);
+}
+
+ChartToC::ChartToC(const Interpreter& other) : TransformerImpl() {
+ cloneFrom(other.getImpl());
+}
+
+void ChartToC::writeTo(std::ostream& stream) {
+ _binding = (HAS_ATTR(_scxml, "binding") && iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY);
+
+ std::set<std::string> elements;
+ elements.insert(_nsInfo.xmlNSPrefix + "scxml");
+ elements.insert(_nsInfo.xmlNSPrefix + "state");
+ elements.insert(_nsInfo.xmlNSPrefix + "final");
+ elements.insert(_nsInfo.xmlNSPrefix + "parallel");
+ elements.insert(_nsInfo.xmlNSPrefix + "history");
+ elements.insert(_nsInfo.xmlNSPrefix + "initial");
+ elements.insert(_nsInfo.xmlNSPrefix + "parallel");
+ _states = inDocumentOrder(elements, _scxml);
+
+ for (int i = 0; i < _states.size(); i++) {
+ Element<std::string> state(_states[i]);
+ state.setAttribute("documentOrder", toStr(i));
+ if (HAS_ATTR(state, "id")) {
+ _stateNames[ATTR(state, "id")] = state;
+ }
+ }
+
+ elements.clear();
+ elements.insert(_nsInfo.xmlNSPrefix + "transition");
+ _transitions = inPostFixOrder(elements, _scxml);
+
+ for (int i = 0; i < _transitions.size(); i++) {
+ Element<std::string> transition(_transitions[i]);
+ transition.setAttribute("postFixOrder", toStr(i));
+ }
+
+ // how many bits do we need to represent the state array?
+ std::string seperator;
+ _stateCharArraySize = ceil((float)_states.size() / (float)8);
+ _stateCharArrayInit = "{";
+ for (int i = 0; i < _stateCharArraySize; i++) {
+ _stateCharArrayInit += seperator + "0";
+ seperator = ", ";
+ }
+ _stateCharArrayInit += "}";
+
+ seperator = "";
+ _transCharArraySize = ceil((float)_transitions.size() / (float)8);
+ _transCharArrayInit = "{";
+ for (int i = 0; i < _transCharArraySize; i++) {
+ _transCharArrayInit += seperator + "0";
+ seperator = ", ";
+ }
+ _transCharArrayInit += "}";
+
+ writeIncludes(stream);
+ writeMacros(stream);
+ writeTypes(stream);
+ writeElementInfo(stream);
+ writeExecContent(stream);
+ writeStates(stream);
+ writeTransitions(stream);
+ writeHelpers(stream);
+ writeFSM(stream);
+
+ // http://stackoverflow.com/questions/2525310/how-to-define-and-work-with-an-array-of-bits-in-c
+
+}
+
+void ChartToC::writeIncludes(std::ostream& stream) {
+ stream << "#include <stdint.h> // explicit types" << std::endl;
+ stream << "#include <stddef.h> // NULL" << std::endl;
+ stream << std::endl;
+}
+
+void ChartToC::writeMacros(std::ostream& stream) {
+ stream << "#define IS_SET(idx, bitset) ((bitset[idx >> 3] & (1 << (idx & 7))) != 0)" << std::endl;
+ stream << "#define SET_BIT(idx, bitset) bitset[idx >> 3] |= (1 << (idx & 7));" << std::endl;
+ stream << "#define CLEARBIT(idx, bitset) bitset[idx >> 3] &= (1 << (idx & 7)) ^ 0xFF;" << std::endl;
+ stream << std::endl;
+
+ stream << "// error return codes" << std::endl;
+ stream << "#define SCXML_ERR_OK 0" << std::endl;
+ stream << "#define SCXML_ERR_IDLE 1" << std::endl;
+ stream << "#define SCXML_ERR_DONE 2" << std::endl;
+ stream << "#define SCXML_ERR_MISSING_CALLBACK 3" << std::endl;
+ stream << "#define SCXML_ERR_FOREACH_DONE 4" << std::endl;
+ stream << "#define SCXML_ERR_EXEC_CONTENT 5" << std::endl;
+ stream << "#define SCXML_ERR_INVALID_TARGET 6" << std::endl;
+ stream << "#define SCXML_ERR_INVALID_TYPE 7" << std::endl;
+ stream << std::endl;
+
+ stream << "#define SCXML_NUMBER_STATES " << _states.size() << std::endl;
+ stream << "#define SCXML_NUMBER_TRANSITIONS " << _transitions.size() << std::endl;
+ stream << std::endl;
+
+ stream << "#define SCXML_TRANS_SPONTANEOUS 0x01" << std::endl;
+ stream << "#define SCXML_TRANS_TARGETLESS 0x02" << std::endl;
+ stream << "#define SCXML_TRANS_INTERNAL 0x04" << std::endl;
+ stream << std::endl;
+
+ stream << "#define SCXML_STATE_ATOMIC 0x01" << std::endl;
+ stream << "#define SCXML_STATE_PARALLEL 0x02" << std::endl;
+ stream << "#define SCXML_STATE_COMPOUND 0x03" << std::endl;
+ stream << "#define SCXML_STATE_FINAL 0x04" << std::endl;
+ stream << "#define SCXML_STATE_HISTORY_DEEP 0x05" << std::endl;
+ stream << "#define SCXML_STATE_HISTORY_SHALLOW 0x06" << std::endl;
+ stream << "#define SCXML_STATE_INITIAL 0x07" << std::endl;
+
+ stream << "" << std::endl;
+ stream << "#define SCXML_CTX_PRISTINE 0x00" << std::endl;
+ stream << "#define SCXML_CTX_SPONTANEOUS 0x01" << std::endl;
+ stream << "#define SCXML_CTX_INITIALIZED 0x02" << std::endl;
+ stream << "#define SCXML_CTX_TOP_LEVEL_FINAL 0x04" << std::endl;
+ stream << "#define SCXML_CTX_TRANSITION_FOUND 0x08" << std::endl;
+ stream << std::endl;
+
+ stream << "#define ELEM_DATA_IS_SET(data) (data->id != NULL)" << std::endl;
+ stream << "#define ELEM_PARAM_IS_SET(param) (param->name != NULL)" << std::endl;
+ stream << std::endl;
+}
+
+void ChartToC::writeTypes(std::ostream& stream) {
+
+ stream << std::endl;
+ stream << "typedef struct scxml_transition scxml_transition;" << std::endl;
+ stream << "typedef struct scxml_state scxml_state;" << std::endl;
+ stream << "typedef struct scxml_ctx scxml_ctx;" << std::endl;
+ stream << "typedef struct scxml_invoke scxml_invoke;" << std::endl;
+
+ stream << std::endl;
+
+ stream << "typedef struct scxml_elem_send scxml_elem_send;" << std::endl;
+ stream << "typedef struct scxml_elem_param scxml_elem_param;" << std::endl;
+ stream << "typedef struct scxml_elem_data scxml_elem_data;" << std::endl;
+ stream << "typedef struct scxml_elem_foreach scxml_elem_foreach;" << std::endl;
+ stream << std::endl;
+
+ stream << "typedef void* (*dequeue_internal_cb_t)(const scxml_ctx* ctx);" << std::endl;
+ stream << "typedef void* (*dequeue_external_cb_t)(const scxml_ctx* ctx);" << std::endl;
+ stream << "typedef int (*is_enabled_cb_t)(const scxml_ctx* ctx, const scxml_transition* transition, const void* event);" << std::endl;
+ stream << "typedef int (*is_true_cb_t)(const scxml_ctx* ctx, const char* expr);" << std::endl;
+ stream << "typedef int (*exec_content_t)(const scxml_ctx* ctx, const scxml_state* state, const void* event);" << std::endl;
+ stream << "typedef int (*raise_done_event_t)(const scxml_ctx* ctx, const scxml_state* state);" << std::endl;
+ stream << "typedef int (*invoke_t)(const scxml_ctx* ctx, const scxml_state* s, const scxml_invoke* x);" << std::endl;
+ stream << std::endl;
+
+ stream << "typedef int (*exec_content_log_t)(const scxml_ctx* ctx, const char* label, const char* expr);" << std::endl;
+ stream << "typedef int (*exec_content_raise_t)(const scxml_ctx* ctx, const char* event);" << std::endl;
+ stream << "typedef int (*exec_content_send_t)(const scxml_ctx* ctx, const scxml_elem_send* send);" << std::endl;
+ stream << "typedef int (*exec_content_foreach_init_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);" << std::endl;
+ stream << "typedef int (*exec_content_foreach_next_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);" << std::endl;
+ stream << "typedef int (*exec_content_foreach_done_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);" << std::endl;
+ stream << "typedef int (*exec_content_assign_t)(const scxml_ctx* ctx, const char* location, const char* expr);" << std::endl;
+ stream << "typedef int (*exec_content_init_t)(const scxml_ctx* ctx, const scxml_elem_data* data);" << std::endl;
+ stream << "typedef int (*exec_content_cancel_t)(const scxml_ctx* ctx, const char* sendid, const char* sendidexpr);" << std::endl;
+ stream << "typedef int (*exec_content_finalize_t)(const scxml_ctx* ctx, const scxml_invoke* invoker, const void* event);" << std::endl;
+ stream << "typedef int (*exec_content_script_t)(const scxml_ctx* ctx, const char* src, const char* content);" << std::endl;
+ stream << std::endl;
+
+ stream << "struct scxml_elem_data {" << std::endl;
+ stream << " const char* id;" << std::endl;
+ stream << " const char* src;" << std::endl;
+ stream << " const char* expr;" << std::endl;
+ stream << " const char* content;" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ stream << "struct scxml_state {" << std::endl;
+ stream << " const char* name; // eventual name" << std::endl;
+ stream << " exec_content_t on_entry; // on entry handlers" << std::endl;
+ stream << " exec_content_t on_exit; // on exit handlers" << std::endl;
+ stream << " invoke_t invoke; // invocations" << std::endl;
+ stream << " char children[" << _stateCharArraySize << "]; // all children" << std::endl;
+ stream << " char completion[" << _stateCharArraySize << "]; // default completion" << std::endl;
+ stream << " char ancestors[" << _stateCharArraySize << "]; // all ancestors" << std::endl;
+ stream << " const scxml_elem_data* data;" << std::endl;
+ stream << " uint8_t type; // atomic, parallel, compound, final, history" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ stream << "struct scxml_transition {" << std::endl;
+ stream << " uint16_t source;" << std::endl;
+ stream << " char target[" << _stateCharArraySize << "];" << std::endl;
+ stream << " const char* event;" << std::endl;
+ stream << " const char* condition;" << std::endl;
+ stream << " exec_content_t on_transition;" << std::endl;
+ stream << " uint8_t type;" << std::endl;
+ stream << " char conflicts[" << _transCharArraySize << "];" << std::endl;
+ stream << " char exit_set[" << _stateCharArraySize << "];" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ stream << "struct scxml_elem_foreach {" << std::endl;
+ stream << " const char* array;" << std::endl;
+ stream << " const char* item;" << std::endl;
+ stream << " const char* index;" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ stream << "struct scxml_elem_param {" << std::endl;
+ stream << " const char* name;" << std::endl;
+ stream << " const char* expr;" << std::endl;
+ stream << " const char* location;" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ stream << "struct scxml_elem_invoke {" << std::endl;
+ stream << " const char* type;" << std::endl;
+ stream << " const char* typeexpr;" << std::endl;
+ stream << " const char* src;" << std::endl;
+ stream << " const char* srcexpr;" << std::endl;
+ stream << " const char* id;" << std::endl;
+ stream << " const char* idlocation;" << std::endl;
+ stream << " const char* namelist;" << std::endl;
+ stream << " uint8_t autoforward;" << std::endl;
+ stream << " const scxml_elem_param* params;" << std::endl;
+ stream << " const exec_content_finalize_t* finalize;" << std::endl;
+ stream << " const char* content;" << std::endl;
+ stream << " void* user_data;" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ stream << "struct scxml_elem_send {" << std::endl;
+ stream << " const char* event;" << std::endl;
+ stream << " const char* eventexpr;" << std::endl;
+ stream << " const char* target;" << std::endl;
+ stream << " const char* targetexpr;" << std::endl;
+ stream << " const char* type;" << std::endl;
+ stream << " const char* typeexpr;" << std::endl;
+ stream << " const char* id;" << std::endl;
+ stream << " const char* idlocation;" << std::endl;
+ stream << " const char* delay;" << std::endl;
+ stream << " const char* delayexpr;" << std::endl;
+ stream << " const char* namelist;" << std::endl;
+ stream << " const char* content;" << std::endl;
+ stream << " const scxml_elem_param* params;" << std::endl;
+ stream << " void* user_data;" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+
+ stream << "struct scxml_ctx {" << std::endl;
+ stream << " uint8_t flags;" << std::endl;
+ stream << std::endl;
+ stream << " char config[" << _stateCharArraySize << "];" << std::endl;
+ stream << " char history[" << _stateCharArraySize << "];" << std::endl;
+ stream << " char pending_invokes[" << _stateCharArraySize << "];" << std::endl;
+ stream << " char initialized_data[" << _stateCharArraySize << "];" << std::endl;
+ stream << std::endl;
+ stream << " void* user_data;" << std::endl;
+ stream << std::endl;
+ stream << " dequeue_internal_cb_t dequeue_internal;" << std::endl;
+ stream << " dequeue_external_cb_t dequeue_external;" << std::endl;
+ stream << " is_enabled_cb_t is_enabled;" << std::endl;
+ stream << " is_true_cb_t is_true;" << std::endl;
+ stream << " raise_done_event_t raise_done_event;" << std::endl;
+ stream << std::endl;
+ stream << " exec_content_log_t exec_content_log;" << std::endl;
+ stream << " exec_content_raise_t exec_content_raise;" << std::endl;
+ stream << " exec_content_send_t exec_content_send;" << std::endl;
+ stream << " exec_content_foreach_init_t exec_content_foreach_init;" << std::endl;
+ stream << " exec_content_foreach_next_t exec_content_foreach_next;" << std::endl;
+ stream << " exec_content_foreach_done_t exec_content_foreach_done;" << std::endl;
+ stream << " exec_content_assign_t exec_content_assign;" << std::endl;
+ stream << " exec_content_init_t exec_content_init;" << std::endl;
+ stream << " exec_content_cancel_t exec_content_cancel;" << std::endl;
+ stream << " exec_content_script_t exec_content_script;" << std::endl;
+ stream << " invoke_t invoke;" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+}
+
+void ChartToC::writeHelpers(std::ostream& stream) {
+ stream << "#ifdef SCXML_VERBOSE" << std::endl;
+ stream << "void printStateNames(const char* a) {" << std::endl;
+ stream << " const char* seperator = \"\";" << std::endl;
+ stream << " for (int i = 0; i < SCXML_NUMBER_STATES; i++) {" << std::endl;
+ stream << " if (IS_SET(i, a)) {" << std::endl;
+ stream << " printf(\"%s%s\", seperator, (scxml_states[i].name != NULL ? scxml_states[i].name : \"UNK\"));" << std::endl;
+ stream << " seperator = \", \";" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " printf(\"\\n\");" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+
+ stream << "void printBitsetIndices(const char* a, size_t length) {" << std::endl;
+ stream << " const char* seperator = \"\";" << std::endl;
+ stream << " for (int i = 0; i < length; i++) {" << std::endl;
+ stream << " if (IS_SET(i, a)) {" << std::endl;
+ stream << " printf(\"%s%d\", seperator, i);" << std::endl;
+ stream << " seperator = \", \";" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " printf(\"\\n\");" << std::endl;
+ stream << "}" << std::endl;
+
+ stream << "#endif" << std::endl;
+ stream << std::endl;
+
+ stream << "void bit_or(char* dest, const char* mask, size_t length) {" << std::endl;
+ stream << " for (int i = 0; i < length; ++i) {" << std::endl;
+ stream << " dest[i] |= mask[i];" << std::endl;
+ stream << " }" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+
+ stream << "void bit_copy(char* dest, const char* source, size_t length) {" << std::endl;
+ stream << " for (int i = 0; i < length; ++i) {" << std::endl;
+ stream << " dest[i] = source[i];" << std::endl;
+ stream << " }" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+
+ stream << "int bit_has_and(const char* a, const char* b, size_t length) {" << std::endl;
+ stream << " for (int i = 0; i < length; ++i) {" << std::endl;
+ stream << " if (a[i] & b[i])" << std::endl;
+ stream << " return true;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " return false;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+
+ stream << "void bit_and_not(char* dest, const char* mask, size_t length) {" << std::endl;
+ stream << " for (int i = 0; i < length; ++i) {" << std::endl;
+ stream << " dest[i] &= ~mask[i];" << std::endl;
+ stream << " }" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+
+ stream << "int bit_any_set(const char* a, size_t length) {" << std::endl;
+ stream << " for (int i = 0; i < length; ++i) {" << std::endl;
+ stream << " if (a[i] > 0)" << std::endl;
+ stream << " return true;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " return false;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+
+}
+
+void ChartToC::writeExecContent(std::ostream& stream) {
+ for (int i = 0; i < _states.size(); i++) {
+ Element<std::string> state(_states[i]);
+
+ NodeSet<std::string> onexit = filterChildElements(_nsInfo.xmlNSPrefix + "onexit", state);
+ for (int j = 0; j < onexit.size(); j++) {
+ stream << "int " << DOMUtils::idForNode(state) << "_on_exit_" << toStr(j) << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << " int err = SCXML_ERR_OK;" << std::endl;
+ writeExecContent(stream, onexit[j], 1);
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+ }
+
+ if (onexit.size() > 0) {
+ stream << "int " << DOMUtils::idForNode(state) << "_on_exit(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ for (int j = 0; j < onexit.size(); j++) {
+ stream << " " << DOMUtils::idForNode(state) << "_on_exit_" << toStr(j) << "(ctx, state, event);" << std::endl;
+ }
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+ }
+
+
+ NodeSet<std::string> onentry = filterChildElements(_nsInfo.xmlNSPrefix + "onentry", state);
+ for (int j = 0; j < onentry.size(); j++) {
+ stream << "int " << DOMUtils::idForNode(state) << "_on_entry_" << toStr(j) << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << " int err = SCXML_ERR_OK;" << std::endl;
+ writeExecContent(stream, onentry[j], 1);
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+ }
+
+ bool hasInitialState = false;
+ NodeSet<std::string> initial = filterChildElements(_nsInfo.xmlNSPrefix + "initial", state);
+ if (initial.size() > 0) {
+ NodeSet<std::string> initialTransition = filterChildElements(_nsInfo.xmlNSPrefix + "transition", initial);
+ if (initialTransition.size() > 0) {
+ hasInitialState = true;
+ stream << "int " << DOMUtils::idForNode(state) << "_initial" << "(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << " int err = SCXML_ERR_OK;" << std::endl;
+ writeExecContent(stream, initialTransition[0], 1);
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+ }
+ }
+
+
+ if (onentry.size() > 0) {
+ stream << "int " << DOMUtils::idForNode(state) << "_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ for (int j = 0; j < onentry.size(); j++) {
+ stream << " " << DOMUtils::idForNode(state) << "_on_entry_" << toStr(j) << "(ctx, state, event);" << std::endl;
+ }
+ if (hasInitialState) {
+ stream << " " << DOMUtils::idForNode(state) << "_initial" << "(ctx, state, event);" << std::endl;
+ }
+
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+ }
+
+
+ NodeSet<std::string> invoke = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", state);
+ if (invoke.size() > 0) {
+ stream << "int " << DOMUtils::idForNode(state) << "_invoke(const scxml_ctx* ctx, const scxml_state* s, const scxml_invoke* x) {" << std::endl;
+ for (int j = 0; j < invoke.size(); j++) {
+ stream << " ctx->invoke(ctx, s, x);" << std::endl;
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << std::endl;
+ }
+ stream << "}" << std::endl;
+ }
+ }
+
+ for (int i = 0; i < _transitions.size(); i++) {
+ Element<std::string> transition(_transitions[i]);
+ if (iequals(TAGNAME_CAST(transition.getParentNode()), "initial"))
+ continue;
+
+ NodeSet<std::string> execContent = filterChildType(Node_base::ELEMENT_NODE, transition);
+
+ if (execContent.size() > 0) {
+ stream << "int " << DOMUtils::idForNode(transition) << "_on_trans(const scxml_ctx* ctx, const scxml_state* state, const void* event) {" << std::endl;
+ stream << " int err = SCXML_ERR_OK;" << std::endl;
+ writeExecContent(stream, Element<std::string>(execContent[0]), 1);
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+ }
+ }
+}
+
+void ChartToC::writeExecContent(std::ostream& stream, const Arabica::DOM::Node<std::string>& node, int indent) {
+ if (!node)
+ return;
+
+ if (node.getNodeType() == Node_base::TEXT_NODE) {
+ if (boost::trim_copy(node.getNodeValue()).length() > 0) {
+ std::string escaped = escape(node.getNodeValue());
+ stream << escaped;
+ }
+ return;
+ }
+
+ std::string padding;
+ for (int i = 0; i < indent; i++) {
+ padding += " ";
+ }
+
+
+ if (node.getNodeType() != Node_base::ELEMENT_NODE)
+ return; // skip anything not an element
+
+ Arabica::DOM::Element<std::string> elem = Arabica::DOM::Element<std::string>(node);
+
+ if (false) {
+ } else if(TAGNAME(elem) == "onentry" || TAGNAME(elem) == "onexit" || TAGNAME(elem) == "transition" || TAGNAME(elem) == "finalize") {
+ // descent into childs and write their contents
+ Arabica::DOM::Node<std::string> child = node.getFirstChild();
+ while(child) {
+ writeExecContent(stream, child, indent);
+ child = child.getNextSibling();
+ }
+ } else if(TAGNAME(elem) == "script") {
+ stream << padding;
+ stream << "if (ctx->exec_content_script != NULL) {" << std::endl;
+ stream << padding;
+ stream << " if ((err = ctx->exec_content_script(ctx, ";
+ stream << (HAS_ATTR(elem, "src") ? "\"" + escape(ATTR(elem, "src")) + "\"" : "NULL") << ", ";
+
+ NodeSet<std::string> scriptTexts = filterChildType(Node_base::TEXT_NODE, elem);
+ if (scriptTexts.size() > 0) {
+ stream << "\"";
+ writeExecContent(stream, scriptTexts[0], 0);
+ stream << "\"";
+ } else {
+ stream << "NULL";
+ }
+
+ stream << ")) != SCXML_ERR_OK) return err" << std::endl;
+ stream << padding << "} else {" << std::endl;
+ stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
+ stream << padding << "}" << std::endl;
+
+ } else if(TAGNAME(elem) == "log") {
+ stream << padding;
+ stream << "if (ctx->exec_content_log != NULL) {" << std::endl;
+ stream << padding;
+ stream << " if ((ctx->exec_content_log(ctx, ";
+ stream << (HAS_ATTR(elem, "label") ? "\"" + escape(ATTR(elem, "label")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(elem, "expr") ? "\"" + escape(ATTR(elem, "expr")) + "\"" : "NULL");
+ stream << ")) != SCXML_ERR_OK) return err;" << std::endl;
+ stream << padding << "} else {" << std::endl;
+ stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
+ stream << padding << "}" << std::endl;
+
+ } else if(TAGNAME(elem) == "foreach") {
+ stream << padding << "if (ctx->exec_content_foreach_init != NULL &&" << std::endl;
+ stream << padding << " ctx->exec_content_foreach_next != NULL &&" << std::endl;
+ stream << padding << " ctx->exec_content_foreach_done != NULL) {" << std::endl;
+ stream << std::endl;
+
+ stream << padding << " if ((ctx->exec_content_foreach_init(ctx, &scxml_elem_foreachs[" << ATTR(elem, "documentOrder") << "])) != SCXML_ERR_OK) return err;" << std::endl;
+ stream << padding << " while (ctx->exec_content_foreach_next(ctx, &scxml_elem_foreachs[" << ATTR(elem, "documentOrder") << "]) == SCXML_ERR_OK) {" << std::endl;
+ Arabica::DOM::Node<std::string> child = node.getFirstChild();
+ while(child) {
+ writeExecContent(stream, child, indent + 2);
+ child = child.getNextSibling();
+ }
+ stream << padding << " }" << std::endl;
+ stream << padding << " if ((ctx->exec_content_foreach_done(ctx, &scxml_elem_foreachs[" << ATTR(elem, "documentOrder") << "])) != SCXML_ERR_OK) return err;" << std::endl;
+ stream << padding << "} else {" << std::endl;
+ stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
+ stream << padding << "}" << std::endl;
+
+ } else if(TAGNAME(elem) == "if") {
+ stream << padding;
+ stream << "if (ctx->is_true != NULL) {" << std::endl;
+ stream << padding;
+ stream << " if (ctx->is_true(ctx, " << (HAS_ATTR(elem, "cond") ? "\"" + escape(ATTR(elem, "cond")) + "\"" : "NULL") << ")) {" << std::endl;
+ Arabica::DOM::Node<std::string> child = elem.getFirstChild();
+ while(child) {
+ if (child.getNodeType() == Node_base::ELEMENT_NODE && TAGNAME_CAST(child) == "elseif") {
+ stream << padding;
+ stream << " } else if (ctx->is_true(ctx, " << (HAS_ATTR_CAST(child, "cond") ? "\"" + escape(ATTR_CAST(child, "cond")) + "\"" : "NULL") << ")) {" << std::endl;
+ } else if (child.getNodeType() == Node_base::ELEMENT_NODE && TAGNAME_CAST(child) == "else") {
+ stream << padding;
+ stream << " } else {" << std::endl;
+ } else {
+ writeExecContent(stream, child, indent + 2);
+ }
+ child = child.getNextSibling();
+ }
+ stream << padding << " }" << std::endl;
+ stream << padding << "} else {" << std::endl;
+ stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
+ stream << padding << "}" << std::endl;
+
+ } else if(TAGNAME(elem) == "assign") {
+ stream << padding;
+ stream << "if (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 << ")) != SCXML_ERR_OK) return err;" << std::endl;
+ stream << padding << "} else {" << std::endl;
+ stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
+ stream << padding << "}" << std::endl;
+
+
+ } else if(TAGNAME(elem) == "raise") {
+ stream << padding;
+ stream << "if (ctx->exec_content_raise != NULL) {" << std::endl;
+ stream << padding;
+ stream << " if ((ctx->exec_content_raise(ctx, ";
+ stream << (HAS_ATTR(elem, "event") ? "\"" + escape(ATTR(elem, "event")) + "\"" : "NULL");
+ stream << ")) != SCXML_ERR_OK) return err;" << std::endl;
+ stream << padding << "} else {" << std::endl;
+ stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
+ stream << padding << "}" << std::endl;
+
+ } else if(TAGNAME(elem) == "send") {
+ stream << padding;
+ stream << "if (ctx->exec_content_send != NULL) {" << std::endl;
+ stream << padding;
+ stream << " if ((ctx->exec_content_send(ctx, &scxml_elem_sends[" << ATTR(elem, "documentOrder") << "]";
+ stream << ")) != SCXML_ERR_OK) return err;" << std::endl;
+ stream << padding << "} else {" << std::endl;
+ stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
+ stream << padding << "}" << std::endl;
+
+ } else if(TAGNAME(elem) == "cancel") {
+ stream << padding;
+ stream << "if (ctx->exec_content_cancel != NULL) {" << std::endl;
+ stream << padding;
+ stream << " if ((ctx->exec_content_cancel(ctx, ";
+ stream << (HAS_ATTR(elem, "sendid") ? "\"" + escape(ATTR(elem, "sendid")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(elem, "sendidexpr") ? "\"" + escape(ATTR(elem, "sendidexpr")) + "\"" : "NULL");
+ stream << ")) != SCXML_ERR_OK) return err;" << std::endl;
+ stream << padding << "} else {" << std::endl;
+ stream << padding << " return SCXML_ERR_MISSING_CALLBACK;" << std::endl;
+ stream << padding << "}" << std::endl;
+
+ } else {
+ std::cerr << "'" << TAGNAME(elem) << "'" << std::endl << elem << std::endl;
+ assert(false);
+ }
+
+}
+
+void ChartToC::writeElementInfo(std::ostream& stream) {
+ NodeSet<std::string> foreachs = filterChildElements(_nsInfo.xmlNSPrefix + "foreach", _scxml, true);
+ if (foreachs.size() > 0) {
+ stream << "scxml_elem_foreach scxml_elem_foreachs[" << foreachs.size() << "] = {" << std::endl;
+ for (int i = 0; i < foreachs.size(); i++) {
+ Element<std::string> foreach(foreachs[i]);
+ stream << " { ";
+ stream << (HAS_ATTR(foreach, "array") ? "\"" + escape(ATTR(foreach, "array")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(foreach, "item") ? "\"" + escape(ATTR(foreach, "item")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(foreach, "index") ? "\"" + escape(ATTR(foreach, "index")) + "\"" : "NULL");
+ stream << " }" << (i + 1 < foreachs.size() ? ",": "") << std::endl;
+ foreach.setAttribute("documentOrder", toStr(i));
+ }
+ stream << "};" << std::endl;
+ stream << std::endl;
+ }
+
+ NodeSet<std::string> datas = filterChildElements(_nsInfo.xmlNSPrefix + "data", _scxml, true);
+ if (datas.size() > 0) {
+ if (_binding == InterpreterImpl::EARLY) {
+ Element<std::string>(_states[0]).setAttribute("dataIndex", "0");
+ }
+
+ Node<std::string> parent;
+ size_t distinctParents = 0;
+ for (int i = 0; i < datas.size(); i++) {
+ Element<std::string> data(datas[i]);
+ if (data.getParentNode() != parent) {
+ distinctParents++;
+ }
+ }
+ parent = Node<std::string>();
+
+ stream << "scxml_elem_data scxml_elem_datas[" << datas.size() + distinctParents << "] = {" << std::endl;
+ for (int i = 0; i < datas.size(); i++) {
+ Element<std::string> data(datas[i]);
+ if (data.getParentNode().getParentNode() != parent) {
+ if (_binding == InterpreterImpl::LATE) {
+ Element<std::string>(data.getParentNode().getParentNode()).setAttribute("dataIndex", toStr(i));
+ if (i > 0) {
+ stream << " { NULL, NULL, NULL, NULL }," << std::endl;
+ }
+ }
+ parent = data.getParentNode().getParentNode();
+ }
+ stream << " { ";
+ stream << (HAS_ATTR(data, "id") ? "\"" + escape(ATTR(data, "id")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(data, "src") ? "\"" + escape(ATTR(data, "src")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(data, "expr") ? "\"" + escape(ATTR(data, "expr")) + "\"" : "NULL") << ", ";
+
+ NodeSet<std::string> dataTexts = filterChildType(Node_base::TEXT_NODE, data);
+ if (dataTexts.size() > 0) {
+ if (boost::trim_copy(dataTexts[0].getNodeValue()).length() > 0) {
+ std::string escaped = escape(dataTexts[0].getNodeValue());
+ stream << "\"" << escaped << "\"" << std::endl;
+ }
+ } else {
+ stream << "NULL";
+ }
+
+ stream << " }," << std::endl;
+
+ }
+ stream << " { NULL, NULL, NULL, NULL }" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+ }
+
+ NodeSet<std::string> params = filterChildElements(_nsInfo.xmlNSPrefix + "param", _scxml, true);
+ if (params.size() > 0) {
+ Node<std::string> parent;
+ size_t distinctParents = 0;
+ for (int i = 0; i < params.size(); i++) {
+ Element<std::string> param(params[i]);
+ if (param.getParentNode() != parent) {
+ distinctParents++;
+ }
+ }
+ parent = Node<std::string>();
+
+ stream << "scxml_elem_param scxml_elem_params[" << params.size() + distinctParents << "] = {" << std::endl;
+ for (int i = 0; i < params.size(); i++) {
+ Element<std::string> param(params[i]);
+ if (param.getParentNode() != parent) {
+ Element<std::string>(param.getParentNode()).setAttribute("paramIndex", toStr(i));
+ if (i > 0) {
+ stream << " { NULL, NULL, NULL }," << std::endl;
+ }
+ parent = param.getParentNode();
+ }
+ stream << " { ";
+ stream << (HAS_ATTR(param, "name") ? "\"" + escape(ATTR(param, "name")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(param, "expr") ? "\"" + escape(ATTR(param, "expr")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(param, "location") ? "\"" + escape(ATTR(param, "location")) + "\"" : "NULL");
+ stream << " }," << std::endl;
+
+ }
+ stream << " { NULL, NULL, NULL }" << std::endl;
+ stream << "};" << std::endl;
+ stream << std::endl;
+ }
+
+ NodeSet<std::string> sends = filterChildElements(_nsInfo.xmlNSPrefix + "send", _scxml, true);
+ if (sends.size() > 0) {
+ stream << "scxml_elem_send scxml_elem_sends[" << sends.size() << "] = {" << std::endl;
+ for (int i = 0; i < sends.size(); i++) {
+ Element<std::string> send(sends[i]);
+ stream << " { ";
+ stream << (HAS_ATTR(send, "event") ? "\"" + escape(ATTR(send, "event")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "eventexpr") ? "\"" + escape(ATTR(send, "eventexpr")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "target") ? "\"" + escape(ATTR(send, "target")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "targetexpr") ? "\"" + escape(ATTR(send, "targetexpr")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "type") ? "\"" + escape(ATTR(send, "type")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "typeexpr") ? "\"" + escape(ATTR(send, "typeexpr")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "id") ? "\"" + escape(ATTR(send, "id")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "idlocation") ? "\"" + escape(ATTR(send, "idlocation")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "delay") ? "\"" + escape(ATTR(send, "delay")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "delayexpr") ? "\"" + escape(ATTR(send, "delayexpr")) + "\"" : "NULL") << ", ";
+ stream << (HAS_ATTR(send, "namelist") ? "\"" + escape(ATTR(send, "namelist")) + "\"" : "NULL") << ", ";
+
+ NodeSet<std::string> contents = filterChildElements(_nsInfo.xmlNSPrefix + "content", send);
+ if (contents.size() > 0) {
+ std::stringstream ss;
+ stream << "\"";
+ NodeList<std::string> cChilds = contents[0].getChildNodes();
+ for (int j = 0; j < cChilds.getLength(); j++) {
+ ss << cChilds.item(j);
+ stream << escape(ss.str());
+ }
+ stream << "\", ";
+ } else {
+ stream << "NULL, ";
+ }
+
+
+ if (HAS_ATTR(send, "paramIndex")) {
+ stream << "(const scxml_elem_param*)&scxml_elem_params[" << escape(ATTR(send, "paramIndex")) << "], ";
+ } else {
+ stream << "NULL, ";
+ }
+ stream << "NULL";
+
+ stream << " }" << (i + 1 < sends.size() ? ",": "") << std::endl;
+ send.setAttribute("documentOrder", toStr(i));
+ }
+ stream << "};" << std::endl;
+ stream << std::endl;
+ }
+
+}
+
+void ChartToC::writeStates(std::ostream& stream) {
+ stream << "scxml_state scxml_states[" << toStr(_states.size()) << "] = {" << std::endl;
+ for (int i = 0; i < _states.size(); i++) {
+ Element<std::string> state(_states[i]);
+ stream << " { ";
+
+ // name
+ stream << (HAS_ATTR(state, "id") ? "\"" + escape(ATTR(state, "id")) + "\"" : "NULL") << ", ";
+
+ // onentry
+ stream << (filterChildElements(_nsInfo.xmlNSPrefix + "onentry", state).size() > 0 ? DOMUtils::idForNode(state) + "_on_entry" : "NULL") << ", ";
+
+ // onexit
+ stream << (filterChildElements(_nsInfo.xmlNSPrefix + "onexit", state).size() > 0 ? DOMUtils::idForNode(state) + "_on_exit" : "NULL") << ", ";
+
+ // invokers
+ stream << (filterChildElements(_nsInfo.xmlNSPrefix + "invoke", state).size() > 0 ? DOMUtils::idForNode(state) + "_invoke" : "NULL") << ", ";
+
+ // children
+ std::string childBools;
+ std::string childBoolsIdx;
+ for (int j = 0; j < _states.size(); j++) {
+ if (_states[j].getParentNode() == state) {
+ childBools += "1";
+ childBoolsIdx += " " + toStr(j);
+ } else {
+ childBools += "0";
+ }
+ }
+ stream << "{ ";
+ writeCharArrayInitList(stream, childBools);
+ stream << " /* " << childBools << "," << childBoolsIdx << " */";
+ stream << " }, ";
+
+ // default completion
+ NodeSet<std::string> completion;
+ if (isParallel(state)) {
+ completion = getChildStates(state);
+ } else if (state.hasAttribute("initial")) {
+ completion = getStates(tokenizeIdRefs(state.getAttribute("initial")));
+ } else {
+ NodeSet<std::string> initElems = filterChildElements(_nsInfo.xmlNSPrefix + "initial", state);
+ if(initElems.size() > 0 && !iequals(ATTR_CAST(initElems[0], "generated"), "true")) {
+ // initial element is first child
+ completion.push_back(initElems[0]);
+ } else {
+ // first child state
+ Arabica::XPath::NodeSet<std::string> initStates;
+ NodeList<std::string> childs = state.getChildNodes();
+ for (int i = 0; i < childs.getLength(); i++) {
+ if (childs.item(i).getNodeType() != Node_base::ELEMENT_NODE)
+ continue;
+ if (isState(Element<std::string>(childs.item(i)))) {
+ completion.push_back(childs.item(i));
+ }
+ }
+ }
+ }
+
+ std::string descBools;
+ std::string descBoolsIdx;
+ for (int j = 0; j < _states.size(); j++) {
+ if (isMember(_states[j], completion)) {
+ descBools += "1";
+ descBoolsIdx += " " + toStr(j);
+ } else {
+ descBools += "0";
+ }
+ }
+ stream << "{ ";
+ writeCharArrayInitList(stream, descBools);
+ stream << " /* " << descBools << "," << descBoolsIdx << " */";
+ stream << " }, ";
+
+ // ancestors
+ std::string ancBools;
+ std::string ancBoolsIdx;
+ for (int j = 0; j < _states.size(); j++) {
+ if (isDescendant(state, _states[j])) {
+ ancBools += "1";
+ ancBoolsIdx += " " + toStr(j);
+ } else {
+ ancBools += "0";
+ }
+ }
+ stream << "{ ";
+ writeCharArrayInitList(stream, ancBools);
+ stream << " /* " << ancBools << "," << ancBoolsIdx << " */";
+ stream << " }, ";
+
+ if (HAS_ATTR(state, "dataIndex")) {
+ stream << "(const scxml_elem_data*)&scxml_elem_datas[" << escape(ATTR(state, "dataIndex")) << "], ";
+ } else {
+ stream << "NULL, ";
+ }
+
+ if (false) {
+ } else if (iequals(TAGNAME(state), "initial")) {
+ stream << "SCXML_STATE_INITIAL";
+ } else if (isFinal(state)) {
+ stream << "SCXML_STATE_FINAL";
+ } else if (isHistory(state)) {
+ if (HAS_ATTR(state, "type") && iequals(ATTR(state, "type"), "deep")) {
+ stream << "SCXML_STATE_HISTORY_DEEP";
+ } else {
+ stream << "SCXML_STATE_HISTORY_SHALLOW";
+ }
+ } else if (isAtomic(state)) {
+ stream << "SCXML_STATE_ATOMIC";
+ } else if (isParallel(state)) {
+ stream << "SCXML_STATE_PARALLEL";
+ } else if (isCompound(state)) {
+ stream << "SCXML_STATE_COMPOUND";
+ } else { // <scxml>
+ stream << "SCXML_STATE_COMPOUND";
+ }
+
+ stream << " }" << (i + 1 < _states.size() ? ",": "") << std::endl;
+ }
+ stream << "};" << std::endl;
+ stream << std::endl;
+}
+
+
+void ChartToC::writeTransitions(std::ostream& stream) {
+
+ stream << "scxml_transition scxml_transitions[" << toStr(_transitions.size()) << "] = {" << std::endl;
+ for (int i = 0; i < _transitions.size(); i++) {
+ Element<std::string> transition(_transitions[i]);
+ stream << " { ";
+
+ /**
+ uint16_t source;
+ target[SCXML_NUMBER_STATES / 8 + 1];
+ const char* event;
+ const char* condition;
+ exec_content_t on_transition;
+ uint8_t type;
+ char conflicts[SCXML_NUMBER_STATES / 8 + 1];
+ char exit_set[SCXML_NUMBER_STATES / 8 + 1];
+ */
+
+ // source
+ stream << ATTR_CAST(transition.getParentNode(), "documentOrder") << ", ";
+
+ // targets
+ if (HAS_ATTR(transition, "target")) {
+ std::list<std::string> targets = tokenize(ATTR(transition, "target"));
+
+ std::string targetBools;
+ for (int j = 0; j < _states.size(); j++) {
+ Element<std::string> state(_states[j]);
+
+ if (HAS_ATTR(state, "id") &&
+ std::find(targets.begin(), targets.end(), escape(ATTR(state, "id"))) != targets.end()) {
+ targetBools += "1";
+ } else {
+ targetBools += "0";
+ }
+ }
+
+ stream << "{ ";
+ writeCharArrayInitList(stream, targetBools);
+ stream << " /* " << targetBools << " */";
+ stream << " }, ";
+
+ } else {
+ stream << "{ NULL }, ";
+ }
+
+ // event
+ stream << (HAS_ATTR(transition, "event") ? "\"" + escape(ATTR(transition, "event")) + "\"" : "NULL") << ", ";
+
+ // condition
+ stream << (HAS_ATTR(transition, "cond") ? "\"" + escape(ATTR(transition, "cond")) + "\"" : "NULL") << ", ";
+
+ // on transition handlers
+ if (filterChildType(Arabica::DOM::Node_base::ELEMENT_NODE, transition).size() > 0 &&
+ !iequals(TAGNAME_CAST(transition.getParentNode()), "initial")) {
+ stream << DOMUtils::idForNode(transition) + "_on_trans, ";
+ } else {
+ stream << "NULL, ";
+ }
+
+ // type
+ std::string seperator = "";
+ if (!HAS_ATTR(transition, "target")) {
+ stream << seperator << "SCXML_TRANS_TARGETLESS";
+ seperator = " & ";
+ }
+
+ if (HAS_ATTR(transition, "type") && iequals(ATTR(transition, "type"), "internal")) {
+ stream << seperator << "SCXML_TRANS_INTERNAL";
+ seperator = " & ";
+ }
+
+ if (!HAS_ATTR(transition, "event")) {
+ stream << seperator << "SCXML_TRANS_SPONTANEOUS";
+ seperator = " & ";
+ }
+
+ if (seperator.size() == 0) {
+ stream << "0";
+ }
+ stream << ", ";
+
+ // conflicts
+ std::string conflictBools;
+ for (unsigned int j = 0; j < _transitions.size(); j++) {
+ Element<std::string> t2(_transitions[j]);
+ if (hasIntersection(computeExitSet(transition), computeExitSet(t2)) ||
+ (getSourceState(transition) == getSourceState(t2)) ||
+ (isDescendant(getSourceState(transition), getSourceState(t2))) ||
+ (isDescendant(getSourceState(t2), getSourceState(transition)))) {
+ conflictBools += "1";
+ } else {
+ conflictBools += "0";
+ }
+ }
+ stream << "{ ";
+ writeCharArrayInitList(stream, conflictBools);
+ stream << " /* " << conflictBools << " */";
+ stream << " }, ";
+
+ // exit set
+ std::string exitSetBools;
+ NodeSet<std::string> exitSet = computeExitSet(transition);
+ for (unsigned int j = 0; j < _states.size(); j++) {
+ Element<std::string> state(_states[j]);
+ if (isMember(state, exitSet)) {
+ exitSetBools += "1";
+ } else {
+ exitSetBools += "0";
+ }
+ }
+ stream << "{ ";
+ writeCharArrayInitList(stream, exitSetBools);
+ stream << " /* " << exitSetBools << " */";
+ stream << " }";
+
+ stream << " }" << (i + 1 < _transitions.size() ? ",": "") << std::endl;
+ }
+ stream << "};" << std::endl;
+ stream << std::endl;
+}
+
+Arabica::XPath::NodeSet<std::string> ChartToC::computeExitSet(const Arabica::DOM::Element<std::string>& transition) {
+
+ NodeSet<std::string> statesToExit;
+ if (!isTargetless(transition)) {
+ Arabica::DOM::Node<std::string> domain = getTransitionDomain(transition);
+ if (!domain)
+ return statesToExit;
+ for (unsigned int j = 0; j < _states.size(); j++) {
+ const Node<std::string>& s = _states[j];
+ if (isDescendant(s, domain)) {
+ statesToExit.push_back(s);
+ }
+ }
+ }
+
+ return statesToExit;
+}
+
+void ChartToC::writeCharArrayInitList(std::ostream& stream, const std::string& boolString) {
+ /**
+ * 0111 -> 0x08
+ * 1111 -> 0x0f
+ * 1111 1111 -> 0xff
+ * 1111 1111 1110 -> 0x0f, 0xfd
+ *
+ * 76543210 fedcba98 ...
+ */
+
+ std::string charArray;
+ size_t index = 0;
+ char currChar = 0;
+
+ for (std::string::const_iterator bIter = boolString.begin(); bIter != boolString.end(); bIter++) {
+
+ if (*bIter == '1') {
+ currChar |= 1 << index;
+ }
+
+ index++;
+ if (index == 8) {
+ charArray += currChar;
+ currChar = 0;
+ index = 0;
+ }
+ }
+
+ if (index != 0) {
+ charArray += currChar;
+ }
+
+ std::string seperator = "";
+ for (std::string::const_iterator cIter = charArray.begin(); cIter != charArray.end(); cIter++) {
+ stream << seperator << "0x" << std::setw(2) << std::setfill('0') << std::hex << int(*cIter & 0xFF);
+ seperator = ", ";
+ }
+}
+
+void ChartToC::writeFSM(std::ostream& stream) {
+ stream << "int scxml_step(scxml_ctx* ctx) {" << std::endl;
+ stream << std::endl;
+
+ stream << "#ifdef SCXML_VERBOSE" << std::endl;
+ stream << " printStateNames(ctx->config);" << std::endl;
+ stream << "#endif" << std::endl;
+ stream << std::endl;
+
+ stream << "MACRO_STEP:" << std::endl;
+ stream << " ctx->flags &= ~SCXML_CTX_TRANSITION_FOUND;" << std::endl;
+ stream << std::endl;
+
+ stream << " if (ctx->flags & SCXML_CTX_TOP_LEVEL_FINAL) " << std::endl;
+ stream << " return SCXML_ERR_DONE; " << std::endl;
+ stream << std::endl;
+
+ stream << " int err = SCXML_ERR_OK;" << std::endl;
+ stream << " char conflicts[" << _transCharArraySize << "] = " << _transCharArrayInit << ";" << std::endl;
+ stream << " char target_set[" << _stateCharArraySize << "] = " << _stateCharArrayInit << ";" << std::endl;
+ stream << " char exit_set[" << _stateCharArraySize << "] = " << _stateCharArrayInit << ";" << std::endl;
+ stream << " char trans_set[" << _transCharArraySize << "] = " << _transCharArrayInit << ";" << std::endl;
+ stream << " char entry_set[" << _stateCharArraySize << "] = " << _stateCharArrayInit << ";" << std::endl;
+ stream << std::endl;
+
+ stream << " void* event;" << std::endl;
+ stream << " if (ctx->flags == SCXML_CTX_PRISTINE) {" << std::endl;
+ stream << " bit_or(target_set, scxml_states[0].completion, " << _stateCharArraySize << ");" << std::endl;
+ stream << " ctx->flags |= SCXML_CTX_SPONTANEOUS | SCXML_CTX_INITIALIZED;" << std::endl;
+ stream << " goto COMPLETE_CONFIG;" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << " if (ctx->flags & SCXML_CTX_SPONTANEOUS) {" << std::endl;
+ stream << " event = NULL;" << std::endl;
+ stream << " goto SELECT_TRANSITIONS;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " if ((event = ctx->dequeue_internal(ctx)) != NULL) {" << std::endl;
+ stream << " goto SELECT_TRANSITIONS;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " if ((event = ctx->dequeue_external(ctx)) != NULL) {" << std::endl;
+
+ stream << " goto SELECT_TRANSITIONS;" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << "SELECT_TRANSITIONS:" << std::endl;
+ stream << " for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {" << std::endl;
+ stream << " // is the transition active?" << std::endl;
+ stream << " if (IS_SET(scxml_transitions[i].source, ctx->config)) {" << std::endl;
+ stream << " // is it non-conflicting?" << std::endl;
+ stream << " if (!IS_SET(i, conflicts)) {" << std::endl;
+ stream << " // is it enabled?" << std::endl;
+ stream << " if (ctx->is_enabled(ctx, &scxml_transitions[i], event) > 0) {" << std::endl;
+ stream << " // remember that we found a transition" << std::endl;
+ stream << " ctx->flags |= SCXML_CTX_TRANSITION_FOUND;" << std::endl;
+ stream << std::endl;
+
+ stream << " // transitions that are pre-empted" << std::endl;
+ stream << " bit_or(conflicts, scxml_transitions[i].conflicts, " << _transCharArraySize << ");" << std::endl;
+ stream << std::endl;
+ stream << " // states that are directly targeted (resolve as entry-set later)" << std::endl;
+ stream << " bit_or(target_set, scxml_transitions[i].target, " << _stateCharArraySize << ");" << std::endl;
+ stream << std::endl;
+ stream << " // states that will be left" << std::endl;
+ stream << " bit_or(exit_set, scxml_transitions[i].exit_set, " << _stateCharArraySize << ");" << std::endl;
+ stream << std::endl;
+ stream << " SET_BIT(i, trans_set);" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << " if (ctx->flags & SCXML_CTX_TRANSITION_FOUND) {" << std::endl;
+ stream << " ctx->flags |= SCXML_CTX_SPONTANEOUS;" << std::endl;
+ stream << " } else {" << std::endl;
+ stream << " ctx->flags &= ~SCXML_CTX_SPONTANEOUS;" << std::endl;
+ stream << " goto MACRO_STEP;" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << "REMEMBER_HISTORY:" << std::endl;
+ stream << " // are my ancestors in the exit set?" << std::endl;
+ stream << " for (int i = 0; i < SCXML_NUMBER_STATES; i++) {" << std::endl;
+ stream << " if (IS_SET(i, ctx->config) && bit_has_and(exit_set, scxml_states[i].ancestors, " << _stateCharArraySize << ")) {" << std::endl;
+ stream << " SET_BIT(i, ctx->history);" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << "#ifdef SCXML_VERBOSE" << std::endl;
+ stream << " printf(\"Exiting: \");" << std::endl;
+ stream << " printStateNames(exit_set);" << std::endl;
+ stream << "#endif" << std::endl;
+ stream << std::endl;
+
+ stream << "EXIT_STATES:" << std::endl;
+ stream << " for (int i = SCXML_NUMBER_STATES - 1; i >= 0; i--) {" << std::endl;
+ stream << " if (IS_SET(i, exit_set) && IS_SET(i, ctx->config)) {" << std::endl;
+ stream << " // call all on exit handlers" << std::endl;
+ stream << " if (scxml_states[i].on_exit != NULL) {" << std::endl;
+ stream << " if((err = scxml_states[i].on_exit(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)" << std::endl;
+ stream << " return err;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " CLEARBIT(i, ctx->config);" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << "COMPLETE_CONFIG:" << std::endl;
+ stream << " // calculate new entry set" << std::endl;
+ stream << " bit_copy(entry_set, target_set, " << _stateCharArraySize << ");" << std::endl;
+ stream << std::endl;
+ stream << " // iterate for ancestors" << std::endl;
+ stream << " for (int i = 0; i < SCXML_NUMBER_STATES; i++) {" << std::endl;
+ stream << " if (IS_SET(i, entry_set)) {" << std::endl;
+ stream << " bit_or(entry_set, scxml_states[i].ancestors, " << _stateCharArraySize << ");" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << "ADD_DESCENDANTS:" << std::endl;
+ stream << " // iterate for descendants" << std::endl;
+ stream << " for (int i = 0; i < SCXML_NUMBER_STATES; i++) {" << std::endl;
+ stream << " if (IS_SET(i, entry_set)) {" << std::endl;
+ stream << " switch (scxml_states[i].type) {" << std::endl;
+ stream << " case SCXML_STATE_PARALLEL: {" << std::endl;
+ stream << " bit_or(entry_set, scxml_states[i].completion, " << _stateCharArraySize << ");" << std::endl;
+ stream << " break;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " case SCXML_STATE_INITIAL: {" << std::endl;
+ stream << " for (int j = 0; j < SCXML_NUMBER_TRANSITIONS; j++) {" << std::endl;
+ stream << " if (scxml_transitions[j].source == i) {" << std::endl;
+ stream << " SET_BIT(j, trans_set);" << std::endl;
+ stream << " CLEARBIT(i, entry_set);" << std::endl;
+ stream << " bit_or(entry_set, scxml_transitions[j].target, " << _transCharArraySize << ");" << std::endl;
+ stream << " // one target may have been above, reestablish completion" << std::endl;
+ stream << " goto ADD_DESCENDANTS;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " break;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " case SCXML_STATE_COMPOUND: { // we need to check whether one child is already in entry_set" << std::endl;
+ stream << " if (!bit_has_and(entry_set, scxml_states[i].children, " << _stateCharArraySize << ")) {" << std::endl;
+ stream << " bit_or(entry_set, scxml_states[i].completion, " << _stateCharArraySize << ");" << std::endl;
+ stream << " }" << std::endl;
+ stream << " break;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << "#ifdef SCXML_VERBOSE" << std::endl;
+ stream << " printf(\"Transitions: \");" << std::endl;
+ stream << " printBitsetIndices(trans_set, sizeof(char) * 8 * " << _transCharArraySize << ");" << std::endl;
+ stream << "#endif" << std::endl;
+ stream << std::endl;
+
+ stream << "TAKE_TRANSITIONS:" << std::endl;
+ stream << " for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {" << std::endl;
+ stream << " if (IS_SET(i, trans_set)) {" << std::endl;
+ stream << " // call executable content in transition" << std::endl;
+ stream << " if (scxml_transitions[i].on_transition != NULL) {" << std::endl;
+ stream << " if((err = scxml_transitions[i].on_transition(ctx," << std::endl;
+ stream << " &scxml_states[scxml_transitions[i].source]," << std::endl;
+ stream << " event)) != SCXML_ERR_OK)" << std::endl;
+ stream << " return err;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << "#ifdef SCXML_VERBOSE" << std::endl;
+ stream << " printf(\"Entering: \");" << std::endl;
+ stream << " printStateNames(entry_set);" << std::endl;
+ stream << "#endif" << std::endl;
+ stream << std::endl;
+
+ stream << "ENTER_STATES:" << std::endl;
+ stream << " for (int i = 0; i < SCXML_NUMBER_STATES; i++) {" << std::endl;
+ stream << " if (IS_SET(i, entry_set) && !IS_SET(i, ctx->config)) {" << std::endl;
+ stream << " SET_BIT(i, ctx->config);" << std::endl;
+ stream << " if (scxml_states[i].on_entry != NULL) {" << std::endl;
+ stream << " if((err = scxml_states[i].on_entry(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)" << std::endl;
+ stream << " return err;" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << " // initialize data" << std::endl;
+ stream << " if(!IS_SET(i, ctx->initialized_data)) {" << std::endl;
+ stream << " if (scxml_states[i].data != NULL && ctx->exec_content_init != NULL) {" << std::endl;
+ stream << " ctx->exec_content_init(ctx, scxml_states[i].data);" << std::endl;
+ stream << " }" << std::endl;
+ stream << " SET_BIT(i, ctx->initialized_data);" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << " // handle final states" << std::endl;
+ stream << " if (scxml_states[i].type == SCXML_STATE_FINAL) {" << std::endl;
+ stream << " if (scxml_states[i].ancestors[0] == 0x01) {" << std::endl;
+ stream << " ctx->flags |= SCXML_CTX_TOP_LEVEL_FINAL;" << std::endl;
+ stream << " } else {" << std::endl;
+ stream << " // raise done event" << std::endl;
+ stream << " size_t parent = 0;" << std::endl;
+ stream << " for (int j = 0; j < SCXML_NUMBER_STATES; j++) {" << std::endl;
+ stream << " // we could trade runtime for memory here by saving the parent index" << std::endl;
+ stream << " if (!IS_SET(j, scxml_states[i].ancestors)) {" << std::endl;
+ stream << " if (parent != 0) {" << std::endl;
+ stream << " break;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " continue;" << std::endl;
+ stream << " } else {" << std::endl;
+ stream << " parent = j;" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " ctx->raise_done_event(ctx, &scxml_states[parent]);" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << " /**" << std::endl;
+ stream << " * are we the last final state to leave a parallel state?:" << std::endl;
+ stream << " * 1. Gather all parallel states in our ancestor chain" << std::endl;
+ stream << " * 2. Find all states for which these parallels are ancestors" << std::endl;
+ stream << " * 3. Iterate all active final states and remove their ancestors" << std::endl;
+ stream << " * 4. If a state remains, not all children of a parallel are final" << std::endl;
+ stream << " */" << std::endl;
+ stream << " for (int j = 0; j < SCXML_NUMBER_STATES; j++) {" << std::endl;
+ stream << " if (scxml_states[j].type == SCXML_STATE_PARALLEL) {" << std::endl;
+ stream << " char parallel_children[2] = {0, 0};" << std::endl;
+ stream << " size_t parallel = j;" << std::endl;
+ stream << " for (int k = 0; k < SCXML_NUMBER_STATES; k++) {" << std::endl;
+ stream << " if (IS_SET(parallel, scxml_states[k].ancestors) && IS_SET(k, ctx->config)) {" << std::endl;
+ stream << " if (scxml_states[k].type == SCXML_STATE_FINAL) {" << std::endl;
+ stream << " bit_and_not(parallel_children, scxml_states[k].ancestors, 2);" << std::endl;
+ stream << " } else {" << std::endl;
+ stream << " SET_BIT(k, parallel_children);" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " if (!bit_any_set(parallel_children, 2)) {" << std::endl;
+ stream << " ctx->raise_done_event(ctx, &scxml_states[parallel]);" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << " }" << std::endl;
+ stream << " }" << std::endl;
+ stream << std::endl;
+
+ stream << " return SCXML_ERR_OK;" << std::endl;
+ stream << "}" << std::endl;
+ stream << std::endl;
+}
+
+NodeSet<std::string> ChartToC::inPostFixOrder(const std::set<std::string>& elements, const Element<std::string>& root) {
+ NodeSet<std::string> nodes;
+ inPostFixOrder(elements, root, nodes);
+ return nodes;
+}
+
+void ChartToC::inPostFixOrder(const std::set<std::string>& elements, const Element<std::string>& root, NodeSet<std::string>& nodes) {
+ NodeList<std::string> children = root.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE)
+ continue;
+ const Arabica::DOM::Element<std::string>& childElem(children.item(i));
+ inPostFixOrder(elements, childElem, nodes);
+
+ }
+ for (int i = 0; i < children.getLength(); i++) {
+ if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE)
+ continue;
+ const Arabica::DOM::Element<std::string>& childElem(children.item(i));
+
+ if (elements.find(TAGNAME(childElem)) != elements.end()) {
+ nodes.push_back(childElem);
+ }
+ }
+}
+
+NodeSet<std::string> ChartToC::inDocumentOrder(const std::set<std::string>& elements, const Element<std::string>& root) {
+ NodeSet<std::string> nodes;
+ inDocumentOrder(elements, root, nodes);
+ return nodes;
+}
+
+void ChartToC::inDocumentOrder(const std::set<std::string>& elements, const Element<std::string>& root, NodeSet<std::string>& nodes) {
+ if (elements.find(TAGNAME(root)) != elements.end()) {
+ nodes.push_back(root);
+ }
+
+ NodeList<std::string> children = root.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ if (children.item(i).getNodeType() != Node_base::ELEMENT_NODE)
+ continue;
+ const Arabica::DOM::Element<std::string>& childElem(children.item(i));
+ inDocumentOrder(elements, childElem, nodes);
+ }
+}
+
+ChartToC::~ChartToC() {
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/transform/ChartToC.h b/src/uscxml/transform/ChartToC.h
new file mode 100644
index 0000000..e18dfab
--- /dev/null
+++ b/src/uscxml/transform/ChartToC.h
@@ -0,0 +1,89 @@
+/**
+ * @file
+ * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
+ * @copyright Simplified BSD
+ *
+ * @cond
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the FreeBSD license as published by the FreeBSD
+ * project.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * You should have received a copy of the FreeBSD license along with this
+ * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
+ * @endcond
+ */
+
+#ifndef FSMTOCPP_H_201672B0
+#define FSMTOCPP_H_201672B0
+
+#include "uscxml/interpreter/InterpreterDraft6.h"
+#include "uscxml/DOMUtils.h"
+#include "uscxml/util/Trie.h"
+#include "Transformer.h"
+
+#include <DOM/Document.hpp>
+#include <DOM/Node.hpp>
+#include <XPath/XPath.hpp>
+#include <ostream>
+
+namespace uscxml {
+
+class USCXML_API ChartToC : public InterpreterRC, public TransformerImpl {
+public:
+
+ virtual ~ChartToC();
+ static Transformer transform(const Interpreter& other);
+
+ void writeTo(std::ostream& stream);
+
+ static Arabica::XPath::NodeSet<std::string> inPostFixOrder(const std::set<std::string>& elements,
+ const Arabica::DOM::Element<std::string>& root);
+ static Arabica::XPath::NodeSet<std::string> inDocumentOrder(const std::set<std::string>& elements,
+ const Arabica::DOM::Element<std::string>& root);
+protected:
+ ChartToC(const Interpreter& other);
+
+ static void inPostFixOrder(const std::set<std::string>& elements,
+ const Arabica::DOM::Element<std::string>& root,
+ Arabica::XPath::NodeSet<std::string>& nodes);
+
+ static void inDocumentOrder(const std::set<std::string>& elements,
+ const Arabica::DOM::Element<std::string>& root,
+ Arabica::XPath::NodeSet<std::string>& nodes);
+
+ void writeIncludes(std::ostream& stream);
+ void writeMacros(std::ostream& stream);
+ void writeTypes(std::ostream& stream);
+ void writeHelpers(std::ostream& stream);
+ void writeExecContent(std::ostream& stream);
+ void writeElementInfo(std::ostream& stream);
+
+ void writeStates(std::ostream& stream);
+ void writeTransitions(std::ostream& stream);
+ void writeFSM(std::ostream& stream);
+ void writeCharArrayInitList(std::ostream& stream, const std::string& boolString);
+
+ void writeExecContent(std::ostream& stream, const Arabica::DOM::Node<std::string>& node, int indent = 0);
+
+ Arabica::XPath::NodeSet<std::string> computeExitSet(const Arabica::DOM::Element<std::string>& transition);
+
+ Interpreter interpreter;
+
+ Arabica::XPath::NodeSet<std::string> _states;
+ std::map<std::string, Arabica::DOM::Element<std::string> > _stateNames;
+ Arabica::XPath::NodeSet<std::string> _transitions;
+
+ size_t _transCharArraySize;
+ std::string _transCharArrayInit;
+
+ size_t _stateCharArraySize;
+ std::string _stateCharArrayInit;
+};
+
+}
+
+#endif /* end of include guard: FSMTOCPP_H_201672B0 */
diff --git a/src/uscxml/transform/ChartToCPP.cpp.todo b/src/uscxml/transform/ChartToCPP.cpp.todo
deleted file mode 100644
index 6b78374..0000000
--- a/src/uscxml/transform/ChartToCPP.cpp.todo
+++ /dev/null
@@ -1,546 +0,0 @@
-/**
- * @file
- * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#include "uscxml/transform/ChartToFSM.h"
-#include "uscxml/transform/ChartToCPP.h"
-#include <DOM/io/Stream.hpp>
-#include <iostream>
-#include "uscxml/UUID.h"
-#include <math.h>
-#include <boost/algorithm/string.hpp>
-#include <glog/logging.h>
-
-namespace uscxml {
-
-using namespace Arabica::DOM;
-using namespace Arabica::XPath;
-
-void ChartToCPP::writeProgram(std::ostream& stream,
- const Interpreter& interpreter) {
- ChartToCPP promelaWriter;
- promelaWriter.cloneFrom(interpreter.getImpl());
- promelaWriter.writeProgram(stream);
-}
-
-ChartToCPP::ChartToCPP() : _eventTrie(".") {
-}
-
-void ChartToCPP::writeEvents(std::ostream& stream) {
- std::list<TrieNode*> eventNames = _eventTrie.getWordsWithPrefix("");
- std::list<TrieNode*>::iterator eventIter = eventNames.begin();
- stream << "// event name identifiers" << std::endl;
- while(eventIter != eventNames.end()) {
- stream << "#define " << "e" << (*eventIter)->identifier << " " << (*eventIter)->identifier;
- stream << " // from \"" << (*eventIter)->value << "\"" << std::endl;
- eventIter++;
- }
-}
-
-void ChartToCPP::writeStates(std::ostream& stream) {
- stream << "// state name identifiers" << std::endl;
- for (int i = 0; i < _globalStates.size(); i++) {
- stream << "#define " << "s" << i << " " << i;
- stream << " // from \"" << ATTR_CAST(_globalStates[i], "id") << "\"" << std::endl;
- }
-
-}
-
-Arabica::XPath::NodeSet<std::string> ChartToCPP::getTransientContent(const Arabica::DOM::Element<std::string>& state) {
- Arabica::XPath::NodeSet<std::string> content;
- Arabica::DOM::Element<std::string> currState = state;
- for (;;) {
- if (!HAS_ATTR(currState, "transient") || !DOMUtils::attributeIsTrue(ATTR(currState, "transient")))
- break;
- content.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "invoke", currState));
- content.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "onentry", currState));
- content.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "onexit", currState));
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", currState);
- currState = _states[ATTR_CAST(transitions[0], "target")];
- }
-
- return content;
-}
-
-Node<std::string> ChartToCPP::getUltimateTarget(const Arabica::DOM::Element<std::string>& transition) {
- Arabica::DOM::Element<std::string> currState = _states[ATTR(transition, "target")];
-
- for (;;) {
- if (!HAS_ATTR(currState, "transient") || !DOMUtils::attributeIsTrue(ATTR(currState, "transient")))
- return currState;
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", currState);
- currState = _states[ATTR_CAST(transitions[0], "target")];
- }
-}
-
-void ChartToCPP::writeInlineComment(std::ostream& stream, const Arabica::DOM::Element<std::string>& node) {
- if (node.getNodeType() != Node_base::COMMENT_NODE)
- return;
-
- std::string comment = node.getNodeValue();
- boost::trim(comment);
- if (!boost::starts_with(comment, "promela-inline:"))
- return;
-
- std::stringstream ssLine(comment);
- std::string line;
- std::getline(ssLine, line); // consume first line
- while(std::getline(ssLine, line)) {
- if (line.length() == 0)
- continue;
- stream << line;
- }
-}
-
-void ChartToCPP::writeExecutableContent(std::ostream& stream, const Arabica::DOM::Element<std::string>& node, int indent) {
-
- std::string padding;
- for (int i = 0; i < indent; i++) {
- padding += " ";
- }
-
- if (node.getNodeType() == Node_base::COMMENT_NODE) {
- std::string comment = node.getNodeValue();
- boost::trim(comment);
- std::stringstream inlinePromela;
- if (!boost::starts_with(comment, "promela-inline:"))
- return;
- std::stringstream ssLine(comment);
- std::string line;
- std::getline(ssLine, line); // consume first line
- while(std::getline(ssLine, line)) {
- if (line.length() == 0)
- continue;
- inlinePromela << line << std::endl;
- }
- stream << padding << "skip;" << std::endl;
- stream << beautifyIndentation(inlinePromela.str(), indent) << std::endl;
- }
-
- if (node.getNodeType() != Node_base::ELEMENT_NODE)
- return;
-
- if (false) {
- } else if(TAGNAME(node) == "state") {
- if (HAS_ATTR(node, "transient") && DOMUtils::attributeIsTrue(ATTR(node, "transient"))) {
- Arabica::XPath::NodeSet<std::string> execContent = getTransientContent(node);
- for (int i = 0; i < execContent.size(); i++) {
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(execContent[i]), indent);
- }
- } else {
- Arabica::DOM::Node<std::string> child = node.getFirstChild();
- while(child) {
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(child), indent);
- child = child.getNextSibling();
- }
- }
- } else if(TAGNAME(node) == "transition") {
- stream << "t" << _transitions[node] << ":" << std::endl;
-
- stream << padding << "atomic {" << std::endl;
- writeExecutableContent(stream, _states[ATTR(node, "target")], indent+1);
- stream << padding << " skip;" << std::endl;
-
- Node<std::string> newState = getUltimateTarget(node);
- for (int i = 0; i < _globalStates.size(); i++) {
- if (newState != _globalStates[i])
- continue;
- stream << padding << " s = s" << i << ";" << std::endl;
- }
-
- stream << padding << "}" << std::endl;
- if (isFinal(Element<std::string>(newState))) {
- stream << padding << "goto terminate;" << std::endl;
- } else {
- stream << padding << "goto nextStep;" << std::endl;
- }
-
- } else if(TAGNAME(node) == "onentry" || TAGNAME(node) == "onexit") {
- Arabica::DOM::Node<std::string> child = node.getFirstChild();
- while(child) {
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(child), indent);
- child = child.getNextSibling();
- }
-
- } else if(TAGNAME(node) == "script") {
- NodeSet<std::string> scriptText = filterChildType(Node_base::TEXT_NODE, node, true);
- for (int i = 0; i < scriptText.size(); i++) {
- stream << beautifyIndentation(scriptText[i].getNodeValue(), indent) << std::endl;
- }
-
- } else if(TAGNAME(node) == "log") {
- // ignore
-
- } else if(TAGNAME(node) == "foreach") {
- if (HAS_ATTR(node, "index"))
- stream << padding << ATTR(node, "index") << " = 0;" << std::endl;
- stream << padding << "for (" << ATTR(node, "item") << " in " << ATTR(node, "array") << ") {" << std::endl;
- Arabica::DOM::Node<std::string> child = node.getFirstChild();
- while(child) {
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(child), indent + 1);
- child = child.getNextSibling();
- }
- if (HAS_ATTR(node, "index"))
- stream << padding << " " << ATTR(node, "index") << "++;" << std::endl;
- stream << padding << "}" << std::endl;
-
- } else if(TAGNAME(node) == "if") {
- NodeSet<std::string> condChain;
- condChain.push_back(node);
- condChain.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "elseif", node));
- condChain.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "else", node));
-
- writeIfBlock(stream, condChain, indent);
-
- } else if(TAGNAME(node) == "raise") {
- TrieNode* trieNode = _eventTrie.getNodeWithPrefix(ATTR(node, "event"));
- stream << padding << "iQ!e" << trieNode->identifier << ";" << std::endl;
- } else if(TAGNAME(node) == "send") {
- if (!HAS_ATTR(node, "target")) {
- // this is for our external queue
- TrieNode* trieNode = _eventTrie.getNodeWithPrefix(ATTR(node, "event"));
- stream << padding << "tmpQ!e" << trieNode->identifier << ";" << std::endl;
- }
- } else if(TAGNAME(node) == "invoke") {
- } else if(TAGNAME(node) == "uninvoke") {
- stream << padding << ATTR(node, "invokeid") << "EventSourceDone" << "= 1;" << std::endl;
- } else {
-
- std::cerr << "'" << TAGNAME(node) << "'" << std::endl << node << std::endl;
- assert(false);
- }
-
-}
-
-void ChartToCPP::writeIfBlock(std::ostream& stream, const Arabica::XPath::NodeSet<std::string>& condChain, int indent) {
- if (condChain.size() == 0)
- return;
-
- std::string padding;
- for (int i = 0; i < indent; i++) {
- padding += " ";
- }
-
- bool noNext = condChain.size() == 1;
- bool nextIsElse = false;
- if (condChain.size() > 1) {
- if (TAGNAME_CAST(condChain[1]) == "else") {
- nextIsElse = true;
- }
- }
-
- Node<std::string> ifNode = condChain[0];
-
- stream << padding << "if" << std::endl;
- // we need to nest the elseifs to resolve promela if semantics
- stream << padding << ":: (" << ATTR_CAST(ifNode, "cond") << ") -> {" << std::endl;
-
- Arabica::DOM::Node<std::string> child;
- if (TAGNAME_CAST(ifNode) == "if") {
- child = ifNode.getFirstChild();
- } else {
- child = ifNode.getNextSibling();
- }
- while(child) {
- if (child.getNodeType() == Node_base::ELEMENT_NODE) {
- if (TAGNAME_CAST(child) == "elseif" || TAGNAME_CAST(child) == "else")
- break;
- }
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(child), indent + 1);
- child = child.getNextSibling();
- }
- stream << padding << "}" << std::endl;
- stream << padding << ":: else -> ";
-
- if (nextIsElse) {
- child = condChain[1].getNextSibling();
- stream << "{" << std::endl;
- while(child) {
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(child), indent + 1);
- child = child.getNextSibling();
- }
- stream << padding << "}" << std::endl;
-
- } else if (noNext) {
- stream << "skip;" << std::endl;
- } else {
- stream << "{" << std::endl;
-
- Arabica::XPath::NodeSet<std::string> cdrCondChain;
- for (int i = 1; i < condChain.size(); i++) {
- cdrCondChain.push_back(condChain[i]);
- }
- writeIfBlock(stream, cdrCondChain, indent + 1);
- stream << padding << "}" << std::endl;
-
- }
-
- stream << padding << "fi;" << std::endl;
-
-}
-
-std::string ChartToCPP::beautifyIndentation(const std::string& code, int indent) {
-
- std::string padding;
- for (int i = 0; i < indent; i++) {
- padding += " ";
- }
-
- // remove topmost indentation from every line and reindent
- std::stringstream beautifiedSS;
-
- std::string initialIndent;
- bool gotIndent = false;
- bool isFirstLine = true;
- std::stringstream ssLine(code);
- std::string line;
-
- while(std::getline(ssLine, line)) {
- size_t firstChar = line.find_first_not_of(" \t\r\n");
- if (firstChar != std::string::npos) {
- if (!gotIndent) {
- initialIndent = line.substr(0, firstChar);
- gotIndent = true;
- }
- beautifiedSS << (isFirstLine ? "" : "\n") << padding << boost::replace_first_copy(line, initialIndent, "");
- isFirstLine = false;
- }
- }
-
- return beautifiedSS.str();
-}
-
-void ChartToCPP::writeDeclarations(std::ostream& stream) {
-
- // get all data elements
- NodeSet<std::string> datas = _xpath.evaluate("//" + _nsInfo.xpathPrefix + "data", _scxml).asNodeSet();
- NodeSet<std::string> dataText = filterChildType(Node_base::TEXT_NODE, datas, true);
-
- // write their text content
- stream << "// datamodel variables" << std::endl;
- for (int i = 0; i < dataText.size(); i++) {
- Node<std::string> data = dataText[i];
- stream << beautifyIndentation(data.getNodeValue()) << std::endl;
- }
-
- stream << std::endl;
- stream << "// global variables" << std::endl;
- stream << "int e; /* current event */" << std::endl;
- stream << "int s; /* current state */" << std::endl;
- stream << "chan iQ = [100] of {int} /* internal queue */" << std::endl;
- stream << "chan eQ = [100] of {int} /* external queue */" << std::endl;
- stream << "chan tmpQ = [100] of {int} /* temporary queue for external events in transitions */" << std::endl;
- stream << "int tmpQItem;" << std::endl;
-
- stream << std::endl;
- stream << "// event sources" << std::endl;
-
-}
-
-void ChartToCPP::writeFSM(std::ostream& stream) {
- NodeSet<std::string> transitions;
-
- stream << "proctype step() {" << std::endl;
- // write initial transition
- transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _startState);
- assert(transitions.size() == 1);
- stream << " // transition's executable content" << std::endl;
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(transitions[0]), 1);
-
- for (int i = 0; i < _globalStates.size(); i++) {
- if (_globalStates[i] == _startState)
- continue;
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _globalStates[i]);
- for (int j = 0; j < transitions.size(); j++) {
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(transitions[j]), 1);
- }
- }
-
- stream << std::endl;
- stream << "nextStep:" << std::endl;
- stream << " // push send events to external queue" << std::endl;
- stream << " if" << std::endl;
- stream << " :: len(tmpQ) != 0 -> { tmpQ?e; eQ!e }" << std::endl;
- stream << " :: else -> skip;" << std::endl;
- stream << " fi;" << std::endl << std::endl;
-
- stream << " /* pop an event */" << std::endl;
- stream << " if" << std::endl;
- stream << " :: len(iQ) != 0 -> iQ ? e /* from internal queue */" << std::endl;
- stream << " :: else -> eQ ? e /* from external queue */" << std::endl;
- stream << " fi;" << std::endl;
- stream << " /* event dispatching per state */" << std::endl;
- stream << " if" << std::endl;
-
- writeEventDispatching(stream);
-
- stream << " :: else -> goto nextStep;" << std::endl;
- stream << " fi;" << std::endl;
- stream << "terminate: skip;" << std::endl;
-
-
- stream << "}" << std::endl;
-}
-
-void ChartToCPP::writeEventDispatching(std::ostream& stream) {
- for (int i = 0; i < _globalStates.size(); i++) {
- if (_globalStates[i] == _startState)
- continue;
-
- stream << " :: (s == s" << i << ") -> {" << std::endl;
-
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _globalStates[i]);
- writeDispatchingBlock(stream, transitions, 2);
- stream << " goto nextStep;" << std::endl;
- stream << " }" << std::endl;
- }
-}
-
-void ChartToCPP::writeDispatchingBlock(std::ostream& stream, const Arabica::XPath::NodeSet<std::string>& transChain, int indent) {
- if (transChain.size() == 0)
- return;
-
- std::string padding;
- for (int i = 0; i < indent; i++) {
- padding += " ";
- }
-
- stream << padding << "if" << std::endl;
- stream << padding << ":: ((0";
-
- Node<std::string> currTrans = transChain[0];
- std::string eventDesc = ATTR_CAST(currTrans, "event");
- if (boost::ends_with(eventDesc, "*"))
- eventDesc = eventDesc.substr(0, eventDesc.size() - 1);
- if (boost::ends_with(eventDesc, "."))
- eventDesc = eventDesc.substr(0, eventDesc.size() - 1);
-
- if (eventDesc.size() == 0) {
- stream << " || 1";
- } else {
- std::list<TrieNode*> trieNodes = _eventTrie.getWordsWithPrefix(eventDesc);
-
- std::list<TrieNode*>::iterator trieIter = trieNodes.begin();
- while(trieIter != trieNodes.end()) {
- stream << " || e == e" << (*trieIter)->identifier;
- trieIter++;
- }
- }
-
- stream << ") && ";
- stream << (HAS_ATTR_CAST(currTrans, "cond") ? ATTR_CAST(currTrans, "cond") : "1");
- stream << ") -> goto t" << _transitions[Arabica::DOM::Element<std::string>(currTrans)] << ";" << std::endl;
- ;
-
- stream << padding << ":: else {" << std::endl;
-
- Arabica::XPath::NodeSet<std::string> cdrTransChain;
- for (int i = 1; i < transChain.size(); i++) {
- cdrTransChain.push_back(transChain[i]);
- }
- writeDispatchingBlock(stream, cdrTransChain, indent + 1);
-
- stream << padding << " goto nextStep;" << std::endl;
- stream << padding << "}" << std::endl;
- stream << padding << "fi;" << std::endl;
-}
-
-
-void ChartToCPP::writeMain(std::ostream& stream) {
- stream << std::endl;
- stream << "init {" << std::endl;
- stream << " run step();" << std::endl;
- stream << "}" << std::endl;
-
-}
-
-void ChartToCPP::initNodes() {
- // get all states
- NodeSet<std::string> states = filterChildElements(_nsInfo.xmlNSPrefix + "state", _scxml);
- for (int i = 0; i < states.size(); i++) {
- Arabica::DOM::Element<std::string> stateElem = Arabica::DOM::Element<std::string>(states[i]);
- _states[ATTR(stateElem, "id")] = stateElem;
- if (HAS_ATTR(stateElem, "transient") && DOMUtils::attributeIsTrue(ATTR(stateElem, "transient")))
- continue;
- _globalStates.push_back(states[i]);
- }
- _startState = _states[ATTR(_scxml, "initial")];
-
- // initialize event trie with all events that might occur
- NodeSet<std::string> internalEventNames;
- internalEventNames.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "transition", _scxml).asNodeSet());
- internalEventNames.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "raise", _scxml).asNodeSet());
- internalEventNames.push_back(_xpath.evaluate("//" + _nsInfo.xpathPrefix + "send", _scxml).asNodeSet());
-
- for (int i = 0; i < internalEventNames.size(); i++) {
- if (HAS_ATTR_CAST(internalEventNames[i], "event")) {
- std::string eventNames = ATTR_CAST(internalEventNames[i], "event");
- std::list<std::string> events = tokenizeIdRefs(eventNames);
- for (std::list<std::string>::iterator eventIter = events.begin();
- eventIter != events.end(); eventIter++) {
- std::string eventName = *eventIter;
- if (boost::ends_with(eventName, "*"))
- eventName = eventName.substr(0, eventName.size() - 1);
- if (boost::ends_with(eventName, "."))
- eventName = eventName.substr(0, eventName.size() - 1);
- _eventTrie.addWord(eventName);
- }
- }
- }
-
- // enumerate transitions
- NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _scxml, true);
- int index = 0;
- for (int i = 0; i < transitions.size(); i++) {
- _transitions[Arabica::DOM::Element<std::string>(transitions[i])] = index++;
- }
-}
-
-void ChartToCPP::writeProgram(std::ostream& stream) {
-
- if (!HAS_ATTR(_scxml, "flat") || !DOMUtils::attributeIsTrue(ATTR(_scxml, "flat"))) {
- LOG(ERROR) << "Given SCXML document was not flattened";
- return;
- }
-
- if (!HAS_ATTR(_scxml, "datamodel") || ATTR(_scxml, "datamodel") != "promela") {
- LOG(ERROR) << "Can only convert SCXML documents with \"promela\" datamodel";
- return;
- }
-
- if (HAS_ATTR(_scxml, "binding") && ATTR(_scxml, "binding") != "early") {
- LOG(ERROR) << "Can only convert for early data bindings";
- return;
- }
-
- initNodes();
-
- writeEvents(stream);
- stream << std::endl;
- writeStates(stream);
- stream << std::endl;
- writeDeclarations(stream);
- stream << std::endl;
- writeFSM(stream);
- stream << std::endl;
- writeMain(stream);
- stream << std::endl;
-
-}
-
-} \ No newline at end of file
diff --git a/src/uscxml/transform/ChartToCPP.h.todo b/src/uscxml/transform/ChartToCPP.h.todo
deleted file mode 100644
index 8cdebb9..0000000
--- a/src/uscxml/transform/ChartToCPP.h.todo
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * @file
- * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de)
- * @copyright Simplified BSD
- *
- * @cond
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the FreeBSD license as published by the FreeBSD
- * project.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the FreeBSD license along with this
- * program. If not, see <http://www.opensource.org/licenses/bsd-license>.
- * @endcond
- */
-
-#ifndef FSMTOCPP_H_201672B0
-#define FSMTOCPP_H_201672B0
-
-#include "uscxml/interpreter/InterpreterDraft6.h"
-#include "uscxml/DOMUtils.h"
-#include "uscxml/util/Trie.h"
-
-#include <DOM/Document.hpp>
-#include <DOM/Node.hpp>
-#include <XPath/XPath.hpp>
-#include <ostream>
-
-namespace uscxml {
-
-class USCXML_API ChartToCPP : public InterpreterDraft6 {
-public:
- static void writeProgram(std::ostream& stream,
- const Interpreter& interpreter);
-
- static std::string beautifyIndentation(const std::string& code, int indent = 0);
-
-protected:
- ChartToCPP();
- void writeProgram(std::ostream& stream);
-
- void initNodes();
-
- void writeEvents(std::ostream& stream);
- void writeStates(std::ostream& stream);
- void writeDeclarations(std::ostream& stream);
- void writeExecutableContent(std::ostream& stream, const Arabica::DOM::Element<std::string>& node, int indent = 0);
- void writeInlineComment(std::ostream& stream, const Arabica::DOM::Element<std::string>& node);
- void writeFSM(std::ostream& stream);
- void writeEventDispatching(std::ostream& stream);
- void writeMain(std::ostream& stream);
-
- void writeIfBlock(std::ostream& stream, const Arabica::XPath::NodeSet<std::string>& condChain, int indent = 0);
- void writeDispatchingBlock(std::ostream& stream, const Arabica::XPath::NodeSet<std::string>& transChain, int indent = 0);
-
- Arabica::XPath::NodeSet<std::string> getTransientContent(const Arabica::DOM::Element<std::string>& state);
- Arabica::DOM::Node<std::string> getUltimateTarget(const Arabica::DOM::Element<std::string>& transition);
-
- Trie _eventTrie;
- Arabica::XPath::NodeSet<std::string> _globalStates;
- Arabica::DOM::Element<std::string> _startState;
- std::map<std::string, Arabica::DOM::Element<std::string> > _states;
- std::map<Arabica::DOM::Element<std::string>, int> _transitions;
-
-};
-
-}
-
-#endif /* end of include guard: FSMTOCPP_H_201672B0 */
diff --git a/src/uscxml/transform/Transformer.h b/src/uscxml/transform/Transformer.h
index 313bfaa..9d31b71 100644
--- a/src/uscxml/transform/Transformer.h
+++ b/src/uscxml/transform/Transformer.h
@@ -20,8 +20,8 @@
#ifndef TRANSFORMER_H_32113356
#define TRANSFORMER_H_32113356
+#include <iostream>
#include "uscxml/interpreter/InterpreterRC.h"
-#include <ostream>
namespace uscxml {
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 13b371e..dcb145a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -29,6 +29,10 @@ if (NOT WIN32)
USCXML_TEST_COMPILE(NAME test-promela-parser LABEL general/test-promela-parser FILES src/test-promela-parser.cpp)
target_link_libraries(test-promela-parser uscxml_transform)
USCXML_TEST_COMPILE(BUILD_ONLY NAME test-stress LABEL general/test-stress FILES src/test-stress.cpp)
+
+ USCXML_TEST_COMPILE(NAME test-c-machine LABEL general/test-c-machine FILES src/test-c-machine.cpp)
+ USCXML_TEST_COMPILE(NAME test-misc LABEL general/test-misc FILES src/test-misc.cpp)
+
endif()
if (APPLE)
@@ -75,10 +79,10 @@ add_test(test-done-data ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-browser ${CMAKE
find_program(SPIN spin)
find_program(GCC gcc)
+find_program(GPP g++)
if (NOT BUILD_MINIMAL)
# compile and add all reported issues tests
-
add_executable(test-w3c src/test-w3c.cpp)
target_link_libraries(test-w3c uscxml uscxml_transform)
@@ -138,6 +142,27 @@ if (NOT BUILD_MINIMAL)
endif()
endif()
+ if (BUILD_TESTS_GENERATED_C AND TEST_NAME MATCHES "^ecma\\/.*")
+ add_test(NAME "gen_c/${TEST_NAME}"
+ COMMAND ${CMAKE_COMMAND}
+ -DOUTDIR:FILEPATH=${CMAKE_CURRENT_BINARY_DIR}/gen_c
+ -DTESTFILE:FILEPATH=${W3C_TEST}
+ -DJSC_LIBRARY:FILEPATH=${JSC_LIBRARY}
+ -DUSCXML_TRANSFORM_BIN:FILEPATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/uscxml-transform
+ -DGCC_BIN:FILEPATH=${GCC}
+ -DGPP_BIN:FILEPATH=${GPP}
+ -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
+ -DUSCXML_PLATFORM_ID=${USCXML_PLATFORM_ID}
+ -DCMAKE_BINARY_DIR=${CMAKE_BINARY_DIR}
+ -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
+ -DSCAFFOLDING_FOR_GENERATED_C:FILEPATH=${CMAKE_CURRENT_SOURCE_DIR}/src/test-c-machine.cpp
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/w3c/run_generated_c_test.cmake)
+ set_property(TEST "gen_c/${TEST_NAME}" PROPERTY LABELS "gen_c/${TEST_NAME}")
+ set_tests_properties("gen_c/${TEST_NAME}" PROPERTIES TIMEOUT ${TEST_TIMEOUT})
+ set_tests_properties("gen_c/${TEST_NAME}" PROPERTIES DEPENDS uscxml-transform)
+
+ endif()
+
if (BUILD_TESTS_FSM AND BUILD_TESTS_FSM_ECMA AND TEST_NAME MATCHES "^ecma\\/.*")
add_test("fsm/${TEST_NAME}" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-w3c -f ${W3C_TEST})
set_property(TEST "fsm/${TEST_NAME}" PROPERTY LABELS "fsm/${TEST_NAME}")
diff --git a/test/ctest/CTestCustom.ctest.in b/test/ctest/CTestCustom.ctest.in
index 3b0243a..248ae8a 100644
--- a/test/ctest/CTestCustom.ctest.in
+++ b/test/ctest/CTestCustom.ctest.in
@@ -195,6 +195,44 @@ set(CTEST_CUSTOM_TESTS_IGNORE
# "spin/promela/test577.scxml" # send without target for basichttp
+ ### Ignore for generated C sources
+
+ # we do not support invokers yet
+ "gen_c/ecma/test191.scxml"
+ "gen_c/ecma/test192.scxml"
+ "gen_c/ecma/test207.scxml"
+ "gen_c/ecma/test215.scxml"
+ "gen_c/ecma/test216.scxml"
+ "gen_c/ecma/test220.scxml"
+ "gen_c/ecma/test223.scxml"
+ "gen_c/ecma/test224.scxml"
+ "gen_c/ecma/test225.scxml"
+ "gen_c/ecma/test226.scxml"
+ "gen_c/ecma/test228.scxml"
+ "gen_c/ecma/test229.scxml"
+ "gen_c/ecma/test230.scxml"
+ "gen_c/ecma/test232.scxml"
+ "gen_c/ecma/test233.scxml"
+ "gen_c/ecma/test234.scxml"
+ "gen_c/ecma/test235.scxml"
+ "gen_c/ecma/test236.scxml"
+ "gen_c/ecma/test237.scxml"
+ "gen_c/ecma/test239.scxml"
+ "gen_c/ecma/test240.scxml"
+ "gen_c/ecma/test241.scxml"
+ "gen_c/ecma/test242.scxml"
+ "gen_c/ecma/test243.scxml"
+ "gen_c/ecma/test244.scxml"
+ "gen_c/ecma/test245.scxml"
+ "gen_c/ecma/test247.scxml"
+ "gen_c/ecma/test250.scxml"
+ "gen_c/ecma/test252.scxml"
+ "gen_c/ecma/test253.scxml"
+ "gen_c/ecma/test276.scxml"
+
+ # we do not support io processors yet
+ "gen_c/ecma/test201.scxml"
+
)
diff --git a/test/src/test-c-machine.cpp b/test/src/test-c-machine.cpp
new file mode 100644
index 0000000..a9e5ecb
--- /dev/null
+++ b/test/src/test-c-machine.cpp
@@ -0,0 +1,429 @@
+#include <string.h>
+#include <stdlib.h> // malloc
+#include <assert.h> // assert
+#include <stdio.h> // printf
+#include <sstream> // stringstream
+#include <deque> // deque
+#include <boost/algorithm/string.hpp> // trim
+
+#define SCXML_VERBOSE 1
+
+#ifndef AUTOINCLUDE_TEST
+#include "test-c-machine.machine.c"
+#endif
+
+#include "uscxml/Convenience.h"
+//#include "uscxml/DOMUtils.h"
+#include "uscxml/Factory.h"
+#include "uscxml/InterpreterInfo.h"
+#include "uscxml/UUID.h"
+
+#include "uscxml/concurrency/DelayedEventQueue.h"
+#include "uscxml/concurrency/tinythread.h"
+
+#define USER_DATA(ctx) ((GenCInterpreterInfo*)(((scxml_ctx*)ctx)->user_data))
+
+using namespace uscxml;
+
+typedef struct scxml_foreach_info scxml_foreach_info;
+struct scxml_foreach_info {
+ size_t iterations;
+ size_t currIteration;
+};
+
+class GenCInterpreterInfo : public InterpreterInfo {
+public:
+ NameSpaceInfo getNameSpaceInfo() const {
+ return nsInfo;
+ }
+ const std::string& getName() {
+ return name;
+ }
+ const std::string& getSessionId() {
+ return sessionId;
+ }
+ const std::map<std::string, IOProcessor>& getIOProcessors() {
+ return ioProcs;
+ }
+ bool isInState(const std::string& stateId) {
+ for (int i = 0 ; i < SCXML_NUMBER_STATES; i++) {
+ if (scxml_states[i].name != NULL && IS_SET(i, ctx->config) && stateId == scxml_states[i].name)
+ return true;
+ }
+ return false;
+ }
+ Arabica::DOM::Document<std::string> getDocument() const {
+ return document;
+ }
+ const std::map<std::string, Invoker>& getInvokers() {
+ return invokers;
+ }
+
+ NameSpaceInfo nsInfo;
+ std::string name;
+ std::string sessionId;
+ std::map<std::string, IOProcessor> ioProcs;
+ std::map<std::string, Invoker> invokers;
+ Arabica::DOM::Document<std::string> document;
+ scxml_ctx* ctx;
+ boost::shared_ptr<DataModelImpl> datamodel;
+
+ std::map<const scxml_elem_foreach*, scxml_foreach_info*> foreachInfo;
+ std::deque<Event*> iq;
+ std::deque<Event*> eq;
+
+ DelayedEventQueue delayQueue;
+ std::map<std::string, SendRequest*> sendIds;
+
+ tthread::condition_variable monitor;
+ tthread::mutex mutex;
+};
+
+int matches(const char* desc, const char* event) {
+ const char* dPtr = desc;
+ const char* ePtr = event;
+ while(true) {
+
+ // next event descriptor
+ if (*dPtr == ' ') {
+ dPtr++;
+ ePtr = event;
+ continue;
+ }
+
+ // descriptor is done, return match
+ if (*dPtr == 0 || *dPtr == '*')
+ return true;
+
+ // descriptor differs from event name
+ if (*dPtr != *ePtr)
+ return false;
+
+ // move both pointers one character
+ dPtr++;
+ ePtr++;
+ }
+}
+
+int exec_content_raise(const scxml_ctx* ctx, const char* event) {
+ Event* e = new Event();
+ e->name = strdup(event);
+ printf("Raising Internal Event: %s\n", e->name.c_str());
+ USER_DATA(ctx)->iq.push_back(e);
+ return SCXML_ERR_OK;
+}
+
+int is_true(const scxml_ctx* ctx, const char* expr) {
+ try {
+ return USER_DATA(ctx)->datamodel->evalAsBool(expr);
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ }
+ return false;
+}
+
+int is_enabled(const scxml_ctx* ctx, const scxml_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 is_true(ctx, t->condition);
+ return true;
+ } else {
+ // spontaneous transition, but real event
+ return false;
+ }
+ }
+
+ // real transition, real event
+ if (matches(t->event, event->name.c_str())) {
+ if (t->condition != NULL)
+ return is_true(ctx, t->condition);
+ return true;
+ }
+ return false;
+}
+
+int raise_done_event(const scxml_ctx* ctx, const scxml_state* state) {
+ std::string doneName = "done.state.";
+ exec_content_raise(ctx, (doneName + state->name).c_str());
+ return SCXML_ERR_OK;
+}
+
+void delayedSend(void* ctx, std::string eventName) {
+ tthread::lock_guard<tthread::mutex> lock(USER_DATA(ctx)->mutex);
+
+ SendRequest* e = USER_DATA(ctx)->sendIds[eventName];
+ if (e->target == "#_internal") {
+ printf("Pushing Internal Event: %s\n", e->name.c_str());
+ USER_DATA(ctx)->iq.push_back(e);
+ } else {
+ printf("Pushing External Event: %s\n", e->name.c_str());
+ USER_DATA(ctx)->eq.push_back(e);
+ }
+ USER_DATA(ctx)->monitor.notify_all();
+}
+
+int exec_content_cancel(const scxml_ctx* ctx, const char* sendid, const char* sendidexpr) {
+ std::string eventId;
+ if (sendid != NULL) {
+ eventId = sendid;
+ } else if (sendidexpr != NULL) {
+ eventId = USER_DATA(ctx)->datamodel->evalAsString(sendidexpr);
+ }
+
+ if (eventId.length() > 0) {
+ USER_DATA(ctx)->delayQueue.cancelEvent(eventId);
+ } else {
+ exec_content_raise(ctx, "error.execution");
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_OK;
+}
+
+int exec_content_send(const scxml_ctx* ctx, const scxml_elem_send* send) {
+ SendRequest* e = new SendRequest();
+
+ std::string target;
+ if (send->target != NULL) {
+ e->target = send->target;
+ } else if (send->targetexpr != NULL) {
+ e->target = USER_DATA(ctx)->datamodel->evalAsString(send->targetexpr);
+ }
+
+ if (e->target.size() > 0 && (e->target[0] != '#' || e->target[1] != '_')) {
+ delete e;
+ exec_content_raise(ctx, "error.execution");
+ return SCXML_ERR_INVALID_TARGET;
+ }
+
+ if (send->type != NULL) {
+ e->type = send->type;
+ } else if (send->typeexpr != NULL) {
+ e->type = USER_DATA(ctx)->datamodel->evalAsString(send->typeexpr);
+ } else {
+ e->type = "http://www.w3.org/TR/scxml/#SCXMLEventProcessor";
+ }
+
+ // only one somewhat supported
+ if (e->type != "http://www.w3.org/TR/scxml/#SCXMLEventProcessor") {
+ delete e;
+ exec_content_raise(ctx, "error.execution");
+ return SCXML_ERR_INVALID_TARGET;
+ }
+
+ e->origintype = e->type;
+
+ if (send->eventexpr != NULL) {
+ e->name = USER_DATA(ctx)->datamodel->evalAsString(send->eventexpr);
+ } else {
+ e->name = strdup(send->event);
+ }
+
+ const scxml_elem_param* param = send->params;
+ while (param && ELEM_PARAM_IS_SET(param)) {
+ Data paramValue;
+ if (param->expr != NULL) {
+ paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->expr);
+ } else if(param->location) {
+ paramValue = USER_DATA(ctx)->datamodel->getStringAsData(param->location);
+ }
+ e->params.insert(std::make_pair(param->name, paramValue));
+ param++;
+ }
+
+ if (send->content != NULL) {
+ e->data = Data(send->content, Data::VERBATIM);
+ }
+
+ const char* sendid = NULL;
+ if (send->id != NULL) {
+ sendid = send->id;
+ } else {
+ sendid = strdup(UUID::getUUID().c_str());
+ if (send->idlocation != NULL)
+ USER_DATA(ctx)->datamodel->assign(send->idlocation, Data(sendid, Data::VERBATIM));
+ }
+
+ size_t delayMs = 0;
+ std::string delay;
+ if (send->delayexpr != NULL) {
+ delay = USER_DATA(ctx)->datamodel->evalAsString(send->delayexpr);
+ } else if (send->delay != NULL) {
+ delay = send->delay;
+ }
+ if (delay.size() > 0) {
+ boost::trim(delay);
+
+ NumAttr delayAttr(delay);
+ if (iequals(delayAttr.unit, "ms")) {
+ delayMs = strTo<uint32_t>(delayAttr.value);
+ } else if (iequals(delayAttr.unit, "s")) {
+ delayMs = strTo<double>(delayAttr.value) * 1000;
+ } else if (delayAttr.unit.length() == 0) { // unit less delay is interpreted as milliseconds
+ delayMs = strTo<uint32_t>(delayAttr.value);
+ } else {
+ std::cerr << "Cannot make sense of delay value " << delay << ": does not end in 's' or 'ms'";
+ }
+ }
+
+ USER_DATA(ctx)->sendIds[sendid] = e;
+ if (delayMs > 0) {
+ USER_DATA(ctx)->delayQueue.addEvent(sendid, delayedSend, delayMs, (void*)ctx);
+ } else {
+ delayedSend((void*)ctx, sendid);
+ }
+
+ return SCXML_ERR_OK;
+}
+
+int exec_content_init(const scxml_ctx* ctx, const scxml_elem_data* data) {
+ while(ELEM_DATA_IS_SET(data)) {
+ Data d;
+ if (data->expr != NULL) {
+ d = Data(data->expr, Data::INTERPRETED);
+ } else if (data->content != NULL) {
+ d = Data(data->content, Data::INTERPRETED);
+ } else {
+ d = Data("undefined", Data::INTERPRETED);
+ }
+ try {
+ USER_DATA(ctx)->datamodel->init(data->id, d);
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ }
+ data++;
+ }
+ return SCXML_ERR_OK;
+}
+
+int exec_content_assign(const scxml_ctx* ctx, const char* location, const char* expr) {
+ try {
+ Data d(expr, Data::INTERPRETED);
+ USER_DATA(ctx)->datamodel->assign(location, d);
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_OK;
+}
+
+int exec_content_foreach_init(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
+ try {
+ scxml_foreach_info* feInfo = (scxml_foreach_info*)malloc(sizeof(scxml_foreach_info));
+ USER_DATA(ctx)->foreachInfo[foreach] = feInfo;
+
+ feInfo->iterations = USER_DATA(ctx)->datamodel->getLength(foreach->array);
+ feInfo->currIteration = 0;
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_OK;
+}
+
+int exec_content_foreach_next(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
+ try {
+ scxml_foreach_info* feInfo = USER_DATA(ctx)->foreachInfo[foreach];
+ if (feInfo->currIteration < feInfo->iterations) {
+ USER_DATA(ctx)->datamodel->setForeach((foreach->item != NULL ? foreach->item : ""),
+ (foreach->array != NULL ? foreach->array : ""),
+ (foreach->index != NULL ? foreach->index : ""),
+ feInfo->currIteration);
+ feInfo->currIteration++;
+ return SCXML_ERR_OK;
+ }
+ } catch (Event e) {
+ exec_content_raise(ctx, e.name.c_str());
+ free(USER_DATA(ctx)->foreachInfo[foreach]);
+ USER_DATA(ctx)->foreachInfo.erase(foreach);
+ return SCXML_ERR_EXEC_CONTENT;
+ }
+ return SCXML_ERR_FOREACH_DONE;
+}
+
+int exec_content_foreach_done(const scxml_ctx* ctx, const scxml_elem_foreach* foreach) {
+ free(USER_DATA(ctx)->foreachInfo[foreach]);
+ USER_DATA(ctx)->foreachInfo.erase(foreach);
+ return SCXML_ERR_OK;
+}
+
+int exec_content_log(const scxml_ctx* ctx, const char* label, const char* expr) {
+ if (label != 0) {
+ printf("%s: %s\n", label, expr);
+ } else {
+ printf("%s\n", USER_DATA(ctx)->datamodel->evalAsString(expr).c_str());
+ }
+ return SCXML_ERR_OK;
+}
+
+void* dequeue_external(const scxml_ctx* ctx) {
+ tthread::lock_guard<tthread::mutex> lock(USER_DATA(ctx)->mutex);
+ while (USER_DATA(ctx)->eq.size() == 0) {
+ USER_DATA(ctx)->monitor.wait(USER_DATA(ctx)->mutex);
+ }
+ Event* e = USER_DATA(ctx)->eq.front();
+ USER_DATA(ctx)->eq.pop_front();
+ USER_DATA(ctx)->datamodel->setEvent(*e);
+ printf("Popping External Event: %s\n", e->name.c_str());
+ return e;
+}
+
+void* dequeue_internal(const scxml_ctx* ctx) {
+ if (USER_DATA(ctx)->iq.size() == 0)
+ return NULL;
+ Event* e = USER_DATA(ctx)->iq.front();
+ USER_DATA(ctx)->iq.pop_front();
+ USER_DATA(ctx)->datamodel->setEvent(*e);
+ printf("Popping Internal Event: %s\n", e->name.c_str());
+ return e;
+}
+
+int main(int argc, char** argv) {
+ int err;
+
+ // setup info object required for datamodel
+ GenCInterpreterInfo interpreterInfo;
+ interpreterInfo.name = "adsf";
+ interpreterInfo.sessionId = "rfwef";
+ interpreterInfo.datamodel = Factory::getInstance()->createDataModel("ecmascript", &interpreterInfo);
+ interpreterInfo.delayQueue.start();
+
+ scxml_ctx ctx;
+ memset(&ctx, 0, sizeof(scxml_ctx));
+ interpreterInfo.ctx = &ctx;
+
+ // set info object as user data
+ ctx.user_data = (void*)&interpreterInfo;
+
+ // register callbacks with scxml context
+ ctx.is_enabled = &is_enabled;
+ ctx.is_true = &is_true;
+ ctx.raise_done_event = &raise_done_event;
+
+ ctx.exec_content_send = &exec_content_send;
+ ctx.exec_content_raise = &exec_content_raise;
+ ctx.exec_content_cancel = &exec_content_cancel;
+ ctx.exec_content_log = &exec_content_log;
+ ctx.exec_content_assign = &exec_content_assign;
+ ctx.exec_content_foreach_init = &exec_content_foreach_init;
+ ctx.exec_content_foreach_next = &exec_content_foreach_next;
+ ctx.exec_content_foreach_done = &exec_content_foreach_done;
+ ctx.dequeue_external = &dequeue_external;
+ ctx.dequeue_internal = &dequeue_internal;
+ ctx.exec_content_init = &exec_content_init;
+
+ while((err = scxml_step(&ctx)) == SCXML_ERR_OK);
+ assert(ctx.flags & SCXML_CTX_TOP_LEVEL_FINAL);
+
+ size_t passIdx = 0;
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (scxml_states[i].name && strcmp(scxml_states[i].name, "pass") == 0) {
+ passIdx = i;
+ break;
+ }
+ }
+ assert(IS_SET(passIdx, ctx.config));
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/test/src/test-c-machine.machine.c b/test/src/test-c-machine.machine.c
new file mode 100644
index 0000000..d4c924d
--- /dev/null
+++ b/test/src/test-c-machine.machine.c
@@ -0,0 +1,532 @@
+#include <stdint.h> // explicit types
+#include <stddef.h> // NULL
+
+#define IS_SET(idx, bitset) ((bitset[idx >> 3] & (1 << (idx & 7))) != 0)
+#define SET_BIT(idx, bitset) bitset[idx >> 3] |= (1 << (idx & 7));
+#define CLEARBIT(idx, bitset) bitset[idx >> 3] &= (1 << (idx & 7)) ^ 0xFF;
+
+// error return codes
+#define SCXML_ERR_OK 0
+#define SCXML_ERR_IDLE 1
+#define SCXML_ERR_DONE 2
+#define SCXML_ERR_MISSING_CALLBACK 3
+#define SCXML_ERR_FOREACH_DONE 4
+#define SCXML_ERR_EXEC_CONTENT 5
+#define SCXML_ERR_INVALID_TARGET 6
+#define SCXML_ERR_INVALID_TYPE 7
+
+#define SCXML_NUMBER_STATES 5
+#define SCXML_NUMBER_TRANSITIONS 4
+
+#define SCXML_TRANS_SPONTANEOUS 0x01
+#define SCXML_TRANS_TARGETLESS 0x02
+#define SCXML_TRANS_INTERNAL 0x04
+
+#define SCXML_STATE_ATOMIC 0x01
+#define SCXML_STATE_PARALLEL 0x02
+#define SCXML_STATE_COMPOUND 0x03
+#define SCXML_STATE_FINAL 0x04
+#define SCXML_STATE_HISTORY_DEEP 0x05
+#define SCXML_STATE_HISTORY_SHALLOW 0x06
+#define SCXML_STATE_INITIAL 0x07
+
+#define SCXML_CTX_PRISTINE 0x00
+#define SCXML_CTX_SPONTANEOUS 0x01
+#define SCXML_CTX_INITIALIZED 0x02
+#define SCXML_CTX_TOP_LEVEL_FINAL 0x04
+#define SCXML_CTX_TRANSITION_FOUND 0x08
+
+#define ELEM_DATA_IS_SET(data) (data->id != NULL)
+#define ELEM_PARAM_IS_SET(param) (param->name != NULL)
+
+
+typedef struct scxml_transition scxml_transition;
+typedef struct scxml_state scxml_state;
+typedef struct scxml_ctx scxml_ctx;
+typedef struct scxml_invoke scxml_invoke;
+
+typedef struct scxml_elem_send scxml_elem_send;
+typedef struct scxml_elem_param scxml_elem_param;
+typedef struct scxml_elem_data scxml_elem_data;
+typedef struct scxml_elem_foreach scxml_elem_foreach;
+
+typedef void* (*dequeue_internal_cb_t)(const scxml_ctx* ctx);
+typedef void* (*dequeue_external_cb_t)(const scxml_ctx* ctx);
+typedef int (*is_enabled_cb_t)(const scxml_ctx* ctx, const scxml_transition* transition, const void* event);
+typedef int (*is_true_cb_t)(const scxml_ctx* ctx, const char* expr);
+typedef int (*exec_content_t)(const scxml_ctx* ctx, const scxml_state* state, const void* event);
+typedef int (*raise_done_event_t)(const scxml_ctx* ctx, const scxml_state* state);
+typedef int (*invoke_t)(const scxml_ctx* ctx, const scxml_state* s, const scxml_invoke* x);
+
+typedef int (*exec_content_log_t)(const scxml_ctx* ctx, const char* label, const char* expr);
+typedef int (*exec_content_raise_t)(const scxml_ctx* ctx, const char* event);
+typedef int (*exec_content_send_t)(const scxml_ctx* ctx, const scxml_elem_send* send);
+typedef int (*exec_content_foreach_init_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);
+typedef int (*exec_content_foreach_next_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);
+typedef int (*exec_content_foreach_done_t)(const scxml_ctx* ctx, const scxml_elem_foreach* foreach);
+typedef int (*exec_content_assign_t)(const scxml_ctx* ctx, const char* location, const char* expr);
+typedef int (*exec_content_init_t)(const scxml_ctx* ctx, const scxml_elem_data* data);
+typedef int (*exec_content_cancel_t)(const scxml_ctx* ctx, const char* sendid, const char* sendidexpr);
+typedef int (*exec_content_finalize_t)(const scxml_ctx* ctx, const scxml_invoke* invoker, const void* event);
+typedef int (*exec_content_script_t)(const scxml_ctx* ctx, const char* src, const char* content);
+
+struct scxml_elem_data {
+ const char* id;
+ const char* src;
+ const char* expr;
+ const char* content;
+};
+
+struct scxml_state {
+ const char* name; // eventual name
+ exec_content_t on_entry; // on entry handlers
+ exec_content_t on_exit; // on exit handlers
+ invoke_t invoke; // invocations
+ char children[1]; // all children
+ char completion[1]; // default completion
+ char ancestors[1]; // all ancestors
+ const scxml_elem_data* data;
+ uint8_t type; // atomic, parallel, compound, final, history
+};
+
+struct scxml_transition {
+ uint16_t source;
+ char target[1];
+ const char* event;
+ const char* condition;
+ exec_content_t on_transition;
+ uint8_t type;
+ char conflicts[1];
+ char exit_set[1];
+};
+
+struct scxml_elem_foreach {
+ const char* array;
+ const char* item;
+ const char* index;
+};
+
+struct scxml_elem_param {
+ const char* name;
+ const char* expr;
+ const char* location;
+};
+
+struct scxml_elem_invoke {
+ const char* type;
+ const char* typeexpr;
+ const char* src;
+ const char* srcexpr;
+ const char* id;
+ const char* idlocation;
+ const char* namelist;
+ uint8_t autoforward;
+ const scxml_elem_param* params;
+ const exec_content_finalize_t* finalize;
+ const char* content;
+ void* user_data;
+};
+
+struct scxml_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;
+ const char* content;
+ const scxml_elem_param* params;
+ void* user_data;
+};
+
+struct scxml_ctx {
+ uint8_t flags;
+
+ char config[1];
+ char history[1];
+ char pending_invokes[1];
+ char initialized_data[1];
+
+ void* user_data;
+
+ dequeue_internal_cb_t dequeue_internal;
+ dequeue_external_cb_t dequeue_external;
+ is_enabled_cb_t is_enabled;
+ is_true_cb_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;
+};
+
+scxml_elem_data scxml_elem_datas[4] = {
+ { "Var1", NULL, NULL, NULL },
+ { NULL, NULL, NULL, NULL },
+ { "Var2", NULL, "1", NULL },
+ { NULL, NULL, NULL, NULL }
+};
+
+int s1_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if (ctx->exec_content_assign != NULL) {
+ if ((ctx->exec_content_assign(ctx, "Var1", "Var2")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+int s1_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ s1_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+int pass_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if (ctx->exec_content_log != NULL) {
+ if ((ctx->exec_content_log(ctx, "Outcome", "'pass'")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+int pass_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ pass_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+int fail_on_entry_0(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ int err = SCXML_ERR_OK;
+ if (ctx->exec_content_log != NULL) {
+ if ((ctx->exec_content_log(ctx, "Outcome", "'fail'")) != SCXML_ERR_OK) return err;
+ } else {
+ return SCXML_ERR_MISSING_CALLBACK;
+ }
+ return SCXML_ERR_OK;
+}
+
+int fail_on_entry(const scxml_ctx* ctx, const scxml_state* state, const void* event) {
+ fail_on_entry_0(ctx, state, event);
+ return SCXML_ERR_OK;
+}
+
+scxml_state scxml_states[5] = {
+ { NULL, NULL, NULL, NULL, { 0x1e /* 01111, 1 2 3 4 */ }, { 0x02 /* 01000, 1 */ }, { 0x00 /* 00000, */ }, (const scxml_elem_data*)&scxml_elem_datas[0], SCXML_STATE_COMPOUND },
+ { "s0", NULL, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_ATOMIC },
+ { "s1", s1_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, (const scxml_elem_data*)&scxml_elem_datas[1], SCXML_STATE_ATOMIC },
+ { "pass", pass_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_FINAL },
+ { "fail", fail_on_entry, NULL, NULL, { 0x00 /* 00000, */ }, { 0x00 /* 00000, */ }, { 0x01 /* 10000, 0 */ }, NULL, SCXML_STATE_FINAL }
+};
+
+scxml_transition scxml_transitions[4] = {
+ { 1, { 0x04 /* 00100 */ }, NULL, "typeof Var2 === 'undefined' ", NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
+ { 1, { 0x10 /* 00001 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
+ { 2, { 0x08 /* 00010 */ }, NULL, "Var1===Var2", NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } },
+ { 2, { 0x10 /* 00001 */ }, NULL, NULL, NULL, SCXML_TRANS_SPONTANEOUS, { 0x0f /* 1111 */ }, { 0x1e /* 01111 */ } }
+};
+
+#ifdef SCXML_VERBOSE
+void printStateNames(const char* a) {
+ const char* seperator = "";
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, a)) {
+ printf("%s%s", seperator, (scxml_states[i].name != NULL ? scxml_states[i].name : "UNK"));
+ seperator = ", ";
+ }
+ }
+ printf("\n");
+}
+
+void printBitsetIndices(const char* a, size_t length) {
+ const char* seperator = "";
+ for (int i = 0; i < length; i++) {
+ if (IS_SET(i, a)) {
+ printf("%s%d", seperator, i);
+ seperator = ", ";
+ }
+ }
+ printf("\n");
+}
+#endif
+
+void bit_or(char* dest, const char* mask, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ dest[i] |= mask[i];
+ }
+}
+
+void bit_copy(char* dest, const char* source, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ dest[i] = source[i];
+ }
+}
+
+int bit_has_and(const char* a, const char* b, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ if (a[i] & b[i])
+ return true;
+ }
+ return false;
+}
+
+void bit_and_not(char* dest, const char* mask, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ dest[i] &= ~mask[i];
+ }
+}
+
+int bit_any_set(const char* a, size_t length) {
+ for (int i = 0; i < length; ++i) {
+ if (a[i] > 0)
+ return true;
+ }
+ return false;
+}
+
+int scxml_step(scxml_ctx* ctx) {
+
+#ifdef SCXML_VERBOSE
+ printStateNames(ctx->config);
+#endif
+
+MACRO_STEP:
+ ctx->flags &= ~SCXML_CTX_TRANSITION_FOUND;
+
+ if (ctx->flags & SCXML_CTX_TOP_LEVEL_FINAL)
+ return SCXML_ERR_DONE;
+
+ int err = SCXML_ERR_OK;
+ char conflicts[1] = {0};
+ char target_set[1] = {0};
+ char exit_set[1] = {0};
+ char trans_set[1] = {0};
+ char entry_set[1] = {0};
+
+ void* event;
+ if (ctx->flags == SCXML_CTX_PRISTINE) {
+ bit_or(target_set, scxml_states[0].completion, 1);
+ ctx->flags |= SCXML_CTX_SPONTANEOUS | SCXML_CTX_INITIALIZED;
+ goto COMPLETE_CONFIG;
+ }
+
+ if (ctx->flags & SCXML_CTX_SPONTANEOUS) {
+ event = NULL;
+ goto SELECT_TRANSITIONS;
+ }
+ if ((event = ctx->dequeue_internal(ctx)) != NULL) {
+ goto SELECT_TRANSITIONS;
+ }
+ if ((event = ctx->dequeue_external(ctx)) != NULL) {
+ goto SELECT_TRANSITIONS;
+ }
+
+SELECT_TRANSITIONS:
+ for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {
+ // is the transition active?
+ if (IS_SET(scxml_transitions[i].source, ctx->config)) {
+ // is it non-conflicting?
+ if (!IS_SET(i, conflicts)) {
+ // is it enabled?
+ if (ctx->is_enabled(ctx, &scxml_transitions[i], event) > 0) {
+ // remember that we found a transition
+ ctx->flags |= SCXML_CTX_TRANSITION_FOUND;
+
+ // transitions that are pre-empted
+ bit_or(conflicts, scxml_transitions[i].conflicts, 1);
+
+ // states that are directly targeted (resolve as entry-set later)
+ bit_or(target_set, scxml_transitions[i].target, 1);
+
+ // states that will be left
+ bit_or(exit_set, scxml_transitions[i].exit_set, 1);
+
+ SET_BIT(i, trans_set);
+ }
+ }
+ }
+ }
+
+ if (ctx->flags & SCXML_CTX_TRANSITION_FOUND) {
+ ctx->flags |= SCXML_CTX_SPONTANEOUS;
+ } else {
+ ctx->flags &= ~SCXML_CTX_SPONTANEOUS;
+ goto MACRO_STEP;
+ }
+
+REMEMBER_HISTORY:
+ // are my ancestors in the exit set?
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, ctx->config) && bit_has_and(exit_set, scxml_states[i].ancestors, 1)) {
+ SET_BIT(i, ctx->history);
+ }
+ }
+
+#ifdef SCXML_VERBOSE
+ printf("Exiting: ");
+ printStateNames(exit_set);
+#endif
+
+EXIT_STATES:
+ for (int i = SCXML_NUMBER_STATES - 1; i >= 0; i--) {
+ if (IS_SET(i, exit_set) && IS_SET(i, ctx->config)) {
+ // call all on exit handlers
+ if (scxml_states[i].on_exit != NULL) {
+ if((err = scxml_states[i].on_exit(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)
+ return err;
+ }
+ CLEARBIT(i, ctx->config);
+ }
+ }
+
+COMPLETE_CONFIG:
+ // calculate new entry set
+ bit_copy(entry_set, target_set, 1);
+
+ // iterate for ancestors
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, entry_set)) {
+ bit_or(entry_set, scxml_states[i].ancestors, 1);
+ }
+ }
+
+ADD_DESCENDANTS:
+ // iterate for descendants
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, entry_set)) {
+ switch (scxml_states[i].type) {
+ case SCXML_STATE_PARALLEL: {
+ bit_or(entry_set, scxml_states[i].completion, 1);
+ break;
+ }
+ case SCXML_STATE_INITIAL: {
+ for (int j = 0; j < SCXML_NUMBER_TRANSITIONS; j++) {
+ if (scxml_transitions[j].source == i) {
+ SET_BIT(j, trans_set);
+ CLEARBIT(i, entry_set);
+ bit_or(entry_set, scxml_transitions[j].target, 1);
+ // one target may have been above, reestablish completion
+ goto ADD_DESCENDANTS;
+ }
+ }
+ break;
+ }
+ case SCXML_STATE_COMPOUND: { // we need to check whether one child is already in entry_set
+ if (!bit_has_and(entry_set, scxml_states[i].children, 1)) {
+ bit_or(entry_set, scxml_states[i].completion, 1);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+#ifdef SCXML_VERBOSE
+ printf("Transitions: ");
+ printBitsetIndices(trans_set, sizeof(char) * 8 * 1);
+#endif
+
+TAKE_TRANSITIONS:
+ for (int i = 0; i < SCXML_NUMBER_TRANSITIONS; i++) {
+ if (IS_SET(i, trans_set)) {
+ // call executable content in transition
+ if (scxml_transitions[i].on_transition != NULL) {
+ if((err = scxml_transitions[i].on_transition(ctx,
+ &scxml_states[scxml_transitions[i].source],
+ event)) != SCXML_ERR_OK)
+ return err;
+ }
+ }
+ }
+
+#ifdef SCXML_VERBOSE
+ printf("Entering: ");
+ printStateNames(entry_set);
+#endif
+
+ENTER_STATES:
+ for (int i = 0; i < SCXML_NUMBER_STATES; i++) {
+ if (IS_SET(i, entry_set) && !IS_SET(i, ctx->config)) {
+ SET_BIT(i, ctx->config);
+ if (scxml_states[i].on_entry != NULL) {
+ if((err = scxml_states[i].on_entry(ctx, &scxml_states[i], event)) != SCXML_ERR_OK)
+ return err;
+ }
+
+ // initialize data
+ if(!IS_SET(i, ctx->initialized_data)) {
+ if (scxml_states[i].data != NULL && ctx->exec_content_init != NULL) {
+ ctx->exec_content_init(ctx, scxml_states[i].data);
+ }
+ SET_BIT(i, ctx->initialized_data);
+ }
+
+ // handle final states
+ if (scxml_states[i].type == SCXML_STATE_FINAL) {
+ if (scxml_states[i].ancestors[0] == 0x01) {
+ ctx->flags |= SCXML_CTX_TOP_LEVEL_FINAL;
+ } else {
+ // raise done event
+ size_t parent = 0;
+ for (int j = 0; j < SCXML_NUMBER_STATES; j++) {
+ // we could trade runtime for memory here by saving the parent index
+ if (!IS_SET(j, scxml_states[i].ancestors)) {
+ if (parent != 0) {
+ break;
+ }
+ continue;
+ } else {
+ parent = j;
+ }
+ }
+ ctx->raise_done_event(ctx, &scxml_states[parent]);
+ }
+
+ /**
+ * 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 (int j = 0; j < SCXML_NUMBER_STATES; j++) {
+ if (scxml_states[j].type == SCXML_STATE_PARALLEL) {
+ char parallel_children[2] = {0, 0};
+ size_t parallel = j;
+ for (int k = 0; k < SCXML_NUMBER_STATES; k++) {
+ if (IS_SET(parallel, scxml_states[k].ancestors) && IS_SET(k, ctx->config)) {
+ if (scxml_states[k].type == SCXML_STATE_FINAL) {
+ bit_and_not(parallel_children, scxml_states[k].ancestors, 2);
+ } else {
+ SET_BIT(k, parallel_children);
+ }
+ }
+ }
+ if (!bit_any_set(parallel_children, 2)) {
+ ctx->raise_done_event(ctx, &scxml_states[parallel]);
+ }
+ }
+ }
+
+ }
+
+ }
+ }
+
+ return SCXML_ERR_OK;
+}
+
diff --git a/test/src/test-misc.cpp b/test/src/test-misc.cpp
new file mode 100644
index 0000000..09cda31
--- /dev/null
+++ b/test/src/test-misc.cpp
@@ -0,0 +1,3 @@
+int main(int argc, char** argv) {
+ return 0;
+} \ No newline at end of file
diff --git a/test/src/test-w3c.cpp b/test/src/test-w3c.cpp
index 1480ecb..0069d0d 100644
--- a/test/src/test-w3c.cpp
+++ b/test/src/test-w3c.cpp
@@ -20,11 +20,36 @@
static bool withFlattening = false;
static double delayFactor = 1;
+static size_t benchmarkRuns = 0;
static std::string documentURI;
int retCode = EXIT_FAILURE;
uscxml::Interpreter interpreter;
+void printUsageAndExit(const char* progName) {
+ // remove path from program name
+ std::string progStr(progName);
+ if (progStr.find_last_of(PATH_SEPERATOR) != std::string::npos) {
+ progStr = progStr.substr(progStr.find_last_of(PATH_SEPERATOR) + 1, progStr.length() - (progStr.find_last_of(PATH_SEPERATOR) + 1));
+ }
+
+ printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progStr.c_str());
+ printf("Usage\n");
+ printf("\t%s", progStr.c_str());
+ printf(" [-f] [-dN] [-bN]");
+#ifdef BUILD_AS_PLUGINS
+ printf(" [-p pluginPath]");
+#endif
+ printf(" URL");
+ printf("\n");
+ printf("Options\n");
+ printf("\t-f : flatten to SCXML state-machine\n");
+ printf("\t-d FACTOR : delay factor\n");
+ printf("\t-b ITERATIONS : benchmark with number of runs\n");
+ printf("\n");
+ exit(1);
+}
+
class W3CStatusMonitor : public uscxml::StateTransitionMonitor {
void beforeCompletion(uscxml::Interpreter tmp) {
@@ -61,7 +86,7 @@ int main(int argc, char** argv) {
}
int option;
- while ((option = getopt(argc, argv, "fd:")) != -1) {
+ while ((option = getopt(argc, argv, "fd:b:")) != -1) {
switch(option) {
case 'f':
withFlattening = true;
@@ -69,6 +94,9 @@ int main(int argc, char** argv) {
case 'd':
delayFactor = strTo<double>(optarg);
break;
+ case 'b':
+ benchmarkRuns = strTo<size_t>(optarg);
+ break;
default:
break;
}
@@ -76,7 +104,7 @@ int main(int argc, char** argv) {
documentURI = argv[optind];
- LOG(INFO) << "Processing " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor));
+ LOG(INFO) << "Processing " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor)) << (benchmarkRuns > 0 ? " for " + toStr(benchmarkRuns) + " benchmarks" : "");
if (withFlattening) {
interpreter = Interpreter::fromURL(documentURI);
Transformer flattener = ChartToFlatSCXML::transform(interpreter);
@@ -117,11 +145,37 @@ int main(int argc, char** argv) {
}
if (interpreter) {
- W3CStatusMonitor* vm = new W3CStatusMonitor();
- interpreter.addMonitor(vm);
-
- interpreter.start();
- while(interpreter.runOnMainThread(25));
+ if (benchmarkRuns > 0) {
+ LOG(INFO) << "Benchmarking " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor));
+
+ InterpreterState state = interpreter.getState();
+
+ double avg = 0;
+ uint64_t now = 0;
+ size_t remainingRuns = benchmarkRuns;
+ uint64_t start = tthread::chrono::system_clock::now();
+
+ while(remainingRuns-- > 0) {
+ now = tthread::chrono::system_clock::now();
+ for(;;) {
+ state = interpreter.step(true);
+ if (state < 0)
+ break;
+ }
+ avg += (double)(tthread::chrono::system_clock::now() - now) / (double)benchmarkRuns;
+ interpreter.reset();
+ }
+ uint64_t totalDuration = tthread::chrono::system_clock::now() - start;
+ std::cout << benchmarkRuns << " iterations in " << totalDuration << " ms" << std::endl;
+ std::cout << avg << " ms on average" << std::endl;
+
+ } else {
+ W3CStatusMonitor* vm = new W3CStatusMonitor();
+ interpreter.addMonitor(vm);
+
+ interpreter.start();
+ while(interpreter.runOnMainThread(25));
+ }
}
} catch(Event e) {
std::cout << e << std::endl;
diff --git a/test/w3c/run_generated_c_test.cmake b/test/w3c/run_generated_c_test.cmake
new file mode 100644
index 0000000..7ae7b4d
--- /dev/null
+++ b/test/w3c/run_generated_c_test.cmake
@@ -0,0 +1,47 @@
+# convert given file to promela and run spin
+
+get_filename_component(TEST_FILE_NAME ${TESTFILE} NAME)
+execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTDIR})
+
+message(STATUS "${USCXML_TRANSFORM_BIN} -tc -i ${TESTFILE} -o ${OUTDIR}/${TEST_FILE_NAME}.machine.c")
+execute_process(COMMAND time -p ${USCXML_TRANSFORM_BIN} -tc -i ${TESTFILE} -o ${OUTDIR}/${TEST_FILE_NAME}.machine.c RESULT_VARIABLE CMD_RESULT)
+if(CMD_RESULT)
+ message(FATAL_ERROR "Error running ${USCXML_TRANSFORM_BIN}: ${CMD_RESULT}")
+endif()
+message(STATUS "time for transforming to c machine")
+
+set(COMPILE_CMD
+"-o" "${OUTDIR}/${TEST_FILE_NAME}"
+"-L${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+"-luscxml64"
+"-include" "${OUTDIR}/${TEST_FILE_NAME}.machine.c"
+"-I${PROJECT_SOURCE_DIR}/contrib/prebuilt/${USCXML_PLATFORM_ID}/include"
+"-I${PROJECT_SOURCE_DIR}/contrib/prebuilt/${USCXML_PLATFORM_ID}/include/arabica"
+"-I${PROJECT_SOURCE_DIR}/contrib/prebuilt/include"
+"-I${CMAKE_BINARY_DIR}"
+"-I${PROJECT_SOURCE_DIR}/src"
+"-Wl,-rpath,${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+"-DAUTOINCLUDE_TEST=ON"
+"${SCAFFOLDING_FOR_GENERATED_C}")
+
+message(STATUS "${GPP_BIN} ${COMPILE_CMD}")
+execute_process(
+ COMMAND time -p ${GPP_BIN} ${COMPILE_CMD}
+ WORKING_DIRECTORY ${OUTDIR} RESULT_VARIABLE CMD_RESULT)
+if(CMD_RESULT)
+ message(FATAL_ERROR "Error running g++ ${GPP_BIN}: ${CMD_RESULT}")
+endif()
+message(STATUS "time for transforming to binary")
+
+message(STATUS "${OUTDIR}/${TEST_FILE_NAME}")
+execute_process(
+ COMMAND time -p ${OUTDIR}/${TEST_FILE_NAME}
+ WORKING_DIRECTORY ${OUTDIR}
+ RESULT_VARIABLE CMD_RESULT)
+if(CMD_RESULT)
+ message(FATAL_ERROR "Error running generated c test: ${CMD_RESULT}")
+endif()
+message(STATUS "time for execution")
+
+# message(STATUS "${TEST_OUT}")
+# file(WRITE ${OUTDIR}/${TEST_FILE_NAME}.pml.out ${TEST_OUT}) \ No newline at end of file