summaryrefslogtreecommitdiffstats
path: root/src/uscxml/transform/FSMToPromela.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/uscxml/transform/FSMToPromela.cpp')
-rw-r--r--src/uscxml/transform/FSMToPromela.cpp708
1 files changed, 680 insertions, 28 deletions
diff --git a/src/uscxml/transform/FSMToPromela.cpp b/src/uscxml/transform/FSMToPromela.cpp
index 0c61900..dc3b581 100644
--- a/src/uscxml/transform/FSMToPromela.cpp
+++ b/src/uscxml/transform/FSMToPromela.cpp
@@ -19,10 +19,13 @@
#include "uscxml/transform/ChartToFSM.h"
#include "uscxml/transform/FSMToPromela.h"
+#include "uscxml/plugins/datamodel/promela/PromelaParser.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 {
@@ -30,47 +33,696 @@ using namespace Arabica::DOM;
using namespace Arabica::XPath;
void FSMToPromela::writeProgram(std::ostream& stream,
- const Arabica::DOM::Document<std::string>& doc,
- const std::map<std::string, std::string>& namespaceInfo) {
+ const Interpreter& interpreter) {
+ FSMToPromela promelaWriter;
+ interpreter.getImpl()->copyTo(&promelaWriter);
+ promelaWriter.writeProgram(stream);
+}
+
+FSMToPromela::FSMToPromela() : _eventTrie(".") {
+}
+
+void FSMToPromela::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 FSMToPromela::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(_globalStates[i], "id") << "\"" << std::endl;
+ }
+
+}
+
+Arabica::XPath::NodeSet<std::string> FSMToPromela::getTransientContent(const Arabica::DOM::Node<std::string>& state) {
+ Arabica::XPath::NodeSet<std::string> content;
+ Arabica::DOM::Node<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(transitions[0], "target")];
+ }
+
+ return content;
+}
+
+Node<std::string> FSMToPromela::getUltimateTarget(const Arabica::DOM::Node<std::string>& transition) {
+ Arabica::DOM::Node<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(transitions[0], "target")];
+ }
+}
+
+void FSMToPromela::writeInlineComment(std::ostream& stream, const Arabica::DOM::Node<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 FSMToPromela::writeExecutableContent(std::ostream& stream, const Arabica::DOM::Node<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, execContent[i], indent);
+ }
+ } else {
+ Arabica::DOM::Node<std::string> child = node.getFirstChild();
+ while(child) {
+ writeExecutableContent(stream, child, indent);
+ child = child.getNextSibling();
+ }
+ }
+ } else if(TAGNAME(node) == "transition") {
+ stream << "t" << _transitions[node] << ":" << std::endl;
+
+ // check for special promela labels
+ PromelaInlines promInls = getInlinePromela(getTransientContent(_states[ATTR(node, "target")]), true);
+
+ if (promInls.hasAcceptLabel)
+ stream << padding << "acceptLabelT" << _transitions[node] << ":" << std::endl;
+ if (promInls.hasEndLabel)
+ stream << padding << "endLabelT" << _transitions[node] << ":" << std::endl;
+ if (promInls.hasProgressLabel)
+ stream << padding << "progressLabelT" << _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(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, 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, 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) == "invoke") {
+ stream << padding << "run " << ATTR(node, "invokeid") << "EventSource();" << std::endl;
+ } 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);
+ }
+
+}
+
+PromelaInlines FSMToPromela::getInlinePromela(const Arabica::XPath::NodeSet<std::string>& elements, bool recurse) {
+ PromelaInlines prom;
+
+ if (elements.size() == 0)
+ return prom;
+
+ Arabica::XPath::NodeSet<std::string> comments = filterChildType(Node_base::COMMENT_NODE, elements, recurse);
+ for (int i = 0; i < comments.size(); i++) {
+ std::stringstream ssLine(comments[i].getNodeValue());
+ std::string line;
+
+ bool isInPromelaCode = false;
+ PromelaInline promInl;
+
+ while(std::getline(ssLine, line)) {
+ std::string trimLine = boost::trim_copy(line);
+ if (line.length() == 0)
+ continue;
+ if (false) {
+ } else if (boost::starts_with(trimLine, "promela-progress")) {
+ prom.hasProgressLabel = true;
+ if (isInPromelaCode) {
+ prom.inlines.push_back(promInl);
+ isInPromelaCode = false;
+ }
+ promInl.type = PromelaInline::PROMELA_PROGRESS_LABEL;
+ promInl.content = line;
+ prom.inlines.push_back(promInl);
+ } else if (boost::starts_with(trimLine, "promela-accept")) {
+ prom.hasAcceptLabel = true;
+ if (isInPromelaCode) {
+ prom.inlines.push_back(promInl);
+ isInPromelaCode = false;
+ }
+ promInl.type = PromelaInline::PROMELA_ACCEPT_LABEL;
+ promInl.content = line;
+ prom.inlines.push_back(promInl);
+ } else if (boost::starts_with(trimLine, "promela-end")) {
+ prom.hasEndLabel = true;
+ if (isInPromelaCode) {
+ prom.inlines.push_back(promInl);
+ isInPromelaCode = false;
+ }
+ promInl.type = PromelaInline::PROMELA_END_LABEL;
+ promInl.content = line;
+ prom.inlines.push_back(promInl);
+ } else if (boost::starts_with(trimLine, "promela-inline")) {
+ prom.hasCode = true;
+ isInPromelaCode = true;
+ promInl.type = PromelaInline::PROMELA_CODE;
+ } else if (isInPromelaCode) {
+ promInl.content += line;
+ }
+ }
+ // inline code ends with comment
+ if (isInPromelaCode) {
+ prom.inlines.push_back(promInl);
+ }
+ }
+ return prom;
+}
+
+void FSMToPromela::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(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(ifNode, "cond") << ") -> {" << std::endl;
+
+ Arabica::DOM::Node<std::string> child;
+ if (TAGNAME(ifNode) == "if") {
+ child = ifNode.getFirstChild();
+ } else {
+ child = ifNode.getNextSibling();
+ }
+ while(child) {
+ if (child.getNodeType() == Node_base::ELEMENT_NODE) {
+ if (TAGNAME(child) == "elseif" || TAGNAME(child) == "else")
+ break;
+ }
+ writeExecutableContent(stream, 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, 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 FSMToPromela::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 FSMToPromela::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 << std::endl;
+ stream << "// event sources" << std::endl;
+ if (_globalEventSource)
+ stream << "bool globalEventSourceDone;" << std::endl;
+
+ std::map<std::string, PromelaEventSource>::iterator invIter = _invokers.begin();
+ while(invIter != _invokers.end()) {
+ stream << "bool " << invIter->first << "EventSourceDone;" << std::endl;
+ stream << std::endl;
+ invIter++;
+ }
+
+}
+
+void FSMToPromela::writeEventSources(std::ostream& stream) {
+ if (_globalEventSource)
+ writeEventSource(stream, "global", _globalEventSource);
+
+ std::map<std::string, PromelaEventSource>::iterator invIter = _invokers.begin();
+ while(invIter != _invokers.end()) {
+ writeEventSource(stream, invIter->first, invIter->second);
+ invIter++;
+ }
+
+}
+
+void FSMToPromela::writeEventSource(std::ostream& stream, const std::string& name, const PromelaEventSource& source) {
+ stream << "proctype " << name << "EventSource() {" << std::endl;
+ stream << " " << name << "EventSourceDone = 0;" << std::endl;
+ stream << " " << name << "NewEvent:" << std::endl;
+ stream << " " << "if" << std::endl;
+ stream << " " << ":: " << name << "EventSourceDone -> skip;" << std::endl;
+ stream << " " << ":: else { " << std::endl;
+ stream << " " << " if" << std::endl;
+ stream << " " << " :: 1 -> " << "goto " << name << "NewEvent;" << std::endl;
+
+ std::list<std::list<std::string> >::const_iterator seqIter = source.sequences.begin();
+ while(seqIter != source.sequences.end()) {
+ stream << " " << ":: ";
+ std::list<std::string>::const_iterator evIter = seqIter->begin();
+ while(evIter != seqIter->end()) {
+ TrieNode* node = _eventTrie.getNodeWithPrefix(*evIter);
+ stream << "eQ!" << node->identifier << "; ";
+ evIter++;
+ }
+ stream << "goto " << name << "NewEvent;" << std::endl;
+ seqIter++;
+ }
+ stream << " " << " fi" << std::endl;
+ stream << " " << "}" << std::endl;
+ stream << " " << "fi" << std::endl;
+ stream << "}" << std::endl;
+}
+
+
+void FSMToPromela::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, 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, transitions[j], 1);
+ }
+ }
+
+ stream << std::endl;
+ stream << "nextStep: /* 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;
+
+ // stop all event sources
+ if (_globalEventSource)
+ stream << " globalEventSourceDone = 1;" << std::endl;
+
+ std::map<std::string, PromelaEventSource>::iterator invIter = _invokers.begin();
+ while(invIter != _invokers.end()) {
+ stream << " " << invIter->first << "EventSourceDone = 1;" << std::endl;
+ invIter++;
+ }
+
+
+ stream << "}" << std::endl;
+}
+
+void FSMToPromela::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;
+ }
}
-FSMToPromela::FSMToPromela(const Arabica::DOM::Document<std::string>& doc,
- const std::map<std::string, std::string>& namespaceInfo) {
+void FSMToPromela::writeDispatchingBlock(std::ostream& stream, const Arabica::XPath::NodeSet<std::string>& transChain, int indent) {
+ if (transChain.size() == 0)
+ return;
- std::map<std::string, std::string>::const_iterator nsIter = namespaceInfo.begin();
- while(nsIter != namespaceInfo.end()) {
- std::string uri = nsIter->first;
- std::string prefix = nsIter->second;
- if (iequals(uri, "http://www.w3.org/2005/07/scxml")) {
- _nsURL = uri;
- if (prefix.size() == 0) {
- _xpathPrefix = "scxml:";
- _nsContext.addNamespaceDeclaration(uri, "scxml");
- _nsToPrefix[uri] = "scxml";
- } else {
- _xpathPrefix = prefix + ":";
- _xmlNSPrefix = _xpathPrefix;
- _nsContext.addNamespaceDeclaration(uri, prefix);
- _nsToPrefix[uri] = prefix;
+ 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(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(currTrans, "cond") ? ATTR(currTrans, "cond") : "1");
+ stream << ") -> goto t" << _transitions[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 FSMToPromela::writeMain(std::ostream& stream) {
+ stream << std::endl;
+ stream << "init {" << std::endl;
+ if (_globalEventSource)
+ stream << " run globalEventSource();" << std::endl;
+ stream << " run step();" << std::endl;
+ stream << "}" << std::endl;
+
+}
+
+void FSMToPromela::initNodes() {
+ // get all states
+ NodeSet<std::string> states = filterChildElements(_nsInfo.xmlNSPrefix + "state", _scxml);
+ for (int i = 0; i < states.size(); i++) {
+ _states[ATTR(states[i], "id")] = states[i];
+ if (HAS_ATTR(states[i], "transient") && DOMUtils::attributeIsTrue(ATTR(states[i], "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(internalEventNames[i], "event")) {
+ std::string eventNames = ATTR(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);
+ }
+ }
+ }
+
+ // external event names from comments
+ NodeSet<std::string> promelaEventSourceComments;
+ NodeSet<std::string> invokers = _xpath.evaluate("//" + _nsInfo.xpathPrefix + "invoke", _scxml).asNodeSet();
+ promelaEventSourceComments.push_back(filterChildType(Node_base::COMMENT_NODE, invokers, true)); // comments in invoke elements
+ promelaEventSourceComments.push_back(filterChildType(Node_base::COMMENT_NODE, _scxml, false)); // comments in scxml element
+
+ for (int i = 0; i < promelaEventSourceComments.size(); i++) {
+ std::string comment = promelaEventSourceComments[i].getNodeValue();
+ boost::trim(comment);
+ if (!boost::starts_with(comment, "promela-event-source:"))
+ continue;
+ PromelaEventSource* eventSource = NULL;
+ if (false) {
+ } else if (TAGNAME(promelaEventSourceComments[i].getParentNode()) == "scxml") {
+ eventSource = &_globalEventSource;
+ } else if (TAGNAME(promelaEventSourceComments[i].getParentNode()) == "invoke") {
+ if (!HAS_ATTR(promelaEventSourceComments[i].getParentNode(), "invokeid")) {
+ Element<std::string> invoker = Element<std::string>(promelaEventSourceComments[i].getParentNode());
+ invoker.setAttribute("invokeid", "invoker" + toStr(_invokers.size()));
}
+ std::string invokeId = ATTR(promelaEventSourceComments[i].getParentNode(), "invokeid");
+ eventSource = &_invokers[invokeId];
} else {
- _nsContext.addNamespaceDeclaration(uri, prefix);
- _nsToPrefix[uri] = prefix;
+ assert(false);
+ }
+ if (!eventSource)
+ continue;
+ std::stringstream ssLine(comment);
+ std::string line;
+ std::getline(ssLine, line); // consume first line
+ while(std::getline(ssLine, line)) {
+ if (line.length() == 0)
+ continue;
+ std::list<std::string> currSeq;
+
+ std::stringstream ssToken(line);
+ std::string token;
+ while(std::getline(ssToken, token, ' ')) {
+ if (token.length() == 0)
+ continue;
+ currSeq.push_back(token);
+ _eventTrie.addWord(token);
+ }
+ eventSource->sequences.push_back(currSeq);
}
- nsIter++;
}
- _xpath.setNamespaceContext(_nsContext);
+
+ // enumerate transitions
+ NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _scxml, true);
+ int index = 0;
+ for (int i = 0; i < transitions.size(); i++) {
+ _transitions[transitions[i]] = index++;
+ }
+}
- _document = doc;
- NodeList<std::string> scxmls = _document.getElementsByTagNameNS(_nsURL, "scxml");
- if (scxmls.getLength() > 0) {
- _scxml = (Arabica::DOM::Element<std::string>)scxmls.item(0);
+void PromelaEventSource::dump() {
+ std::list<std::list<std::string> >::iterator outerIter = sequences.begin();
+ while(outerIter != sequences.end()) {
+ std::list<std::string>::iterator innerIter = outerIter->begin();
+ while(innerIter != outerIter->end()) {
+ std::cout << *innerIter << " ";
+ innerIter++;
+ }
+ std::cout << std::endl;
+ outerIter++;
}
}
void FSMToPromela::writeProgram(std::ostream& stream) {
- stream << "foo";
+
+ 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;
+ writeEventSources(stream);
+ stream << std::endl;
+ writeFSM(stream);
+ stream << std::endl;
+ writeMain(stream);
+ stream << std::endl;
+
}
} \ No newline at end of file