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.cpp1115
1 files changed, 979 insertions, 136 deletions
diff --git a/src/uscxml/transform/FSMToPromela.cpp b/src/uscxml/transform/FSMToPromela.cpp
index 238e40f..8c2836f 100644
--- a/src/uscxml/transform/FSMToPromela.cpp
+++ b/src/uscxml/transform/FSMToPromela.cpp
@@ -19,7 +19,10 @@
#include "uscxml/transform/ChartToFSM.h"
#include "uscxml/transform/FSMToPromela.h"
+#include "uscxml/transform/FlatStateIdentifier.h"
#include "uscxml/plugins/datamodel/promela/PromelaParser.h"
+#include "uscxml/plugins/datamodel/promela/parser/promela.tab.hpp"
+
#include <DOM/io/Stream.hpp>
#include <iostream>
#include "uscxml/UUID.h"
@@ -27,6 +30,15 @@
#include <boost/algorithm/string.hpp>
#include <glog/logging.h>
+#define MAX_MACRO_CHARS 64
+#define MIN_COMMENT_PADDING 60
+
+#define BIT_WIDTH(number) (number > 1 ? (int)ceil(log((double)number) / log((double)2.0)) : 1)
+
+#define INDENT_MIN(stream, start, cols) \
+for (int indentIndex = start; indentIndex < cols; indentIndex++) \
+ stream << " ";
+
namespace uscxml {
using namespace Arabica::DOM;
@@ -39,7 +51,7 @@ void FSMToPromela::writeProgram(std::ostream& stream,
promelaWriter.writeProgram(stream);
}
-FSMToPromela::FSMToPromela() : _eventTrie(".") {
+FSMToPromela::FSMToPromela() {
}
void PromelaEventSource::writeStartEventSources(std::ostream& stream, int indent) {
@@ -126,16 +138,18 @@ void PromelaEventSource::writeEventSource(std::ostream& stream) {
stream << " " << ":: " << sourceName << "EventSourceDone -> skip;" << std::endl;
stream << " " << ":: else { " << std::endl;
+ Trie& trie = analyzer->getTrie();
+
if (sourceIter->type == PromelaInline::PROMELA_EVENT_SOURCE_CUSTOM) {
std::string content = sourceIter->content;
boost::replace_all(content, "#REDO#", sourceName + "NewEvent");
boost::replace_all(content, "#DONE#", sourceName + "Done");
- std::list<TrieNode*> eventNames = trie->getChildsWithWords(trie->getNodeWithPrefix(""));
+ std::list<TrieNode*> eventNames = trie.getChildsWithWords(trie.getNodeWithPrefix(""));
std::list<TrieNode*>::iterator eventNameIter = eventNames.begin();
while(eventNameIter != eventNames.end()) {
- boost::replace_all(content, "#" + (*eventNameIter)->value + "#", "e" + toStr((*eventNameIter)->identifier));
+ boost::replace_all(content, "#" + (*eventNameIter)->value + "#", (*eventNameIter)->identifier);
eventNameIter++;
}
@@ -150,7 +164,7 @@ void PromelaEventSource::writeEventSource(std::ostream& stream) {
stream << " " << ":: ";
std::list<std::string>::const_iterator evIter = seqIter->begin();
while(evIter != seqIter->end()) {
- TrieNode* node = trie->getNodeWithPrefix(*evIter);
+ TrieNode* node = trie.getNodeWithPrefix(*evIter);
stream << "eQ!" << node->identifier << "; ";
evIter++;
}
@@ -173,54 +187,409 @@ void PromelaEventSource::writeEventSource(std::ostream& stream) {
PromelaEventSource::PromelaEventSource() {
type = PROMELA_EVENT_SOURCE_INVALID;
- trie = NULL;
+ analyzer = NULL;
}
PromelaEventSource::PromelaEventSource(const PromelaInlines& sources, const Arabica::DOM::Node<std::string>& parent) {
type = PROMELA_EVENT_SOURCE_INVALID;
- trie = NULL;
+ analyzer = NULL;
eventSources = sources;
container = parent;
}
+void PromelaCodeAnalyzer::addCode(const std::string& code) {
+ PromelaParser parser(code);
+
+ // find all strings
+ std::list<PromelaParserNode*> astNodes;
+ astNodes.push_back(parser.ast);
+
+ while(astNodes.size() > 0) {
+ PromelaParserNode* node = astNodes.front();
+ astNodes.pop_front();
+
+ switch (node->type) {
+ case PML_STRING: {
+ std::string unquoted = node->value;
+ if (boost::starts_with(unquoted, "'")) {
+ unquoted = unquoted.substr(1, unquoted.size() - 2);
+ }
+ addLiteral(unquoted);
+ break;
+ }
+ case PML_CMPND: {
+ std::string nameOfType;
+ std::list<PromelaParserNode*>::iterator opIter = node->operands.begin();
+ assert((*opIter)->type == PML_NAME);
+
+ PromelaTypedef* td = &_typeDefs;
+ std::string seperator;
+
+ while(opIter != node->operands.end()) {
+ switch ((*opIter)->type) {
+ case PML_NAME:
+ td = &td->types[(*opIter)->value];
+ nameOfType += seperator + (*opIter)->value;
+ if (nameOfType.compare("_x") == 0)
+ _usesPlatformVars = true;
+ seperator = "_";
+ td->name = nameOfType + "_t";
+ break;
+ case PML_VAR_ARRAY: {
+ PromelaParserNode* name = (*opIter)->operands.front();
+ PromelaParserNode* subscript = *(++(*opIter)->operands.begin());
+ td = &td->types[name->value];
+ nameOfType += seperator + name->value;
+ td->name = nameOfType + "_t";
+
+ if (isInteger(subscript->value.c_str(), 10)) {
+ td->arraySize = strTo<int>(subscript->value);
+ }
+ break;
+ }
+ default:
+ node->dump();
+ assert(false);
+ break;
+ }
+
+ if (nameOfType.compare("_x_states") == 0) {
+ _usesInPredicate = true;
+ }
+ if (nameOfType.compare("_event_type") == 0) {
+ addLiteral("internal");
+ addLiteral("external");
+ addLiteral("platform");
+ }
+ if (nameOfType.compare("_event_origintype") == 0) {
+ addLiteral("http://www.w3.org/TR/scxml/#SCXMLEventProcessor");
+ }
+ opIter++;
+ }
+ break;
+ }
+ case PML_NAME: {
+ }
+ default:
+ break;
+// node->dump();
+// assert(false);
+ }
+
+ astNodes.merge(node->operands);
+ }
+}
+
+void PromelaCodeAnalyzer::addEvent(const std::string& eventName) {
+ if (_events.find(eventName) != _events.end())
+ return;
+
+ addLiteral(eventName, _lastEventIndex);
+ assert(_strIndex.find(eventName) != _strIndex.end());
+
+ _eventTrie.addWord(eventName);
+ _events[eventName] = _strIndex[eventName];
+ _lastEventIndex++;
+}
+
+void PromelaCodeAnalyzer::addState(const std::string& stateName) {
+ if (_states.find(stateName) != _states.end())
+ return;
+
+ createMacroName(stateName);
+
+// addLiteral(stateName);
+// _states[stateName] = enumerateLiteral(stateName);
+ if (_origStateMap.find(stateName) == _origStateMap.end()) {
+ FlatStateIdentifier flatId(stateName);
+ _origStateMap[stateName] = flatId.getActive();
+ for (std::list<std::string>::iterator origIter = _origStateMap[stateName].begin(); origIter != _origStateMap[stateName].end(); origIter++) {
+ //addLiteral(*origIter); // add original state names as string literals
+ if (_origStateIndex.find(*origIter) == _origStateIndex.end()) {
+ _origStateIndex[*origIter] = _lastStateIndex++;
+ createMacroName(*origIter);
+ }
+ }
+ }
+}
+
+int PromelaCodeAnalyzer::arrayIndexForOrigState(const std::string& stateName) {
+ if (_origStateIndex.find(stateName) == _origStateIndex.end())
+ throw std::runtime_error("No original state " + stateName + " known");
+ return _origStateIndex[stateName];
+}
+
+void PromelaCodeAnalyzer::addLiteral(const std::string& literal, int forceIndex) {
+ if (boost::starts_with(literal, "'"))
+ throw std::runtime_error("Literal " + literal + " passed with quotes");
+
+ if (_strLiterals.find(literal) != _strLiterals.end())
+ return;
+
+ _strLiterals.insert(literal);
+ createMacroName(literal);
+ enumerateLiteral(literal, forceIndex);
+}
+
+int PromelaCodeAnalyzer::enumerateLiteral(const std::string& literal, int forceIndex) {
+ if (forceIndex >= 0) {
+ _strIndex[literal] = forceIndex;
+ return forceIndex;
+ }
+
+ if (_strIndex.find(literal) != _strIndex.end())
+ return _strIndex[literal];
+
+ _strIndex[literal] = ++_lastStrIndex;
+ return _lastStrIndex + 1;
+}
+
+std::string PromelaCodeAnalyzer::createMacroName(const std::string& literal) {
+ if (_strMacroNames.find(literal) != _strMacroNames.end())
+ return _strMacroNames[literal];
+
+ // find a suitable macro name for the strings
+ std::string macroName = literal; //literal.substr(1, literal.size() - 2);
+
+ // cannot start with digit
+ if (isInteger(macroName.substr(0,1).c_str(), 10))
+ macroName = "_" + macroName;
+
+ macroName = macroName.substr(0, MAX_MACRO_CHARS);
+ boost::to_upper(macroName);
+
+ std::string illegalChars = "#\\/:?\"<>| \n\t()[]{}',.-";
+ std::string tmp;
+ std::string::iterator it = macroName.begin();
+ while (it < macroName.end()) {
+ bool found = illegalChars.find(*it) != std::string::npos;
+ if(found) {
+ tmp += '_';
+ it++;
+ while(it < macroName.end() && illegalChars.find(*it) != std::string::npos) {
+ it++;
+ }
+ } else {
+ tmp += *it++;
+ }
+ }
+ macroName = tmp;
+
+ unsigned int index = 2;
+ while (_macroNameSet.find(macroName) != _macroNameSet.end()) {
+ std::string suffix = toStr(index);
+ if (macroName.size() > suffix.size()) {
+ macroName = macroName.substr(0, macroName.size() - suffix.size()) + suffix;
+ } else {
+ macroName = suffix;
+ }
+ index++;
+ }
+
+ _macroNameSet.insert(macroName);
+ _strMacroNames[literal] = macroName;
+ return macroName;
+}
+
+std::string PromelaCodeAnalyzer::macroForLiteral(const std::string& literal) {
+ if (boost::starts_with(literal, "'"))
+ throw std::runtime_error("Literal " + literal + " passed with quotes");
+
+ if (_strMacroNames.find(literal) == _strMacroNames.end())
+ throw std::runtime_error("No macro for literal " + literal + " known");
+ return _strMacroNames[literal];
+}
+
+int PromelaCodeAnalyzer::indexForLiteral(const std::string& literal) {
+ if (boost::starts_with(literal, "'"))
+ throw std::runtime_error("Literal " + literal + " passed with quotes");
+
+ if (_strIndex.find(literal) == _strIndex.end())
+ throw std::runtime_error("No index for literal " + literal + " known");
+ return _strIndex[literal];
+}
+
+std::string PromelaCodeAnalyzer::replaceLiterals(const std::string code) {
+ std::string replaced = code;
+ for (std::map<std::string, std::string>::const_iterator litIter = _strMacroNames.begin(); litIter != _strMacroNames.end(); litIter++) {
+ boost::replace_all(replaced, "'" + litIter->first + "'", litIter->second);
+ }
+ return replaced;
+}
+
+std::set<std::string> PromelaCodeAnalyzer::getEventsWithPrefix(const std::string& prefix) {
+ std::set<std::string> eventNames;
+ std::list<TrieNode*> trieNodes = _eventTrie.getWordsWithPrefix(prefix);
+
+ std::list<TrieNode*>::iterator trieIter = trieNodes.begin();
+ while(trieIter != trieNodes.end()) {
+ eventNames.insert((*trieIter)->value);
+ trieIter++;
+ }
+
+ return eventNames;
+}
+
+
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;
+ std::map<std::string, int> events = _analyzer.getEvents();
+ std::map<std::string, int>::iterator eventIter = events.begin();
+ stream << "/* event name identifiers */" << std::endl;
+ while(eventIter != events.end()) {
+ if (eventIter->first.length() > 0) {
+ stream << "#define " << _analyzer.macroForLiteral(eventIter->first) << " " << _analyzer.indexForLiteral(eventIter->first);
+ stream << " /* from \"" << eventIter->first << "\" */" << std::endl;
+ }
eventIter++;
}
}
void FSMToPromela::writeStates(std::ostream& stream) {
- stream << "// state name identifiers" << std::endl;
+ 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;
+ stream << " /* from \"" << ATTR_CAST(_globalStates[i], "id") << "\" */" << std::endl;
+ }
+}
+
+void FSMToPromela::writeStateMap(std::ostream& stream) {
+ stream << "/* macros for original state names */" << std::endl;
+ std::map<std::string, int> origStates = _analyzer.getOrigStates();
+ for (std::map<std::string, int>::iterator origIter = origStates.begin(); origIter != origStates.end(); origIter++) {
+ stream << "#define " << _analyzer.macroForLiteral(origIter->first) << " " << origIter->second;
+ stream << " /* from \"" << origIter->first << "\" */" << std::endl;
}
+// std::map<std::string, int> states = _analyzer.getStates();
+// size_t stateIndex = 0;
+// for (std::map<std::string, int>::iterator stateIter = states.begin(); stateIter != states.end(); stateIter++) {
+// stream << "_x"
+// std::list<std::string> origStates = _analyzer.getOrigState(stateIter->first);
+// size_t origIndex = 0;
+// for (std::list<std::string>::iterator origIter = origStates.begin(); origIter != origStates.end(); origIter++) {
+//
+// }
+// }
}
-Arabica::XPath::NodeSet<std::string> FSMToPromela::getTransientContent(const Arabica::DOM::Element<std::string>& state) {
+void FSMToPromela::writeTypeDefs(std::ostream& stream) {
+ stream << "/* typedefs */" << std::endl;
+ PromelaCodeAnalyzer::PromelaTypedef typeDefs = _analyzer.getTypes();
+ if (typeDefs.types.size() == 0)
+ return;
+
+ std::list<PromelaCodeAnalyzer::PromelaTypedef> individualDefs;
+ std::list<PromelaCodeAnalyzer::PromelaTypedef> currDefs;
+ currDefs.push_back(typeDefs);
+
+ while(currDefs.size() > 0) {
+ if (std::find(individualDefs.begin(), individualDefs.end(), currDefs.front()) == individualDefs.end()) {
+ individualDefs.push_back(currDefs.front());
+ for (std::map<std::string, PromelaCodeAnalyzer::PromelaTypedef>::iterator typeIter = currDefs.front().types.begin(); typeIter != currDefs.front().types.end(); typeIter++) {
+ currDefs.push_back(typeIter->second);
+ }
+ }
+ currDefs.pop_front();
+ }
+ individualDefs.pop_front();
+
+ for (std::list<PromelaCodeAnalyzer::PromelaTypedef>::reverse_iterator rIter = individualDefs.rbegin(); rIter != individualDefs.rend(); rIter++) {
+ PromelaCodeAnalyzer::PromelaTypedef currDef = *rIter;
+ if (currDef.types.size() == 0 || currDef.name.size() == 0)
+ continue;
+// if (currDef.name.compare("_x_t") == 0) {
+// stream << "typedef platform_t {" << std::endl;
+// if (_analyzer.usesInPredicate()) {
+// stream << " bool states[" << _analyzer.getOrigStates().size() << "];" << std::endl;
+// }
+// stream << "};" << std::endl;
+//
+// continue;
+// }
+ stream << "typedef " << currDef.name << " {" << std::endl;
+ if (currDef.name.compare("_event_t") == 0 && currDef.types.find("name") == currDef.types.end()) { // special treatment for _event
+ stream << " int name;" << std::endl;
+ }
+ for (std::map<std::string, PromelaCodeAnalyzer::PromelaTypedef>::iterator tIter = currDef.types.begin(); tIter != currDef.types.end(); tIter++) {
+ if (currDef.name.compare("_x_t") == 0 && tIter->first.compare("states") == 0) {
+ stream << " bool states[" << _analyzer.getOrigStates().size() << "];" << std::endl;
+ continue;
+ }
+ if (tIter->second.types.size() == 0) {
+ stream << " int " << tIter->first << ";" << std::endl; // not further nested
+ } else {
+ stream << " " << tIter->second.name << " " << tIter->first << ";" << std::endl;
+ }
+ }
+ stream << "};" << std::endl << std::endl;
+ }
+
+ stream << "/* typedef instances */" << std::endl;
+ PromelaCodeAnalyzer::PromelaTypedef allTypes = _analyzer.getTypes();
+ std::map<std::string, PromelaCodeAnalyzer::PromelaTypedef>::iterator typeIter = allTypes.types.begin();
+ while(typeIter != allTypes.types.end()) {
+ stream << typeIter->second.name << " " << typeIter->first << ";" << std::endl;
+ typeIter++;
+ }
+
+}
+
+Arabica::XPath::NodeSet<std::string> FSMToPromela::getTransientContent(const Arabica::DOM::Element<std::string>& state, const std::string& source) {
Arabica::XPath::NodeSet<std::string> content;
Arabica::DOM::Element<std::string> currState = state;
+ FlatStateIdentifier prevFlatId(source);
for (;;) {
- if (!HAS_ATTR(currState, "transient") || !DOMUtils::attributeIsTrue(ATTR(currState, "transient")))
+ if (_analyzer.usesInPredicate()) {
+ // insert state assignments into executable content
+
+ std::stringstream stateSetPromela;
+ stateSetPromela << "#promela-inline " << std::endl;
+ FlatStateIdentifier currFlatId(ATTR(currState, "id"));
+ stateSetPromela << " /* from " << prevFlatId.getFlatActive() << " to " << currFlatId.getFlatActive() << " */" << std::endl;
+
+ // add all that are missing from prevFlatId
+ std::map<std::string, int> allOrigStates = _analyzer.getOrigStates();
+ for (std::map<std::string, int>::iterator allOrigIter = allOrigStates.begin(); allOrigIter != allOrigStates.end(); allOrigIter++) {
+ if (std::find(currFlatId.getActive().begin(), currFlatId.getActive().end(), allOrigIter->first) != currFlatId.getActive().end() &&
+ std::find(prevFlatId.getActive().begin(), prevFlatId.getActive().end(), allOrigIter->first) == prevFlatId.getActive().end()) {
+ // active now but not previously
+ stateSetPromela << " _x.states[" << _analyzer.macroForLiteral(allOrigIter->first) << "] = true; " << std::endl;
+ } else if (std::find(currFlatId.getActive().begin(), currFlatId.getActive().end(), allOrigIter->first) == currFlatId.getActive().end() &&
+ std::find(prevFlatId.getActive().begin(), prevFlatId.getActive().end(), allOrigIter->first) != prevFlatId.getActive().end()) {
+ // previously active but not now
+ stateSetPromela << " _x.states[" << _analyzer.macroForLiteral(allOrigIter->first) << "] = false; " << std::endl;
+ }
+ }
+ Comment<std::string> comment = _document.createComment(stateSetPromela.str());
+ _document.importNode(comment, true);
+ currState.insertBefore(comment, currState.getFirstChild());
+ prevFlatId = currFlatId;
+ }
+
+ content.push_back(filterChildType(Node_base::COMMENT_NODE, currState));
+ if (_analyzer.usesInPredicate()) assert(content.size() > 0);
+
+ if (!HAS_ATTR(currState, "transient") || !DOMUtils::attributeIsTrue(ATTR(currState, "transient"))) {
+ // breaking here causes final state assignment to be written
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> FSMToPromela::getUltimateTarget(const Arabica::DOM::Element<std::string>& transition) {
+ if (!HAS_ATTR(transition, "target")) {
+ return transition.getParentNode();
+ }
+
Arabica::DOM::Element<std::string> currState = _states[ATTR_CAST(transition, "target")];
for (;;) {
@@ -237,7 +606,7 @@ void FSMToPromela::writeInlineComment(std::ostream& stream, const Arabica::DOM::
std::string comment = node.getNodeValue();
boost::trim(comment);
- if (!boost::starts_with(comment, "promela-inline:"))
+ if (!boost::starts_with(comment, "#promela-inline"))
return;
std::stringstream ssLine(comment);
@@ -250,18 +619,25 @@ void FSMToPromela::writeInlineComment(std::ostream& stream, const Arabica::DOM::
}
}
-void FSMToPromela::writeExecutableContent(std::ostream& stream, const Arabica::DOM::Element<std::string>& node, int indent) {
+void FSMToPromela::writeExecutableContent(std::ostream& stream, const Arabica::DOM::Node<std::string>& node, int indent) {
+
+// std::cout << std::endl << node << std::endl;
+ if (!node)
+ return;
std::string padding;
for (int i = 0; i < indent; i++) {
padding += " ";
}
+// std::cerr << node << std::endl;
+
if (node.getNodeType() == Node_base::COMMENT_NODE) {
+ // we cannot have labels in an atomic block, just process inline promela
std::string comment = node.getNodeValue();
boost::trim(comment);
std::stringstream inlinePromela;
- if (!boost::starts_with(comment, "promela-inline:"))
+ if (!boost::starts_with(comment, "#promela-inline"))
return;
std::stringstream ssLine(comment);
std::string line;
@@ -278,81 +654,166 @@ void FSMToPromela::writeExecutableContent(std::ostream& stream, const Arabica::D
if (node.getNodeType() != Node_base::ELEMENT_NODE)
return;
+ Arabica::DOM::Element<std::string> nodeElem = Arabica::DOM::Element<std::string>(node);
+
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;
+// } else if(TAGNAME(nodeElem) == "state") {
+// if (HAS_ATTR(nodeElem, "transient") && DOMUtils::attributeIsTrue(ATTR(nodeElem, "transient"))) {
+// Arabica::XPath::NodeSet<std::string> execContent = getTransientContent(nodeElem);
+// for (int i = 0; i < execContent.size(); i++) {
+// writeExecutableContent(stream, execContent[i], indent);
+// }
+// }
+ } else if(TAGNAME(nodeElem) == "transition") {
+ stream << "t" << _transitions[nodeElem] << ":";
+
+ int number = _transitions[nodeElem];
+ int digits = 0;
+ do {
+ number /= 10;
+ digits++;
+ } while (number != 0);
+
+ INDENT_MIN(stream, 2 + digits, MIN_COMMENT_PADDING);
+
+ Node<std::string> source = node.getParentNode();
+ stream << " /* from state " << ATTR_CAST(source, "id") << " */" << std::endl;
+
+ // gather all executable content
+ NodeSet<std::string> execContent = getTransientContent(_states[ATTR(nodeElem, "target")], ATTR_CAST(source, "id"));
// check for special promela labels
- PromelaInlines promInls = getInlinePromela(getTransientContent(_states[ATTR(node, "target")]), true);
-
- if (promInls.acceptLabels > 0)
- stream << padding << "acceptLabelT" << _transitions[node] << ":" << std::endl;
- if (promInls.endLabels > 0)
- stream << padding << "endLabelT" << _transitions[node] << ":" << std::endl;
- if (promInls.progressLabels > 0)
- stream << padding << "progressLabelT" << _transitions[node] << ":" << std::endl;
+ if (HAS_ATTR(nodeElem, "target")) {
+ PromelaInlines promInls = getInlinePromela(execContent, true);
+
+ if (promInls.acceptLabels > 0)
+ stream << padding << "acceptLabelT" << _transitions[nodeElem] << ":" << std::endl;
+ if (promInls.endLabels > 0)
+ stream << padding << "endLabelT" << _transitions[nodeElem] << ":" << std::endl;
+ if (promInls.progressLabels > 0)
+ stream << padding << "progressLabelT" << _transitions[nodeElem] << ":" << std::endl;
+ }
stream << padding << "atomic {" << std::endl;
- writeExecutableContent(stream, _states[ATTR(node, "target")], indent+1);
+// writeExecutableContent(stream, _states[ATTR(nodeElem, "target")], indent+1);
+ for (int i = 0; i < execContent.size(); i++) {
+ writeExecutableContent(stream, execContent[i], indent+1);
+ }
stream << padding << " skip;" << std::endl;
- Node<std::string> newState = getUltimateTarget(node);
+ Node<std::string> newState = getUltimateTarget(nodeElem);
for (int i = 0; i < _globalStates.size(); i++) {
if (newState != _globalStates[i])
continue;
- stream << padding << " s = s" << i << ";" << std::endl;
+
+ std::string stateId = ATTR_CAST(_globalStates[i], "id");
+
+ stream << padding << " s = s" << i << ";";
+
+ int number = i;
+ int digits = 0;
+ do {
+ number /= 10;
+ digits++;
+ } while (number != 0);
+
+ INDENT_MIN(stream, 10 + digits, MIN_COMMENT_PADDING);
+
+ stream << " /* to state " << stateId << " */" << std::endl;
+
+// if (_analyzer.usesInPredicate()) {
+// FlatStateIdentifier flatId(stateId);
+// std::map<std::string, int> allOrigStates = _analyzer.getOrigStates();
+// for (std::map<std::string, int>::iterator allOrigIter = allOrigStates.begin(); allOrigIter != allOrigStates.end(); allOrigIter++) {
+// stream << padding << " _x.states[" << _analyzer.macroForLiteral(allOrigIter->first) << "] = ";
+// if (std::find(flatId.getActive().begin(), flatId.getActive().end(), allOrigIter->first) != flatId.getActive().end()) {
+// stream << "true;" << std::endl;
+// } else {
+// stream << "false;" << std::endl;
+// }
+// }
+// }
+
}
stream << padding << "}" << std::endl;
if (isFinal(Element<std::string>(newState))) {
- stream << padding << "goto terminate;" << std::endl;
+ stream << padding << "goto terminate;";
+ INDENT_MIN(stream, padding.length() + 14, MIN_COMMENT_PADDING);
+ stream << "/* final state */" << std::endl;
+ } else if (!HAS_ATTR_CAST(node, "event")) {
+ stream << padding << "goto nextTransition;";
+ INDENT_MIN(stream, padding.length() + 19, MIN_COMMENT_PADDING);
+ stream << "/* spontaneous transition, check for more transitions */" << std::endl;
} else {
- stream << padding << "goto nextStep;" << std::endl;
+ stream << padding << "eventLess = true;" << std::endl;
+ stream << padding << "goto nextTransition;";
+ INDENT_MIN(stream, padding.length() + 21, MIN_COMMENT_PADDING);
+ stream << "/* ordinary transition, check for spontaneous transitions */" << std::endl;
}
- } else if(TAGNAME(node) == "onentry" || TAGNAME(node) == "onexit") {
+ } else if(TAGNAME(nodeElem) == "onentry" || TAGNAME(nodeElem) == "onexit") {
Arabica::DOM::Node<std::string> child = node.getFirstChild();
while(child) {
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(child), indent);
+// std::cerr << node << std::endl;
+ if (child.getNodeType() == Node_base::TEXT_NODE) {
+ if (boost::trim_copy(child.getNodeValue()).length() > 0)
+ stream << beautifyIndentation(_analyzer.replaceLiterals(child.getNodeValue()), indent) << std::endl;
+ }
+ if (child.getNodeType() == Node_base::ELEMENT_NODE) {
+ writeExecutableContent(stream, child, indent);
+ }
child = child.getNextSibling();
}
- } else if(TAGNAME(node) == "script") {
+ } else if(TAGNAME(nodeElem) == "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;
+ stream << _analyzer.replaceLiterals(beautifyIndentation(scriptText[i].getNodeValue(), indent)) << std::endl;
}
- } else if(TAGNAME(node) == "log") {
- // ignore
+ } else if(TAGNAME(nodeElem) == "log") {
+ std::string label = (HAS_ATTR(nodeElem, "label") ? ATTR(nodeElem, "label") : "");
+ std::string expr = (HAS_ATTR(nodeElem, "expr") ? ATTR(nodeElem, "expr") : "");
+ std::string trimmedExpr = boost::trim_copy(expr);
+ bool isStringLiteral = (boost::starts_with(trimmedExpr, "\"") || boost::starts_with(trimmedExpr, "'"));
+
+ std::string formatString;
+ std::string varString;
+ std::string seperator;
- } 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;
+ if (label.size() > 0) {
+ formatString += label + ": ";
+ }
+
+ if (isStringLiteral) {
+ formatString += expr;
+ } else {
+ formatString += "%d";
+ varString += seperator + expr;
+ }
+
+ if (varString.length() > 0) {
+ stream << padding << "printf(\"" + formatString + "\", " + varString + ");" << std::endl;
+ } else {
+ stream << padding << "printf(\"" + formatString + "\");" << std::endl;
+ }
+
+ } else if(TAGNAME(nodeElem) == "foreach") {
+ stream << padding << "for (" << (HAS_ATTR(nodeElem, "index") ? ATTR(nodeElem, "index") : "_index") << " in " << ATTR(nodeElem, "array") << ") {" << std::endl;
+ if (HAS_ATTR(nodeElem, "item")) {
+ stream << padding << " " << ATTR(nodeElem, "item") << " = " << ATTR(nodeElem, "array") << "[" << (HAS_ATTR(nodeElem, "index") ? ATTR(nodeElem, "index") : "_index") << "];" << std::endl;
+ }
Arabica::DOM::Node<std::string> child = node.getFirstChild();
while(child) {
- writeExecutableContent(stream, Arabica::DOM::Element<std::string>(child), indent + 1);
+ writeExecutableContent(stream, child, indent + 1);
child = child.getNextSibling();
}
- if (HAS_ATTR(node, "index"))
- stream << padding << " " << ATTR(node, "index") << "++;" << std::endl;
+ if (HAS_ATTR(nodeElem, "index"))
+ stream << padding << " " << ATTR(nodeElem, "index") << "++;" << std::endl;
stream << padding << "}" << std::endl;
- } else if(TAGNAME(node) == "if") {
+ } else if(TAGNAME(nodeElem) == "if") {
NodeSet<std::string> condChain;
condChain.push_back(node);
condChain.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "elseif", node));
@@ -360,22 +821,102 @@ void FSMToPromela::writeExecutableContent(std::ostream& stream, const Arabica::D
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")) {
+ } else if(TAGNAME(nodeElem) == "assign") {
+ if (HAS_ATTR(nodeElem, "location")) {
+ stream << padding << ATTR(nodeElem, "location") << " = ";
+ }
+ if (HAS_ATTR(nodeElem, "expr")) {
+ stream << _analyzer.replaceLiterals(ATTR(nodeElem, "expr")) << ";" << std::endl;
+ } else {
+ NodeSet<std::string> assignTexts = filterChildType(Node_base::TEXT_NODE, nodeElem, true);
+ if (assignTexts.size() > 0) {
+ stream << _analyzer.replaceLiterals(boost::trim_copy(assignTexts[0].getNodeValue())) << ";" << std::endl;
+ }
+ }
+ } else if(TAGNAME(nodeElem) == "send" || TAGNAME(nodeElem) == "raise") {
+ std::string targetQueue;
+ if (TAGNAME(nodeElem) == "raise") {
+ targetQueue = "iQ!";
+ } else if (!HAS_ATTR(nodeElem, "target")) {
+ targetQueue = "tmpQ!";
+ } else if (ATTR(nodeElem, "target").compare("#_internal") == 0) {
+ targetQueue = "iQ!";
+ }
+ if (targetQueue.length() > 0) {
// this is for our external queue
- TrieNode* trieNode = _eventTrie.getNodeWithPrefix(ATTR(node, "event"));
- stream << padding << "tmpQ!e" << trieNode->identifier << ";" << std::endl;
+ std::string event;
+
+ if (HAS_ATTR(nodeElem, "event")) {
+ event = _analyzer.macroForLiteral(ATTR(nodeElem, "event"));
+ } else if (HAS_ATTR(nodeElem, "eventexpr")) {
+ event = ATTR(nodeElem, "eventexpr");
+ }
+ if (_analyzer.usesComplexEventStruct()) {
+ stream << padding << "{" << std::endl;
+ stream << padding << " _event_t tmpEvent;" << std::endl;
+ stream << padding << " tmpEvent.name = " << event << ";" << std::endl;
+
+ if (HAS_ATTR(nodeElem, "idlocation")) {
+ stream << padding << " /* idlocation */" << std::endl;
+ stream << padding << " _lastSendId = _lastSendId + 1;" << std::endl;
+ stream << padding << " " << ATTR(nodeElem, "idlocation") << " = _lastSendId;" << std::endl;
+ stream << padding << " tmpEvent.sendid = _lastSendId;" << std::endl;
+ stream << padding << " if" << std::endl;
+ stream << padding << " :: _lastSendId == 2147483647 -> _lastSendId = 0;" << std::endl;
+ stream << padding << " :: timeout -> skip;" << std::endl;
+ stream << padding << " fi;" << std::endl;
+ } else if (HAS_ATTR(nodeElem, "id")) {
+ stream << padding << " tmpEvent.sendid = " << _analyzer.macroForLiteral(ATTR(nodeElem, "id")) << ";" << std::endl;
+ }
+
+ if (_analyzer.usesEventField("origintype") && targetQueue.compare("iQ!") != 0) {
+ stream << padding << " tmpEvent.origintype = " << _analyzer.macroForLiteral("http://www.w3.org/TR/scxml/#SCXMLEventProcessor") << ";" << std::endl;
+ }
+
+ if (_analyzer.usesEventField("type")) {
+ std::string eventType = (targetQueue.compare("iQ!") == 0 ? _analyzer.macroForLiteral("internal") : _analyzer.macroForLiteral("external"));
+ stream << padding << " tmpEvent.type = " << eventType << ";" << std::endl;
+ }
+
+ NodeSet<std::string> sendParams = filterChildElements(_nsInfo.xmlNSPrefix + "param", nodeElem);
+ NodeSet<std::string> sendContents = filterChildElements(_nsInfo.xmlNSPrefix + "content", nodeElem);
+ std::string sendNameList = ATTR(nodeElem, "namelist");
+ if (sendParams.size() > 0) {
+ for (int i = 0; i < sendParams.size(); i++) {
+ Element<std::string> paramElem = Element<std::string>(sendParams[i]);
+ stream << padding << " tmpEvent.data." << ATTR(paramElem, "name") << " = " << ATTR(paramElem, "expr") << ";" << std::endl;
+ }
+ }
+ if (sendNameList.size() > 0) {
+ std::list<std::string> nameListIds = tokenizeIdRefs(sendNameList);
+ std::list<std::string>::iterator nameIter = nameListIds.begin();
+ while(nameIter != nameListIds.end()) {
+ stream << padding << " tmpEvent.data." << *nameIter << " = " << *nameIter << ";" << std::endl;
+ nameIter++;
+ }
+ }
+
+ if (sendParams.size() == 0 && sendNameList.size() == 0 && sendContents.size() > 0) {
+ Element<std::string> contentElem = Element<std::string>(sendContents[0]);
+ if (contentElem.hasChildNodes() && contentElem.getFirstChild().getNodeType() == Node_base::TEXT_NODE) {
+ stream << padding << " tmpEvent.data = " << spaceNormalize(contentElem.getFirstChild().getNodeValue()) << ";" << std::endl;
+ } else if (HAS_ATTR(contentElem, "expr")) {
+ stream << padding << " tmpEvent.data = " << _analyzer.replaceLiterals(ATTR(contentElem, "expr")) << ";" << std::endl;
+ }
+ }
+ stream << padding << " " << targetQueue << "tmpEvent;" << std::endl;
+ stream << padding << "}" << std::endl;
+ } else {
+ stream << padding << targetQueue << event << ";" << std::endl;
+ }
}
- } else if(TAGNAME(node) == "invoke") {
- _invokers[ATTR(node, "invokeid")].writeStartEventSources(stream, indent);
- } else if(TAGNAME(node) == "uninvoke") {
- stream << padding << ATTR(node, "invokeid") << "EventSourceDone" << "= 1;" << std::endl;
+ } else if(TAGNAME(nodeElem) == "invoke") {
+ _invokers[ATTR(nodeElem, "invokeid")].writeStartEventSources(stream, indent);
+ } else if(TAGNAME(nodeElem) == "uninvoke") {
+ stream << padding << ATTR(nodeElem, "invokeid") << "EventSourceDone" << "= 1;" << std::endl;
} else {
- std::cerr << "'" << TAGNAME(node) << "'" << std::endl << node << std::endl;
+ std::cerr << "'" << TAGNAME(nodeElem) << "'" << std::endl << nodeElem << std::endl;
assert(false);
}
@@ -492,31 +1033,34 @@ void FSMToPromela::writeIfBlock(std::ostream& stream, const Arabica::XPath::Node
stream << padding << "if" << std::endl;
// we need to nest the elseifs to resolve promela if semantics
- stream << padding << ":: (" << ATTR(ifNode, "cond") << ") -> {" << std::endl;
+ stream << padding << ":: (" << _analyzer.replaceLiterals(ATTR(ifNode, "cond")) << ") -> {" << std::endl;
- Arabica::DOM::Element<std::string> child;
+ Arabica::DOM::Node<std::string> child;
if (TAGNAME(ifNode) == "if") {
- child = Arabica::DOM::Element<std::string>(ifNode.getFirstChild());
+ child = ifNode.getFirstChild();
} else {
- child = Arabica::DOM::Element<std::string>(ifNode.getNextSibling());
+ child = ifNode.getNextSibling();
}
while(child) {
if (child.getNodeType() == Node_base::ELEMENT_NODE) {
- if (TAGNAME(child) == "elseif" || TAGNAME(child) == "else")
+ Arabica::DOM::Element<std::string> childElem = Arabica::DOM::Element<std::string>(child);
+ if (TAGNAME(childElem) == "elseif" || TAGNAME_CAST(childElem) == "else")
break;
+ writeExecutableContent(stream, childElem, indent + 1);
}
- writeExecutableContent(stream, child, indent + 1);
- child = Arabica::DOM::Element<std::string>(child.getNextSibling());
+ child = child.getNextSibling();
}
stream << padding << "}" << std::endl;
stream << padding << ":: else -> ";
if (nextIsElse) {
- child = Arabica::DOM::Element<std::string>(condChain[1].getNextSibling());
+ child = condChain[1].getNextSibling();
stream << "{" << std::endl;
while(child) {
- writeExecutableContent(stream, child, indent + 1);
- child = Arabica::DOM::Element<std::string>(child.getNextSibling());
+ if (child.getNodeType() == Node_base::ELEMENT_NODE) {
+ writeExecutableContent(stream, child, indent + 1);
+ }
+ child = child.getNextSibling();
}
stream << padding << "}" << std::endl;
@@ -569,30 +1113,115 @@ std::string FSMToPromela::beautifyIndentation(const std::string& code, int inden
return beautifiedSS.str();
}
+void FSMToPromela::writeStrings(std::ostream& stream) {
+ stream << "/* string literals */" << std::endl;
+ std::set<std::string> literals = _analyzer.getLiterals();
+ std::map<std::string, int> events = _analyzer.getEvents();
+ std::map<std::string, int> origStates = _analyzer.getOrigStates();
+
+ for (std::set<std::string>::const_iterator litIter = literals.begin(); litIter != literals.end(); litIter++) {
+ if (events.find(*litIter) == events.end() && (origStates.find(*litIter) == origStates.end() || !_analyzer.usesInPredicate()))
+ stream << "#define " << _analyzer.macroForLiteral(*litIter) << " " << _analyzer.indexForLiteral(*litIter) << " /* " << *litIter << " */" << std::endl;
+ }
+}
+
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);
+// 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 << "/* datamodel variables */" << std::endl;
+ std::set<std::string> processedIdentifiers;
+ for (int i = 0; i < datas.size(); i++) {
+
+ Node<std::string> data = datas[i];
+ if (isInEmbeddedDocument(data))
+ continue;
+
+ std::string identifier = (HAS_ATTR_CAST(data, "id") ? ATTR_CAST(data, "id") : "");
+ std::string expression = (HAS_ATTR_CAST(data, "expr") ? ATTR_CAST(data, "expr") : "");
+ std::string type = boost::trim_copy(HAS_ATTR_CAST(data, "type") ? ATTR_CAST(data, "type") : "");
+
+ if (processedIdentifiers.find(identifier) != processedIdentifiers.end())
+ continue;
+ processedIdentifiers.insert(identifier);
+
+ if (boost::starts_with(type, "string")) {
+ type = "int" + type.substr(6, type.length() - 6);
+ }
+ std::string arrSize;
+
+ NodeSet<std::string> dataText = filterChildType(Node_base::TEXT_NODE, data, true);
+ std::string value;
+ if (dataText.size() > 0) {
+ value = dataText[0].getNodeValue();
+ boost::trim(value);
+ }
+
+ if (identifier.length() > 0) {
+
+ size_t bracketPos = type.find("[");
+ if (bracketPos != std::string::npos) {
+ arrSize = type.substr(bracketPos, type.length() - bracketPos);
+ type = type.substr(0, bracketPos);
+ }
+ std::string decl = type + " " + identifier + arrSize;
+
+ if (arrSize.length() > 0) {
+ stream << decl << ";" << std::endl;
+ _varInitializers.push_back(value);
+ } else {
+ stream << decl;
+ if (expression.length() > 0) {
+ // id and expr given
+ stream << " = " << _analyzer.replaceLiterals(boost::trim_copy(expression)) << ";" << std::endl;
+ } else if (value.length() > 0) {
+ // id and text content given
+ stream << " = " << _analyzer.replaceLiterals(value) << ";" << std::endl;
+ } else {
+ // only id given
+ stream << ";" << std::endl;
+ }
+ }
+ } else if (value.length() > 0) {
+ // no id but text content given
+ stream << beautifyIndentation(value) << 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 << "/* global variables */" << std::endl;
+
+ if (_analyzer.usesComplexEventStruct()) {
+ // event is defined with the typedefs
+ stream << "unsigned s : " << BIT_WIDTH(_globalStates.size() + 1) << "; /* current state */" << std::endl;
+ stream << "chan iQ = [10] of {_event_t} /* internal queue */" << std::endl;
+ stream << "chan eQ = [10] of {_event_t} /* external queue */" << std::endl;
+ stream << "chan tmpQ = [10] of {_event_t} /* temporary queue for external events in transitions */" << std::endl;
+ stream << "_event_t tmpQItem;" << std::endl;
+ } else {
+ stream << "unsigned _event : " << BIT_WIDTH(_analyzer.getEvents().size() + 1) << "; /* current event */" << std::endl;
+ stream << "unsigned s : " << BIT_WIDTH(_globalStates.size() + 1) << "; /* current state */" << std::endl;
+ stream << "chan iQ = [10] of {int} /* internal queue */" << std::endl;
+ stream << "chan eQ = [10] of {int} /* external queue */" << std::endl;
+ stream << "chan tmpQ = [10] of {int} /* temporary queue for external events in transitions */" << std::endl;
+ stream << "unsigned tmpQItem : " << BIT_WIDTH(_analyzer.getEvents().size() + 1) << ";" << std::endl;
+ }
+ stream << "bool eventLess = true; /* whether to process event-less only n this step */" << std::endl;
+ stream << "hidden int _index; /* helper for indexless foreach loops */" << std::endl;
+
+ if (_analyzer.usesEventField("sendid")) {
+ stream << "hidden int _lastSendId = 0; /* sequential counter for send ids */";
+ }
+
+// if (_analyzer.usesPlatformVars()) {
+// stream << "_x_t _x;" << std::endl;
+// }
stream << std::endl;
- stream << "// event sources" << std::endl;
+ stream << "/* event sources */" << std::endl;
if (_globalEventSource) {
_globalEventSource.writeDeclarations(stream);
@@ -618,10 +1247,8 @@ void FSMToPromela::writeEventSources(std::ostream& stream) {
invIter->second.writeEventSource(stream);
invIter++;
}
-
}
-
void FSMToPromela::writeFSM(std::ostream& stream) {
NodeSet<std::string> transitions;
@@ -629,37 +1256,38 @@ void FSMToPromela::writeFSM(std::ostream& stream) {
// 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);
+ 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, Arabica::DOM::Element<std::string>(transitions[j]), 1);
+ writeExecutableContent(stream, transitions[j], 1);
}
}
stream << std::endl;
stream << "nextStep:" << std::endl;
- stream << " // push send events to external queue" << 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 << " :: len(tmpQ) != 0 -> { tmpQ?_event; eQ!_event }" << 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 << " :: len(iQ) != 0 -> iQ ? _event /* from internal queue */" << std::endl;
+ stream << " :: else -> eQ ? _event /* from external queue */" << std::endl;
+ stream << " fi;" << std::endl << std::endl;
stream << " /* event dispatching per state */" << std::endl;
+ stream << "nextTransition:" << std::endl;
stream << " if" << std::endl;
writeEventDispatching(stream);
- stream << " :: else -> goto nextStep;" << std::endl;
+ stream << " :: else -> assert(false); /* this is an error as we dispatched all valid states */" << std::endl;
stream << " fi;" << std::endl;
stream << "terminate: skip;" << std::endl;
@@ -681,50 +1309,105 @@ void FSMToPromela::writeEventDispatching(std::ostream& stream) {
if (_globalStates[i] == _startState)
continue;
- stream << " :: (s == s" << i << ") -> {" << std::endl;
+ int number = i;
+ int digits = 0;
+ do {
+ number /= 10;
+ digits++;
+ } while (number != 0);
+ stream << " :: (s == s" << i << ") -> {";
+
+ INDENT_MIN(stream, 18 + digits, MIN_COMMENT_PADDING);
+
+ stream << " /* from state " << ATTR_CAST(_globalStates[i], "id") << " */" << std::endl;
NodeSet<std::string> transitions = filterChildElements(_nsInfo.xmlNSPrefix + "transition", _globalStates[i]);
writeDispatchingBlock(stream, transitions, 2);
- stream << " goto nextStep;" << std::endl;
+// stream << " goto nextStep;";
stream << " }" << std::endl;
}
}
void FSMToPromela::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";
+ if (transChain.size() == 0) {
+ stream << padding << "eventLess = false;" << std::endl;
+ stream << padding << "goto nextStep;";
+ INDENT_MIN(stream, padding.size() + 13, MIN_COMMENT_PADDING);
+ stream << "/* no transition applicable */" << std::endl;
+ return;
+ }
+
Element<std::string> currTrans = Element<std::string>(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";
+ std::stringstream tmpSS;
+
+ tmpSS << padding << "if" << std::endl;
+ size_t lineStart = tmpSS.tellp();
+
+ if (HAS_ATTR(currTrans, "cond")) {
+ tmpSS << padding << ":: ((";
} else {
- std::list<TrieNode*> trieNodes = _eventTrie.getWordsWithPrefix(eventDesc);
+ tmpSS << padding << ":: (";
+ }
- std::list<TrieNode*>::iterator trieIter = trieNodes.begin();
- while(trieIter != trieNodes.end()) {
- stream << " || e == e" << (*trieIter)->identifier;
- trieIter++;
+ if (!HAS_ATTR(currTrans, "event")) {
+ tmpSS << "eventLess";
+ } else {
+ std::string eventDescs = ATTR(currTrans, "event");
+
+ std::list<std::string> eventNames = tokenizeIdRefs(eventDescs);
+ std::set<std::string> eventPrefixes;
+ std::list<std::string>::iterator eventNameIter = eventNames.begin();
+ while(eventNameIter != eventNames.end()) {
+ std::string eventDesc = *eventNameIter;
+ 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.length() > 0) {
+ std::set<std::string> tmp = _analyzer.getEventsWithPrefix(*eventNameIter);
+ eventPrefixes.insert(tmp.begin(), tmp.end());
+ }
+ eventNameIter++;
}
+
+ if (eventPrefixes.size() > 0) {
+ tmpSS << "!eventLess && ";
+ } else {
+ tmpSS << "!eventLess";
+ }
+
+ std::string seperator;
+ std::set<std::string>::iterator eventIter = eventPrefixes.begin();
+ while(eventIter != eventPrefixes.end()) {
+ if (_analyzer.usesComplexEventStruct()) {
+ tmpSS << seperator << "_event.name == " << _analyzer.macroForLiteral(*eventIter);
+ } else {
+ tmpSS << seperator << "_event == " << _analyzer.macroForLiteral(*eventIter);
+ }
+ seperator = " || ";
+ eventIter++;
+ }
+ }
+
+ tmpSS << ")";
+ if (HAS_ATTR(currTrans, "cond")) {
+ tmpSS << (HAS_ATTR(currTrans, "cond") ? " && " + _analyzer.replaceLiterals(ATTR(currTrans, "cond")) + ")": "");
}
+ tmpSS << " -> goto t" << _transitions[currTrans] << ";";
+ size_t lineEnd = tmpSS.tellp();
+ size_t lineLength = lineEnd - lineStart;
- stream << ") && ";
- stream << (HAS_ATTR(currTrans, "cond") ? ATTR(currTrans, "cond") : "1");
- stream << ") -> goto t" << _transitions[currTrans] << ";" << std::endl;
- ;
+ for (int i = lineLength; i < MIN_COMMENT_PADDING; i++)
+ tmpSS << " ";
+
+ tmpSS << " /* transition to " << ATTR_CAST(getUltimateTarget(currTrans), "id") << " */" << std::endl;
+ stream << tmpSS.str();
stream << padding << ":: else {" << std::endl;
@@ -734,7 +1417,6 @@ void FSMToPromela::writeDispatchingBlock(std::ostream& stream, const Arabica::XP
}
writeDispatchingBlock(stream, cdrTransChain, indent + 1);
- stream << padding << " goto nextStep;" << std::endl;
stream << padding << "}" << std::endl;
stream << padding << "fi;" << std::endl;
}
@@ -743,6 +1425,14 @@ void FSMToPromela::writeDispatchingBlock(std::ostream& stream, const Arabica::XP
void FSMToPromela::writeMain(std::ostream& stream) {
stream << std::endl;
stream << "init {" << std::endl;
+ if (_varInitializers.size() > 0) {
+ std::list<std::string>::iterator initIter = _varInitializers.begin();
+ while(initIter != _varInitializers.end()) {
+ stream << beautifyIndentation(*initIter);
+ initIter++;
+ }
+ stream << std::endl;
+ }
if (_globalEventSource)
_globalEventSource.writeStartEventSources(stream, 1);
stream << " run step();" << std::endl;
@@ -750,11 +1440,15 @@ void FSMToPromela::writeMain(std::ostream& stream) {
}
+
void FSMToPromela::initNodes() {
// get all states
NodeSet<std::string> states = filterChildElements(_nsInfo.xmlNSPrefix + "state", _scxml);
for (int i = 0; i < states.size(); i++) {
+ if (InterpreterImpl::isInEmbeddedDocument(states[i]))
+ continue;
_states[ATTR_CAST(states[i], "id")] = Element<std::string>(states[i]);
+ _analyzer.addState(ATTR_CAST(states[i], "id"));
if (HAS_ATTR_CAST(states[i], "transient") && DOMUtils::attributeIsTrue(ATTR_CAST(states[i], "transient")))
continue;
_globalStates.push_back(states[i]);
@@ -778,11 +1472,33 @@ void FSMToPromela::initNodes() {
eventName = eventName.substr(0, eventName.size() - 1);
if (boost::ends_with(eventName, "."))
eventName = eventName.substr(0, eventName.size() - 1);
- _eventTrie.addWord(eventName);
+ if (eventName.size() > 0)
+ _analyzer.addEvent(eventName);
}
}
}
+ // do we need sendid / invokeid?
+ {
+ NodeSet<std::string> invokes = filterChildElements(_nsInfo.xmlNSPrefix + "invoke", _scxml, true);
+ NodeSet<std::string> sends = filterChildElements(_nsInfo.xmlNSPrefix + "send", _scxml, true);
+
+ for (int i = 0; i < invokes.size(); i++) {
+ if (HAS_ATTR_CAST(invokes[i], "idlocation")) {
+ }
+ }
+
+ for (int i = 0; i < sends.size(); i++) {
+ if (HAS_ATTR_CAST(sends[i], "idlocation")) {
+ _analyzer.addCode("_event.sendid");
+ }
+ if (HAS_ATTR_CAST(sends[i], "id")) {
+ _analyzer.addLiteral(ATTR_CAST(sends[i], "id"));
+ _analyzer.addCode("_event.sendid");
+ }
+ }
+
+ }
// external event names from comments
NodeSet<std::string> promelaEventSourceComments;
NodeSet<std::string> invokers = _xpath.evaluate("//" + _nsInfo.xpathPrefix + "invoke", _scxml).asNodeSet();
@@ -795,7 +1511,7 @@ void FSMToPromela::initNodes() {
if (TAGNAME_CAST(promelaEventSourceComments[i].getParentNode()) == "scxml") {
promES.type = PromelaEventSource::PROMELA_EVENT_SOURCE_GLOBAL;
- promES.trie = &_eventTrie;
+ promES.analyzer = &_analyzer;
promES.name = "global";
_globalEventSource = promES;
} else if (TAGNAME_CAST(promelaEventSourceComments[i].getParentNode()) == "invoke") {
@@ -805,7 +1521,7 @@ void FSMToPromela::initNodes() {
}
std::string invokeId = ATTR_CAST(promelaEventSourceComments[i].getParentNode(), "invokeid");
promES.type = PromelaEventSource::PROMELA_EVENT_SOURCE_INVOKER;
- promES.trie = &_eventTrie;
+ promES.analyzer = &_analyzer;
promES.name = invokeId;
_invokers[invokeId] = promES;
}
@@ -817,6 +1533,101 @@ void FSMToPromela::initNodes() {
for (int i = 0; i < transitions.size(); i++) {
_transitions[Element<std::string>(transitions[i])] = index++;
}
+
+ // add platform variables as string literals
+ _analyzer.addLiteral("_sessionid");
+ _analyzer.addLiteral("_name");
+
+ if (HAS_ATTR(_scxml, "name")) {
+ _analyzer.addLiteral(ATTR(_scxml, "name"), _analyzer.indexForLiteral("_sessionid"));
+ }
+
+ NodeSet<std::string> contents = filterChildElements(_nsInfo.xmlNSPrefix + "content", _scxml, true);
+ for (int i = 0; i < contents.size(); i++) {
+ Element<std::string> contentElem = Element<std::string>(contents[i]);
+ if (contentElem.hasChildNodes() && contentElem.getFirstChild().getNodeType() == Node_base::TEXT_NODE) {
+ _analyzer.addLiteral(spaceNormalize(contentElem.getFirstChild().getNodeValue()));
+ }
+ }
+
+
+ // extract and analyze source code
+ std::set<std::string> allCode;
+ std::set<std::string> allStrings;
+ {
+ NodeSet<std::string> withCond;
+ withCond.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "transition", _scxml, true));
+ withCond.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "if", _scxml, true));
+ withCond.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "elseif", _scxml, true));
+ for (int i = 0; i < withCond.size(); i++) {
+ Element<std::string> elem = Element<std::string>(withCond[i]);
+ if (HAS_ATTR(elem, "cond")) {
+ std::string code = ATTR(elem, "cond");
+ code = sanitizeCode(code);
+ elem.setAttribute("cond", code);
+ allCode.insert(code);
+ }
+ }
+ }
+ {
+ NodeSet<std::string> withExpr;
+ withExpr.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "log", _scxml, true));
+ withExpr.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "data", _scxml, true));
+ withExpr.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "assign", _scxml, true));
+ withExpr.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "content", _scxml, true));
+ withExpr.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "param", _scxml, true));
+ for (int i = 0; i < withExpr.size(); i++) {
+ Element<std::string> elem = Element<std::string>(withExpr[i]);
+ if (HAS_ATTR(elem, "expr")) {
+ std::string code = ATTR(elem, "expr");
+ code = sanitizeCode(code);
+ elem.setAttribute("expr", code);
+ allCode.insert(code);
+ }
+ }
+ }
+ {
+ NodeSet<std::string> withLocation;
+ withLocation.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "assign", _scxml, true));
+ for (int i = 0; i < withLocation.size(); i++) {
+ Element<std::string> elem = Element<std::string>(withLocation[i]);
+ if (HAS_ATTR(elem, "location")) {
+ std::string code = ATTR(elem, "location");
+ code = sanitizeCode(code);
+ elem.setAttribute("location", code);
+ allCode.insert(code);
+ }
+ }
+ }
+ {
+ NodeSet<std::string> withText;
+ withText.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "script", _scxml, true));
+ withText.push_back(filterChildElements(_nsInfo.xmlNSPrefix + "data", _scxml, true));
+ for (int i = 0; i < withText.size(); i++) {
+ NodeSet<std::string> texts = filterChildType(Node_base::TEXT_NODE, withText[i], true);
+ for (int j = 0; j < texts.size(); j++) {
+ if (texts[j].getNodeValue().size() > 0) {
+ Text<std::string> elem = Text<std::string>(texts[j]);
+ std::string code = elem.getNodeValue();
+ code = sanitizeCode(code);
+ elem.setNodeValue(code);
+ allCode.insert(code);
+ }
+ }
+ }
+ }
+ for (std::set<std::string>::const_iterator codeIter = allCode.begin(); codeIter != allCode.end(); codeIter++) {
+ _analyzer.addCode(*codeIter);
+ }
+
+}
+
+std::string FSMToPromela::sanitizeCode(const std::string& code) {
+ std::string replaced = code;
+ boost::replace_all(replaced, "\"", "'");
+ boost::replace_all(replaced, "_sessionid", "_SESSIONID");
+ boost::replace_all(replaced, "_name", "_NAME");
+ return replaced;
}
void PromelaInline::dump() {
@@ -849,12 +1660,22 @@ void FSMToPromela::writeProgram(std::ostream& stream) {
return;
}
+// std::cerr << _scxml << std::endl;
+
initNodes();
writeEvents(stream);
stream << std::endl;
writeStates(stream);
stream << std::endl;
+ if (_analyzer.usesInPredicate()) {
+ writeStateMap(stream);
+ stream << std::endl;
+ }
+ writeTypeDefs(stream);
+ stream << std::endl;
+ writeStrings(stream);
+ stream << std::endl;
writeDeclarations(stream);
stream << std::endl;
writeEventSources(stream);
@@ -864,6 +1685,28 @@ void FSMToPromela::writeProgram(std::ostream& stream) {
writeMain(stream);
stream << std::endl;
+ // write ltl expression for success
+ std::stringstream acceptingStates;
+ std::string seperator;
+ for (int i = 0; i < _globalStates.size(); i++) {
+ FlatStateIdentifier flatId(ATTR_CAST(_globalStates[i], "id"));
+ if (std::find(flatId.getActive().begin(), flatId.getActive().end(), "pass") != flatId.getActive().end()) {
+ acceptingStates << seperator << "s == s" << i;
+ seperator = " || ";
+ }
+ }
+ if (acceptingStates.str().size() > 0) {
+ stream << "ltl { eventually (" << acceptingStates.str() << ") }" << std::endl;
+ }
+
+// if (_states.find("active:{pass}") != _states.end()) {
+// for (int i = 0; i < _globalStates.size(); i++) {
+// if (_states["active:{pass}"] != _globalStates[i])
+// continue;
+// stream << "ltl { eventually (s == s" << i << ") }";
+// break;
+// }
+// }
}
} \ No newline at end of file