summaryrefslogtreecommitdiffstats
path: root/src/uscxml/transform/promela
diff options
context:
space:
mode:
authorStefan Radomski <github@mintwerk.de>2016-10-25 11:59:18 (GMT)
committerStefan Radomski <github@mintwerk.de>2016-10-25 11:59:18 (GMT)
commit954a1eb75f2abc81da1e09701d700674f0baddfb (patch)
tree873eb6412e958ecd53214ddbd6a3e17465da5100 /src/uscxml/transform/promela
parent1a1513c6497e8818eb2a92a8fbf77d4c60bc911e (diff)
downloaduscxml-954a1eb75f2abc81da1e09701d700674f0baddfb.zip
uscxml-954a1eb75f2abc81da1e09701d700674f0baddfb.tar.gz
uscxml-954a1eb75f2abc81da1e09701d700674f0baddfb.tar.bz2
Worked on PROMELA transformation
Diffstat (limited to 'src/uscxml/transform/promela')
-rw-r--r--src/uscxml/transform/promela/PromelaCodeAnalyzer.cpp596
-rw-r--r--src/uscxml/transform/promela/PromelaCodeAnalyzer.h150
-rw-r--r--src/uscxml/transform/promela/PromelaInlines.cpp165
-rw-r--r--src/uscxml/transform/promela/PromelaInlines.h113
4 files changed, 1024 insertions, 0 deletions
diff --git a/src/uscxml/transform/promela/PromelaCodeAnalyzer.cpp b/src/uscxml/transform/promela/PromelaCodeAnalyzer.cpp
new file mode 100644
index 0000000..4d1d8d5
--- /dev/null
+++ b/src/uscxml/transform/promela/PromelaCodeAnalyzer.cpp
@@ -0,0 +1,596 @@
+/**
+ * @file
+ * @author 2016 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
+ */
+
+#define MAX_MACRO_CHARS 64
+
+#include "PromelaCodeAnalyzer.h"
+#include "uscxml/transform/ChartToPromela.h"
+#include "uscxml/util/Predicates.h"
+#include "uscxml/util/DOM.h"
+#include "uscxml/util/String.h"
+
+#include <boost/algorithm/string.hpp>
+
+namespace uscxml {
+
+using namespace XERCESC_NS;
+
+void PromelaCodeAnalyzer::analyze(ChartToPromela* interpreter) {
+
+ /**
+ Create macro names for state identifiers
+ Do not add as literals as they are not unique with nested state-charts
+ */
+ {
+ for (size_t i = 0; i < interpreter->_states.size(); i++) {
+ DOMElement* state = interpreter->_states[i];
+ if (HAS_ATTR(state, "id")) {
+ createMacroName(ATTR(state, "id"));
+ }
+ }
+ }
+// _lastStrIndex = interpreter->_states.size();
+
+ /** Find all event names that might occur */
+ {
+ std::list<XERCESC_NS::DOMElement*> internalEventNames = DOMUtils::inDocumentOrder({
+ XML_PREFIX(interpreter->_scxml).str() + "transition",
+ XML_PREFIX(interpreter->_scxml).str() + "raise",
+ XML_PREFIX(interpreter->_scxml).str() + "send"
+ }, interpreter->_scxml);
+
+ for (auto internalEventName : internalEventNames) {
+ if (HAS_ATTR_CAST(internalEventName, "event")) {
+ std::string eventNames = ATTR_CAST(internalEventName, "event");
+ std::list<std::string> events = tokenize(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);
+ if (eventName.size() > 0)
+ _eventTrie.addWord(eventName);
+ }
+ }
+ }
+
+ for (auto state : interpreter->_states) {
+ if (HAS_ATTR(state, "id") && (isCompound(state) || isParallel(state))) {
+ _eventTrie.addWord("done.state." + ATTR(state, "id"));
+ }
+ }
+ }
+
+ // add event names from trie to string literals
+ std::list<TrieNode*> events = _eventTrie.getWordsWithPrefix("");
+ for (auto event : events) {
+ addLiteral(event->value);
+ }
+
+
+ /** Find all string literals */
+ {
+ // string literals for raise / send content
+ std::list<XERCESC_NS::DOMElement*> contents = DOMUtils::inDocumentOrder({
+ XML_PREFIX(interpreter->_scxml).str() + "content"
+ }, interpreter->_scxml);
+
+ for (auto content : contents) {
+ std::string contentStr = spaceNormalize(X(((DOMElement*)content)->getFirstChild()->getNodeValue()));
+ if (!isNumeric(contentStr.c_str(), 10)) {
+ addLiteral(contentStr);
+ }
+ }
+ }
+
+ /* add platform variables as string literals */
+ addLiteral(interpreter->_prefix + "_sessionid");
+ addLiteral(interpreter->_prefix + "_name");
+
+
+ /** Extract and analyze source code */
+ {
+ std::list<XERCESC_NS::DOMElement*> withCond = DOMUtils::inDocumentOrder({
+ XML_PREFIX(interpreter->_scxml).str() + "transition",
+ XML_PREFIX(interpreter->_scxml).str() + "if",
+ XML_PREFIX(interpreter->_scxml).str() + "elseif"
+ }, interpreter->_scxml);
+
+ for (auto cond : withCond) {
+ if (HAS_ATTR(cond, "cond")) {
+ std::string code = ATTR_CAST(cond, "cond");
+ code = sanitizeCode(code);
+ addCode(code, interpreter);
+ cond->setAttribute(X("cond"), X(code));
+ }
+ }
+
+ std::list<XERCESC_NS::DOMElement*> withExpr = DOMUtils::inDocumentOrder({
+ XML_PREFIX(interpreter->_scxml).str() + "log",
+ XML_PREFIX(interpreter->_scxml).str() + "data",
+ XML_PREFIX(interpreter->_scxml).str() + "assign",
+ XML_PREFIX(interpreter->_scxml).str() + "content",
+ XML_PREFIX(interpreter->_scxml).str() + "param"
+ }, interpreter->_scxml);
+
+ for (auto expr : withExpr) {
+ if (HAS_ATTR(expr, "expr")) {
+ std::string code = ATTR_CAST(expr, "expr");
+ code = sanitizeCode(code);
+ addCode(code, interpreter);
+ expr->setAttribute(X("expr"), X(code));
+ }
+ }
+
+ std::list<XERCESC_NS::DOMElement*> withLocation = DOMUtils::inDocumentOrder({
+ XML_PREFIX(interpreter->_scxml).str() + "assign"
+ }, interpreter->_scxml);
+
+ for (auto location : withLocation) {
+ if (HAS_ATTR(location, "location")) {
+ std::string code = ATTR_CAST(location, "location");
+ code = sanitizeCode(code);
+ addCode(code, interpreter);
+ location->setAttribute(X("location"), X(code));
+ }
+ }
+
+ std::list<XERCESC_NS::DOMElement*> withText = DOMUtils::inDocumentOrder({
+ XML_PREFIX(interpreter->_scxml).str() + "script"
+ }, interpreter->_scxml);
+
+ for (auto text : withText) {
+ std::list<XERCESC_NS::DOMNode*> texts = DOMUtils::filterChildType(DOMNode::TEXT_NODE, text, true);
+ for (auto textBlock : texts) {
+ DOMText* textText = static_cast<DOMText*>(textBlock);
+ std::string code = X(textText->getNodeValue()).str();
+ if (code.size() > 0) {
+ code = sanitizeCode(code);
+ addCode(code, interpreter);
+ textText->setNodeValue(X(code));
+ }
+ }
+ }
+
+ std::list<XERCESC_NS::DOMElement*> foreachs = DOMUtils::inDocumentOrder({
+ XML_PREFIX(interpreter->_scxml).str() + "foreach"
+ }, interpreter->_scxml);
+
+ for (auto foreach : foreachs) {
+ if (HAS_ATTR(foreach, "index")) {
+ addCode(ATTR(foreach, "index"), interpreter);
+ } else {
+ _hasIndexLessLoops = true;
+ }
+ if (HAS_ATTR(foreach, "item")) {
+ addCode(ATTR(foreach, "item"), interpreter);
+ }
+ }
+
+ // do we need sendid / invokeid?
+ {
+ std::list<DOMElement*> invokes = DOMUtils::inDocumentOrder({XML_PREFIX(interpreter->_scxml).str() + "invoke"}, interpreter->_scxml);
+ std::list<DOMElement*> sends = DOMUtils::inDocumentOrder({XML_PREFIX(interpreter->_scxml).str() + "send"}, interpreter->_scxml);
+ std::list<DOMElement*> cancels = DOMUtils::inDocumentOrder({XML_PREFIX(interpreter->_scxml).str() + "cancel"}, interpreter->_scxml);
+
+ if (cancels.size() > 0) {
+ addCode("_event.invokeid", interpreter);
+ _usesCancel = true;
+ }
+
+ for (auto send : sends) {
+ if (HAS_ATTR(send, "idlocation")) {
+ addCode("_event.sendid", interpreter);
+ }
+ if (HAS_ATTR(send, "id")) {
+ addLiteral(ATTR(send, "id"));
+ addCode("_event.sendid", interpreter);
+ }
+
+ // do we need delays?
+ if (HAS_ATTR(send, "delay") || HAS_ATTR(send, "delayexpr")) {
+ addCode("_event.delay", interpreter);
+#if NEW_DELAY_RESHUFFLE
+#else
+ addCode("_event.seqNr", interpreter);
+#endif
+ }
+ }
+ }
+
+ // add all namelist entries to the _event structure
+ {
+ std::list<DOMElement*> withNamelists;
+ withNamelists.splice(withNamelists.end(), DOMUtils::inDocumentOrder({XML_PREFIX(interpreter->_scxml).str() + "send"}, interpreter->_scxml));
+ withNamelists.splice(withNamelists.end(), DOMUtils::inDocumentOrder({XML_PREFIX(interpreter->_scxml).str() + "invoke"}, interpreter->_scxml));
+ for (auto withNamelist : withNamelists) {
+ if (HAS_ATTR(withNamelist, "namelist")) {
+ std::string namelist = ATTR(withNamelist, "namelist");
+ std::list<std::string> names = tokenize(namelist);
+ for (std::list<std::string>::iterator nameIter = names.begin(); nameIter != names.end(); nameIter++) {
+ addCode("_event.data." + *nameIter + " = 0;", interpreter); // introduce for _event_t typedef
+ }
+ }
+ }
+ }
+
+ }
+
+}
+
+std::string PromelaCodeAnalyzer::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 PromelaCodeAnalyzer::addCode(const std::string& code, ChartToPromela* interpreter) {
+ PromelaParser parser(code);
+ // parser.dump();
+
+ // find all strings
+ std::list<PromelaParserNode*> astNodes;
+ astNodes.push_back(parser.ast);
+
+ while(astNodes.size() > 0) {
+ PromelaParserNode* node = astNodes.front();
+ astNodes.pop_front();
+
+ // node->dump();
+
+ bool hasValue = false;
+ int assignedValue = 0;
+
+
+ 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_ASGN:
+ if (node->operands.back()->type == PML_CONST) {
+ hasValue = true;
+ if (isInteger(node->operands.back()->value.c_str(), 10)) {
+ assignedValue = strTo<int>(node->operands.back()->value);
+ }
+ }
+ if (node->operands.back()->type == PML_STRING) {
+ // remember strings for later
+ astNodes.push_back(node->operands.back());
+ }
+ if (node->operands.front()->type == PML_CMPND) {
+ node = node->operands.front();
+ } else {
+ break;
+ }
+ // if (node->operands.front()->type != PML_NAME)
+ // break; // this will skip array assignments
+ case PML_CMPND: {
+ std::string nameOfType;
+ std::list<PromelaParserNode*>::iterator opIter = node->operands.begin();
+ if ((*opIter)->type != PML_NAME) {
+ node->dump();
+ return;
+ assert(false);
+ }
+
+ PromelaTypedef* td = &_typeDefs;
+ std::string seperator;
+
+ while(opIter != node->operands.end()) {
+ switch ((*opIter)->type) {
+ case PML_NAME:
+ td = &td->types[(*opIter)->value];
+ td->occurrences.insert(interpreter);
+
+ 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];
+ td->occurrences.insert(interpreter);
+
+ nameOfType += seperator + name->value;
+ td->name = nameOfType + "_t";
+
+ if (isInteger(subscript->value.c_str(), 10)) {
+ td->arraySize = strTo<int>(subscript->value);
+ }
+ break;
+ }
+ default:
+ if ((*opIter)->type == PML_CONST) {
+ // break fall through from ASGN
+ break;
+ }
+ // 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++;
+ }
+
+ if (hasValue) {
+ if (td->maxValue < assignedValue)
+ td->maxValue = assignedValue;
+ if (td->minValue > assignedValue)
+ td->minValue = assignedValue;
+ }
+
+ continue; // skip processing nested AST nodes
+ }
+ case PML_NAME: {
+ _typeDefs.types[node->value].occurrences.insert(interpreter);
+ _typeDefs.types[node->value].minValue = 0;
+ _typeDefs.types[node->value].maxValue = 0;
+ // test325
+ if (node->value.compare("_ioprocessors") == 0) {
+ addCode("_ioprocessors.scxml.location", interpreter);
+ }
+
+ break;
+ }
+
+ default:
+ // node->dump();
+ break;
+ // assert(false);
+ }
+
+ astNodes.insert(astNodes.end(), node->operands.begin(), node->operands.end());
+
+ }
+}
+
+void PromelaCodeAnalyzer::addLiteral(const std::string& literal, int forceIndex) {
+ if (boost::starts_with(literal, "'"))
+ throw std::runtime_error("Literal " + literal + " passed with quotes");
+
+ if (_literals.find(literal) != _literals.end())
+ return;
+ _literals.insert(literal);
+ createMacroName(literal);
+ enumerateLiteral(literal, forceIndex);
+}
+
+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::macroForLiteral(const std::string& literal) {
+ if (boost::starts_with(literal, "'"))
+ throw std::runtime_error("Literal " + literal + " passed with quotes");
+
+ if (_strMacros.find(literal) == _strMacros.end())
+ throw std::runtime_error("No macro for literal '" + literal + "' known");
+ return _strMacros[literal];
+}
+
+
+std::string PromelaCodeAnalyzer::createMacroName(const std::string& literal) {
+ if (_strMacros.find(literal) != _strMacros.end())
+ return _strMacros[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;
+ if(macroName.length() < 1)
+ macroName = "_EMPTY_STRING";
+ if(macroName.length() < 2 && macroName[0] == '_')
+ macroName = "_WEIRD_CHARS";
+
+ 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);
+ _strMacros[literal] = macroName;
+ return macroName;
+}
+
+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::adaptCode(const std::string& code, const std::string& prefix) {
+ // for (std::map<std::string, std::string>::const_iterator litIter = _strMacros.begin(); litIter != _strMacros.end(); litIter++) {
+ // boost::replace_all(replaced, "'" + litIter->first + "'", litIter->second);
+ // }
+ // boost::replace_all(replaced, "_event", prefix + "_event");
+ // replace all variables from analyzer
+
+ std::string processed = code;
+ std::stringstream processedStr;
+ std::list<std::pair<size_t, size_t> > posList;
+ std::list<std::pair<size_t, size_t> >::iterator posIter;
+ size_t lastPos;
+
+ // prepend all identifiers with our prefix
+ {
+ PromelaParser parsed(processed);
+ // parsed.dump();
+ posList = getTokenPositions(code, PML_NAME, parsed.ast);
+ posList.sort();
+ posIter = posList.begin();
+ lastPos = 0;
+
+ while (posIter != posList.end()) {
+ std::string token = code.substr(posIter->first, posIter->second - posIter->first);
+ if (std::all_of(token.begin(), token.end(), ::isupper) && false) {
+ // assume it is a state-name macro
+ processedStr << code.substr(lastPos, posIter->first - lastPos) << token;
+ } else {
+ processedStr << code.substr(lastPos, posIter->first - lastPos) << prefix << token;
+ }
+ lastPos = posIter->second;
+ posIter++;
+ }
+ processedStr << processed.substr(lastPos, processed.size() - lastPos);
+
+ processed = processedStr.str();
+ processedStr.clear();
+ processedStr.str("");
+ }
+
+ // replace string literals
+ {
+ PromelaParser parsed(processed);
+ posList = getTokenPositions(code, PML_STRING, parsed.ast);
+ posList.sort();
+ posIter = posList.begin();
+ lastPos = 0;
+
+ while (posIter != posList.end()) {
+ processedStr << processed.substr(lastPos, posIter->first - lastPos);
+ // std::cout << processed.substr(posIter->first + 1, posIter->second - posIter->first - 2) << std::endl;
+ assert(_strMacros.find(processed.substr(posIter->first + 1, posIter->second - posIter->first - 2)) != _strMacros.end());
+ processedStr << _strMacros[processed.substr(posIter->first + 1, posIter->second - posIter->first - 2)];
+ lastPos = posIter->second;
+ posIter++;
+ }
+ processedStr << processed.substr(lastPos, processed.size() - lastPos);
+
+ processed = processedStr.str();
+ processedStr.clear();
+ processedStr.str("");
+ }
+
+ return processed;
+}
+
+std::list<std::pair<size_t, size_t> > PromelaCodeAnalyzer::getTokenPositions(const std::string& expr, int type, PromelaParserNode* ast) {
+ std::list<std::pair<size_t, size_t> > posList;
+ if (ast->type == type && ast->loc != NULL) {
+ // ast->dump();
+ if (type == PML_NAME && ast->parent &&
+ ((ast->parent->type == PML_CMPND && ast->parent->operands.front() != ast) ||
+ (ast->parent->parent && ast->parent->type == PML_VAR_ARRAY && ast->parent->parent->type == PML_CMPND))) {
+ // field in a compound
+ } else {
+ if (ast->loc->firstLine == 0) {
+ posList.push_back(std::make_pair(ast->loc->firstCol, ast->loc->lastCol));
+ } else {
+ int line = ast->loc->firstLine;
+ size_t lastPos = 0;
+ while(line > 0) {
+ lastPos = expr.find_first_of('\n', lastPos + 1);
+ line--;
+ }
+ posList.push_back(std::make_pair(lastPos + ast->loc->firstCol, lastPos + ast->loc->lastCol));
+ }
+ }
+ }
+ for (std::list<PromelaParserNode*>::iterator opIter = ast->operands.begin(); opIter != ast->operands.end(); opIter++) {
+ std::list<std::pair<size_t, size_t> > tmp = getTokenPositions(expr, type, *opIter);
+ posList.insert(posList.end(), tmp.begin(), tmp.end());
+ }
+ return posList;
+}
+
+std::string PromelaCodeAnalyzer::getTypeReset(const std::string& var, const PromelaTypedef& type, const std::string padding) {
+ std::stringstream assignment;
+
+ std::map<std::string, PromelaTypedef>::const_iterator typeIter = type.types.begin();
+ while(typeIter != type.types.end()) {
+ const PromelaTypedef& innerType = typeIter->second;
+ if (innerType.arraySize > 0) {
+ for (size_t i = 0; i < innerType.arraySize; i++) {
+ assignment << padding << var << "." << typeIter->first << "[" << i << "] = 0;" << std::endl;
+ }
+ } else if (innerType.types.size() > 0) {
+ assignment << getTypeReset(var + "." + typeIter->first, typeIter->second, padding);
+ } else {
+ assignment << padding << var << "." << typeIter->first << " = 0;" << std::endl;
+ }
+ typeIter++;
+ }
+ return assignment.str();
+
+}
+
+}
diff --git a/src/uscxml/transform/promela/PromelaCodeAnalyzer.h b/src/uscxml/transform/promela/PromelaCodeAnalyzer.h
new file mode 100644
index 0000000..656b24e
--- /dev/null
+++ b/src/uscxml/transform/promela/PromelaCodeAnalyzer.h
@@ -0,0 +1,150 @@
+/**
+ * @file
+ * @author 2016 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 PROMELACODEANALYZER_H_E89FF519
+#define PROMELACODEANALYZER_H_E89FF519
+
+#include "uscxml/transform/Trie.h"
+#include "uscxml/plugins/datamodel/promela/PromelaParser.h"
+#include "uscxml/plugins/datamodel/promela/parser/promela.tab.hpp"
+
+#include <set>
+
+namespace uscxml {
+
+class ChartToPromela;
+
+class USCXML_API PromelaCodeAnalyzer {
+public:
+ class PromelaTypedef {
+ public:
+ PromelaTypedef() {}
+ std::string name;
+ std::string type;
+ size_t arraySize = 0;
+ size_t minValue = 0;
+ size_t maxValue = 0;
+ std::map<std::string, PromelaTypedef> types;
+ std::set<ChartToPromela*> occurrences;
+
+ bool operator==(const PromelaTypedef& other) const {
+ return name == other.name;
+ }
+
+ };
+
+ PromelaCodeAnalyzer() : _eventTrie(".") {}
+
+ void analyze(ChartToPromela* interpreter);
+
+ void addCode(const std::string& code, ChartToPromela* interpreter);
+ void addLiteral(const std::string& stateName, int forceIndex = -1);
+
+ bool usesComplexEventStruct() {
+ return _typeDefs.types.find("_event") != _typeDefs.types.end() && _typeDefs.types["_event"].types.size() > 0;
+ }
+ bool usesEventField(const std::string& fieldName) {
+ if (usesComplexEventStruct() && _typeDefs.types["_event"].types.find(fieldName) != _typeDefs.types["_event"].types.end())
+ return true;
+ return false;
+ }
+ bool usesCancel(const std::string& elementName) {
+ return _usesCancel;
+ }
+
+ bool usesEventDataField(const std::string& fieldName) {
+ if (usesComplexEventStruct() &&
+ _typeDefs.types["_event"].types.find("data") != _typeDefs.types["_event"].types.end() &&
+ _typeDefs.types["_event"].types["data"].types.find(fieldName) != _typeDefs.types["_event"].types["data"].types.end())
+ return true;
+ return false;
+ }
+
+ std::string getTypeAssignment(const std::string& varTo, const std::string& varFrom, const PromelaTypedef& type, const std::string padding = "");
+ std::string getTypeReset(const std::string& var, const PromelaTypedef& type, const std::string padding = "");
+
+ bool usesInPredicate() {
+ return _usesInPredicate;
+ }
+ void usesInPredicate(bool value) {
+ _usesInPredicate = value;
+ }
+ bool usesPlatformVars() {
+ return _usesPlatformVars;
+ }
+
+ bool hasIndexLessLoops() {
+ return _hasIndexLessLoops;
+ }
+
+ std::string macroForLiteral(const std::string& literal);
+ int indexForLiteral(const std::string& literal);
+
+ std::set<std::string> getLiterals() {
+ return _literals;
+ }
+ std::set<std::string> getEventsWithPrefix(const std::string& prefix);
+
+ Trie& getTrie() {
+ return _eventTrie;
+ }
+
+ std::string adaptCode(const std::string& code, const std::string& prefix);
+
+ static std::string prefixIdentifiers(const std::string& expr, const std::string& prefix);
+ static std::list<std::pair<size_t, size_t> > getTokenPositions(const std::string& expr, int type, PromelaParserNode* ast);
+
+ PromelaTypedef& getTypes() {
+ return _typeDefs;
+ }
+
+ PromelaTypedef& getType(const std::string& typeName) {
+ return _typeDefs.types.at(typeName);
+ }
+
+ std::string sanitizeCode(const std::string& code);
+
+protected:
+ void addEvent(const std::string& eventName);
+ void addState(const std::string& stateName, size_t index);
+
+ std::string createMacroName(const std::string& literal);
+ int enumerateLiteral(const std::string& literal, int forceIndex = -1);
+
+ std::map<std::string, std::string> _strMacros; // macronames for string literals
+ std::map<std::string, int> _strIndex; // integer enumeration for string
+ std::set<std::string> _literals;
+
+ PromelaTypedef _typeDefs;
+ Trie _eventTrie;
+
+private:
+ std::set<std::string> _macroNameSet; // helper set for uniqueness of macros
+ int _lastStrIndex = 1;
+ bool _usesCancel = false;
+ bool _usesInPredicate = false;
+ bool _usesPlatformVars = false;
+ bool _hasIndexLessLoops = false;
+};
+
+
+
+}
+
+#endif /* end of include guard: PROMELACODEANALYZER_H_E89FF519 */
diff --git a/src/uscxml/transform/promela/PromelaInlines.cpp b/src/uscxml/transform/promela/PromelaInlines.cpp
new file mode 100644
index 0000000..7d62762
--- /dev/null
+++ b/src/uscxml/transform/promela/PromelaInlines.cpp
@@ -0,0 +1,165 @@
+/**
+ * @file
+ * @author 2016 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 "PromelaInlines.h"
+#include <boost/algorithm/string.hpp>
+
+namespace uscxml {
+
+using namespace XERCESC_NS;
+
+void PromelaInline::dump() {
+}
+
+PromelaInline::PromelaInline(const XERCESC_NS::DOMNode* node) {
+ if (node->getNodeType() != DOMNode::COMMENT_NODE && node->getNodeType() != DOMNode::TEXT_NODE)
+ return; // nothing to do
+
+ std::stringstream ssLine(X(node->getNodeValue()).str());
+ std::string line;
+
+ while(std::getline(ssLine, line)) {
+ // skip to first promela line
+ boost::trim(line);
+ if (boost::starts_with(line, "promela"))
+ break;
+ }
+
+ if (!boost::starts_with(line, "promela"))
+ return;
+
+ if (false) {
+ } else if (boost::starts_with(line, "promela-code")) {
+ type = PROMELA_CODE;
+ } else if (boost::starts_with(line, "promela-ltl")) {
+ type = PROMELA_LTL;
+ } else if (boost::starts_with(line, "promela-event-all")) {
+ type = PROMELA_EVENT_ALL_BUT;
+ } else if (boost::starts_with(line, "promela-event")) {
+ type = PROMELA_EVENT_ONLY;
+ } else if (boost::starts_with(line, "promela-progress")) {
+ type = PROMELA_PROGRESS_LABEL;
+ } else if (boost::starts_with(line, "promela-accept")) {
+ type = PROMELA_ACCEPT_LABEL;
+ } else if (boost::starts_with(line, "promela-end")) {
+ type = PROMELA_END_LABEL;
+ }
+
+ std::stringstream contentSS;
+ size_t endType = line.find_first_of(": \n");
+
+ std::string seperator;
+ if (endType != std::string::npos && endType + 1 < line.size()) {
+ contentSS << line.substr(endType + 1, line.size() - endType + 1);
+ seperator = "\n";
+ }
+
+ while(std::getline(ssLine, line)) {
+ boost::trim(line);
+ if (boost::starts_with(line, "promela")) {
+ std::cerr << "Split multiple #promela pragmas into multiple comments!" << std::endl;
+ break;
+ }
+ contentSS << seperator << line;
+ seperator = "\n";
+ }
+ content = contentSS.str();
+
+}
+
+PromelaInlines::PromelaInlines(const XERCESC_NS::DOMNode* node) {
+
+ std::list<DOMNode*> levelNodes;
+ levelNodes.push_back(const_cast<XERCESC_NS::DOMNode*>(node));
+
+ size_t level = 0;
+ while(levelNodes.size() > 0) {
+ PromelaInline* predecessor = NULL;
+
+ // iterate all nodes at given level
+ for (auto levelNode : levelNodes) {
+
+ // get all comments
+ std::list<DOMNode*> comments = DOMUtils::filterChildType(DOMNode::COMMENT_NODE, levelNode);
+ for (auto comment : comments) {
+
+ PromelaInline* tmp = new PromelaInline(comment);
+ if (tmp->type == PromelaInline::PROMELA_NIL) {
+ delete tmp;
+ continue;
+ }
+
+ if (predecessor != NULL) {
+ tmp->prevSibling = predecessor;
+ predecessor->nextSibling = tmp;
+ }
+ tmp->level = level;
+ tmp->container = static_cast<DOMElement*>(levelNode);
+ predecessor = tmp;
+ inlines[levelNode].push_back(tmp);
+ allInlines.push_back(tmp);
+ }
+ }
+
+ levelNodes = DOMUtils::filterChildType(DOMNode::ELEMENT_NODE, levelNodes);
+ level++;
+ }
+
+}
+
+PromelaInlines::~PromelaInlines() {
+}
+
+std::list<PromelaInline*> PromelaInlines::getRelatedTo(const XERCESC_NS::DOMNode* node,
+ PromelaInline::PromelaInlineType type) {
+ std::list<PromelaInline*> related;
+
+ auto inlIter = inlines.begin();
+ while (inlIter != inlines.end()) {
+ std::list<PromelaInline*>::iterator pmlIter = inlIter->second.begin();
+ while (pmlIter != inlIter->second.end()) {
+ if ((type != PromelaInline::PROMELA_NIL || (*pmlIter)->type == type) && (*pmlIter)->relatesTo(node)) {
+ related.push_back(*pmlIter);
+ }
+ pmlIter++;
+ }
+ inlIter++;
+ }
+ return related;
+}
+
+std::list<PromelaInline*> PromelaInlines::getAllOfType(uint32_t type) {
+ std::list<PromelaInline*> related;
+
+ auto inlIter = inlines.begin();
+ while (inlIter != inlines.end()) {
+ std::list<PromelaInline*>::iterator pmlIter = inlIter->second.begin();
+ while (pmlIter != inlIter->second.end()) {
+ if ((*pmlIter)->type & type) {
+ related.push_back(*pmlIter);
+ }
+ pmlIter++;
+ }
+ inlIter++;
+ }
+ return related;
+
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/transform/promela/PromelaInlines.h b/src/uscxml/transform/promela/PromelaInlines.h
new file mode 100644
index 0000000..2621f8d
--- /dev/null
+++ b/src/uscxml/transform/promela/PromelaInlines.h
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * @author 2016 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 PROMELAINLINES_H_29BF8EF3
+#define PROMELAINLINES_H_29BF8EF3
+
+#include "uscxml/config.h"
+#include "uscxml/Common.h"
+#include "uscxml/messages/Data.h"
+#include "uscxml/util/DOM.h"
+
+#include <xercesc/dom/DOM.hpp>
+#include <list>
+#include <string>
+
+namespace uscxml {
+
+class USCXML_API PromelaInline {
+public:
+ enum PromelaInlineType {
+ PROMELA_NIL = 0x0000,
+ PROMELA_LTL = 0x0001,
+ PROMELA_CODE = 0x0002,
+ PROMELA_EVENT_ALL_BUT = 0x0004,
+ PROMELA_EVENT_ONLY = 0x0008,
+ PROMELA_PROGRESS_LABEL = 0x0010,
+ PROMELA_ACCEPT_LABEL = 0x0020,
+ PROMELA_END_LABEL = 0x0040
+ };
+
+ PromelaInline(const XERCESC_NS::DOMNode* node);
+ virtual ~PromelaInline() {}
+
+ operator bool() {
+ return (type != PROMELA_NIL);
+ }
+
+ std::list<PromelaInline*> children;
+ PromelaInline* prevSibling;
+ PromelaInline* nextSibling;
+
+ virtual void dump();
+
+ virtual bool relatesTo(const XERCESC_NS::DOMNode* node) {
+ return container == node;
+ }
+
+ size_t level;
+ std::string content;
+ const XERCESC_NS::DOMElement* container;
+ PromelaInlineType type;
+
+protected:
+ PromelaInline() : prevSibling(NULL), nextSibling(NULL), type(PROMELA_NIL) {};
+};
+
+
+class USCXML_API PromelaEventSource : public PromelaInline {
+public:
+ PromelaEventSource(const PromelaInline& pmlInline) {
+ type = pmlInline.type;
+ container = pmlInline.container;
+ content = pmlInline.content;
+ events = Data::fromJSON(pmlInline.content);
+ }
+
+ virtual bool relatesTo(const XERCESC_NS::DOMNode* node) {
+ return container == node || DOMUtils::isDescendant(node, container);
+ }
+
+ Data events;
+};
+
+
+class USCXML_API PromelaInlines {
+public:
+
+ PromelaInlines(const XERCESC_NS::DOMNode* node);
+ PromelaInlines() {}
+
+ virtual ~PromelaInlines();
+
+ std::list<PromelaInline*> getRelatedTo(const XERCESC_NS::DOMNode* node, PromelaInline::PromelaInlineType type);
+ std::list<PromelaInline*> getAllOfType(uint32_t type);
+
+ std::map<const XERCESC_NS::DOMNode*, std::list<PromelaInline*> > inlines;
+ std::list<PromelaInline*> allInlines;
+
+ static std::list<std::string> getStringLiterals(const Data& data);
+ static std::list<std::string> getEventNames(const Data& data);
+
+
+};
+
+}
+
+#endif /* end of include guard: PROMELAINLINES_H_29BF8EF3 */