diff options
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 |