diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-03-28 23:28:46 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-03-28 23:28:46 (GMT) |
commit | 2317f2bf8beb03c60463a9482dbef23540f5c1e0 (patch) | |
tree | 9983553e5289cf622e782d0132bb1810276364d2 /src/uscxml/interpreter | |
parent | 7279ab2caf72b68126bf0c1d7e62c7d89024f9a0 (diff) | |
download | uscxml-2317f2bf8beb03c60463a9482dbef23540f5c1e0.zip uscxml-2317f2bf8beb03c60463a9482dbef23540f5c1e0.tar.gz uscxml-2317f2bf8beb03c60463a9482dbef23540f5c1e0.tar.bz2 |
Refactoring and W3C tests
- Moved core of interpreter to support various versions
- Added experimental setConfiguration()
- There can be more than one initial state
Diffstat (limited to 'src/uscxml/interpreter')
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft6.cpp | 920 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft6.h | 31 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft7.cpp | 439 | ||||
-rw-r--r-- | src/uscxml/interpreter/InterpreterDraft7.h | 46 |
4 files changed, 1436 insertions, 0 deletions
diff --git a/src/uscxml/interpreter/InterpreterDraft6.cpp b/src/uscxml/interpreter/InterpreterDraft6.cpp new file mode 100644 index 0000000..d95ae4c --- /dev/null +++ b/src/uscxml/interpreter/InterpreterDraft6.cpp @@ -0,0 +1,920 @@ +#include "InterpreterDraft6.h" + +#include <glog/logging.h> + +namespace uscxml { + +using namespace Arabica::XPath; +using namespace Arabica::DOM; + +// see: http://www.w3.org/TR/scxml/#AlgorithmforSCXMLInterpretation +void InterpreterDraft6::interpret() { + if (!_isInitialized) + init(); + + if (!_scxml) + return; +// dump(); + + _sessionId = getUUID(); + + std::string datamodelName; + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) + datamodelName = ATTR(_scxml, "datamodel"); + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel + datamodelName = ATTR(_scxml, "profile"); + if(datamodelName.length() > 0) + _dataModel = Factory::createDataModel(datamodelName, this); + if(datamodelName.length() > 0 && !_dataModel) { + LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; + } + + if (_dataModel) { + _dataModel.assign("_x.args", _cmdLineOptions); + if (_httpServlet) { + Data data; + data.compound["location"] = Data(_httpServlet->getURL(), Data::VERBATIM); + _dataModel.assign("_ioprocessors['http']", data); + } + } + + setupIOProcessors(); + + _running = true; + _binding = (HAS_ATTR(_scxml, "binding") && boost::iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); + + // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding + + if (_dataModel && _binding == EARLY) { + // initialize all data elements + NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _document).asNodeSet(); + for (unsigned int i = 0; i < dataElems.size(); i++) { + initializeData(dataElems[i]); + } + } else if(_dataModel) { + // initialize current data elements + NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml)); + for (unsigned int i = 0; i < topDataElems.size(); i++) { + initializeData(topDataElems[i]); + } + } + + // executeGlobalScriptElements + NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "script", _document).asNodeSet(); + for (unsigned int i = 0; i < globalScriptElems.size(); i++) { + if (_dataModel) + executeContent(globalScriptElems[i]); + } + + NodeSet<std::string> initialTransitions; + + if (_userDefinedStartConfiguration.size() == 0) { + // try to get initial transition form initial element + initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet(); + } + + if (initialTransitions.size() == 0) { + Arabica::XPath::NodeSet<std::string> initialStates; + if (_userDefinedStartConfiguration.size() > 0) { + // otherwise use user supplied config + initialTransitions = getStates(_userDefinedStartConfiguration); + } else { + // or fetch per draft + initialStates = getInitialStates(); + } + + assert(initialStates.size() > 0); + for (int i = 0; i < initialStates.size(); i++) { + Arabica::DOM::Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); + initialElem.setAttribute("generated", "true"); + Arabica::DOM::Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); + transitionElem.setAttribute("target", ATTR(initialStates[i], "id")); + initialElem.appendChild(transitionElem); + _scxml.appendChild(initialElem); + initialTransitions.push_back(transitionElem); + } + } + + assert(initialTransitions.size() > 0); + enterStates(initialTransitions); + + assert(hasLegalConfiguration()); + mainEventLoop(); + + if (_parentQueue) { + // send one final event to unblock eventual listeners + Event quit; + quit.name = "done.state.scxml"; + _parentQueue->push(quit); + } + + // set datamodel to null from this thread + if(_dataModel) + _dataModel = DataModel(); + +} + +/** + * Called with a single data element from the topmost datamodel element. + */ +void InterpreterDraft6::initializeData(const Arabica::DOM::Node<std::string>& data) { + if (!_dataModel) { + LOG(ERROR) << "Cannot initialize data when no datamodel is given!"; + return; + } + try { + if (!HAS_ATTR(data, "id")) { + LOG(ERROR) << "Data element has no id!"; + return; + } + + if (HAS_ATTR(data, "expr")) { + std::string value = ATTR(data, "expr"); + _dataModel.assign(ATTR(data, "id"), value); + } else if (HAS_ATTR(data, "src")) { + URL srcURL(ATTR(data, "src")); + if (!srcURL.isAbsolute()) + toAbsoluteURI(srcURL); + + std::stringstream ss; + if (_cachedURLs.find(srcURL.asString()) != _cachedURLs.end()) { + ss << _cachedURLs[srcURL.asString()]; + } else { + ss << srcURL; + _cachedURLs[srcURL.asString()] = srcURL; + } + _dataModel.assign(ATTR(data, "id"), ss.str()); + + } else if (data.hasChildNodes()) { + // search for the text node with the actual script + NodeList<std::string> dataChilds = data.getChildNodes(); + for (int i = 0; i < dataChilds.getLength(); i++) { + if (dataChilds.item(i).getNodeType() == Node_base::TEXT_NODE) { + Data value = Data(dataChilds.item(i).getNodeValue()); + _dataModel.assign(ATTR(data, "id"), value); + break; + } + } + } + + } catch (Event e) { + LOG(ERROR) << "Syntax error in data element:" << std::endl << e << std::endl; + } +} + +void InterpreterDraft6::mainEventLoop() { + std::set<InterpreterMonitor*>::iterator monIter; + + while(_running) { + NodeSet<std::string> enabledTransitions; + _stable = false; + + // Here we handle eventless transitions and transitions + // triggered by internal events until machine is stable + while(_running && !_stable) { +#if 0 + std::cout << "Configuration: "; + for (int i = 0; i < _configuration.size(); i++) { + std::cout << ATTR(_configuration[i], "id") << ", "; + } + std::cout << std::endl; +#endif + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + try { + (*monIter)->beforeMicroStep(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeMicroStep on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeMicroStep on monitors"; + } + monIter++; + } + + enabledTransitions = selectEventlessTransitions(); + if (enabledTransitions.size() == 0) { + if (_internalQueue.size() == 0) { + _stable = true; + } else { + _currEvent = _internalQueue.front(); + _internalQueue.pop_front(); +#if VERBOSE + std::cout << "Received internal event " << _currEvent.name << std::endl; +#endif + if (_dataModel) + _dataModel.setEvent(_currEvent); + enabledTransitions = selectTransitions(_currEvent.name); + } + } + if (!enabledTransitions.empty()) { + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + try { + (*monIter)->beforeTakingTransitions(this, enabledTransitions); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeTakingTransitions on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeTakingTransitions on monitors"; + } + monIter++; + } + microstep(enabledTransitions); + } + } + + for (unsigned int i = 0; i < _statesToInvoke.size(); i++) { + NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", _statesToInvoke[i]); + for (unsigned int j = 0; j < invokes.size(); j++) { + invoke(invokes[j]); + } + } + + _statesToInvoke = NodeSet<std::string>(); + if (!_internalQueue.empty()) + continue; + + // assume that we have a legal configuration as soon as the internal queue is empty + assert(hasLegalConfiguration()); + + monIter = _monitors.begin(); +// if (!_sendQueue || _sendQueue->isEmpty()) { + while(monIter != _monitors.end()) { + try { + (*monIter)->onStableConfiguration(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling onStableConfiguration on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling onStableConfiguration on monitors"; + } + monIter++; + } +// } + + // whenever we have a stable configuration, run the mainThread hooks with 200fps + while(_externalQueue.isEmpty() && _thread == NULL) { + runOnMainThread(200); + } + + _currEvent = _externalQueue.pop(); +#if VERBOSE + std::cout << "Received externalEvent event " << _currEvent.name << std::endl; +#endif + _currEvent.type = Event::EXTERNAL; // make sure it is set to external + if (!_running) + exitInterpreter(); + + if (_dataModel && boost::iequals(_currEvent.name, "cancel.invoke." + _sessionId)) + break; + + if (_dataModel) + try { + _dataModel.setEvent(_currEvent); + } catch (Event e) { + LOG(ERROR) << "Syntax error while setting external event:" << std::endl << e << std::endl; + } + for (unsigned int i = 0; i < _configuration.size(); i++) { + NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", _configuration[i]); + for (unsigned int j = 0; j < invokes.size(); j++) { + Arabica::DOM::Element<std::string> invokeElem = (Arabica::DOM::Element<std::string>)invokes[j]; + std::string invokeId; + if (HAS_ATTR(invokeElem, "id")) + invokeId = ATTR(invokeElem, "id"); + if (HAS_ATTR(invokeElem, "idlocation") && _dataModel) + invokeId = _dataModel.evalAsString(ATTR(invokeElem, "idlocation")); + + std::string autoForward = invokeElem.getAttribute("autoforward"); + if (boost::iequals(invokeId, _currEvent.invokeid)) { + + Arabica::XPath::NodeSet<std::string> finalizes = filterChildElements(_xmlNSPrefix + "finalize", invokeElem); + for (int k = 0; k < finalizes.size(); k++) { + Arabica::DOM::Element<std::string> finalizeElem = Arabica::DOM::Element<std::string>(finalizes[k]); + executeContent(finalizeElem); + } + + } + if (boost::iequals(autoForward, "true")) { + try { + _invokers[invokeId].send(_currEvent); + } catch(...) { + LOG(ERROR) << "Exception caught while sending event to invoker " << invokeId; + } + } + } + } + enabledTransitions = selectTransitions(_currEvent.name); + if (!enabledTransitions.empty()) + microstep(enabledTransitions); + } + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + try { + (*monIter)->beforeCompletion(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeCompletion on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeCompletion on monitors"; + } + monIter++; + } + + exitInterpreter(); + + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + try { + (*monIter)->afterCompletion(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling afterCompletion on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling afterCompletion on monitors"; + } + monIter++; + } + +} + +Arabica::XPath::NodeSet<std::string> InterpreterDraft6::selectTransitions(const std::string& event) { + Arabica::XPath::NodeSet<std::string> enabledTransitions; + + NodeSet<std::string> atomicStates; + for (unsigned int i = 0; i < _configuration.size(); i++) { + if (isAtomic(_configuration[i])) + atomicStates.push_back(_configuration[i]); + } + atomicStates.to_document_order(); + + for (unsigned int i = 0; i < atomicStates.size(); i++) { + NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>()); + + NodeSet<std::string> sortedAncestors; + sortedAncestors.push_back(atomicStates[i]); + sortedAncestors.insert(sortedAncestors.end(), ancestors.begin(), ancestors.end()); + + for (unsigned int j = 0; j < sortedAncestors.size(); j++) { + NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", sortedAncestors[j]); + for (unsigned int k = 0; k < transitions.size(); k++) { + std::string eventName; + if (HAS_ATTR(transitions[k], "event")) { + eventName = ATTR(transitions[k], "event"); + } else if(HAS_ATTR(transitions[k], "eventexpr")) { + if (_dataModel) { + eventName = _dataModel.evalAsString(ATTR(transitions[k], "eventexpr")); + } else { + LOG(ERROR) << "Transition has eventexpr attribute with no datamodel defined"; + goto LOOP; + } + } else { + goto LOOP; + } + + if (eventName.length() > 0 && + nameMatch(eventName, event) && + hasConditionMatch(transitions[k])) { + enabledTransitions.push_back(transitions[k]); + goto LOOP; + } + } + } +LOOP: + ; + } + + enabledTransitions = filterPreempted(enabledTransitions); + return enabledTransitions; +} + +Arabica::XPath::NodeSet<std::string> InterpreterDraft6::selectEventlessTransitions() { + Arabica::XPath::NodeSet<std::string> enabledTransitions; + + NodeSet<std::string> atomicStates; + for (unsigned int i = 0; i < _configuration.size(); i++) { + if (isAtomic(_configuration[i])) + atomicStates.push_back(_configuration[i]); + } + atomicStates.to_document_order(); + + for (unsigned int i = 0; i < atomicStates.size(); i++) { + NodeSet<std::string> ancestors = getProperAncestors(atomicStates[i], Arabica::DOM::Node<std::string>()); + ancestors.push_back(atomicStates[i]); + for (unsigned int j = 0; j < ancestors.size(); j++) { + NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", ancestors[j]); + for (unsigned int k = 0; k < transitions.size(); k++) { + if (!HAS_ATTR(transitions[k], "event") && hasConditionMatch(transitions[k])) { + enabledTransitions.push_back(transitions[k]); + goto LOOP; + } + } + +#if 0 + NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", ancestors[j]); + for (unsigned int k = 0; k < transitions.size(); k++) { + if (!((Arabica::DOM::Element<std::string>)transitions[k]).hasAttribute("event") && hasConditionMatch(transitions[k])) { + enabledTransitions.push_back(transitions[k]); + goto LOOP; + } + } +#endif + } +LOOP: + ; + } + + enabledTransitions = filterPreempted(enabledTransitions); + return enabledTransitions; +} + +Arabica::XPath::NodeSet<std::string> InterpreterDraft6::filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { + Arabica::XPath::NodeSet<std::string> filteredTransitions; + for (unsigned int i = 0; i < enabledTransitions.size(); i++) { + Arabica::DOM::Node<std::string> t = enabledTransitions[i]; + for (unsigned int j = i+1; j < enabledTransitions.size(); j++) { + Arabica::DOM::Node<std::string> t2 = enabledTransitions[j]; + if (isPreemptingTransition(t2, t)) { +#if VERBOSE + std::cout << "Transition preempted!: " << std::endl << t2 << std::endl << t << std::endl; +#endif + goto LOOP; + } + } + filteredTransitions.push_back(t); +LOOP: + ; + } + return filteredTransitions; +} + +bool InterpreterDraft6::isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2) { + assert(t1); + assert(t2); + +#if VERBOSE + std::cout << "Checking preemption: " << std::endl << t1 << std::endl << t2 << std::endl; +#endif + +#if 1 + if (t1 == t2) + return false; + if (isWithinSameChild(t1) && (!isTargetless(t2) && !isWithinSameChild(t2))) + return true; + if (!isTargetless(t1) && !isWithinSameChild(t1)) + return true; + return false; +#endif + +} + +void InterpreterDraft6::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { +#if 0 + std::cout << "Transitions: "; + for (int i = 0; i < enabledTransitions.size(); i++) { + std::cout << ((Arabica::DOM::Element<std::string>)getSourceState(enabledTransitions[i])).getAttribute("id") << " -> " << std::endl; + NodeSet<std::string> targetSet = getTargetStates(enabledTransitions[i]); + for (int j = 0; j < targetSet.size(); j++) { + std::cout << " " << ((Arabica::DOM::Element<std::string>)targetSet[j]).getAttribute("id") << std::endl; + } + } + std::cout << std::endl; +#endif + + exitStates(enabledTransitions); + executeContent(enabledTransitions); + enterStates(enabledTransitions); +} + +void InterpreterDraft6::exitInterpreter() { + NodeSet<std::string> statesToExit = _configuration; + statesToExit.to_document_order(); + statesToExit.reverse(); + + for (int i = 0; i < statesToExit.size(); i++) { + Arabica::XPath::NodeSet<std::string> onExitElems = filterChildElements(_xmlNSPrefix + "onexit", statesToExit[i]); + for (int j = 0; j < onExitElems.size(); j++) { + executeContent(onExitElems[j]); + } + Arabica::XPath::NodeSet<std::string> invokeElems = filterChildElements(_xmlNSPrefix + "invoke", statesToExit[i]); + for (int j = 0; j < invokeElems.size(); j++) { + cancelInvoke(invokeElems[j]); + } + if (isFinal(statesToExit[i]) && parentIsScxmlState(statesToExit[i])) { + returnDoneEvent(statesToExit[i]); + } + } + _configuration = NodeSet<std::string>(); +} + + +void InterpreterDraft6::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { + NodeSet<std::string> statesToExit; + std::set<InterpreterMonitor*>::iterator monIter; + +#if VERBOSE + std::cout << "Enabled exit transitions: " << std::endl; + for (int i = 0; i < enabledTransitions.size(); i++) { + std::cout << enabledTransitions[i] << std::endl; + } + std::cout << std::endl; +#endif + + for (int i = 0; i < enabledTransitions.size(); i++) { + Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]); + if (!isTargetless(transition)) { + std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external"); + NodeSet<std::string> tStates = getTargetStates(transition); + Arabica::DOM::Node<std::string> ancestor; + Arabica::DOM::Node<std::string> source = getSourceState(transition); + + bool allDescendants = true; + for (int j = 0; j < tStates.size(); j++) { + if (!isDescendant(tStates[j], source)) { + allDescendants = false; + break; + } + } + if (boost::iequals(transitionType, "internal") && + isCompound(source) && + allDescendants) { + ancestor = source; + } else { + NodeSet<std::string> tmpStates; + tmpStates.push_back(source); + tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end()); + +#if VERBOSE + std::cout << "tmpStates: "; + for (int i = 0; i < tmpStates.size(); i++) { + std::cout << ATTR(tmpStates[i], "id") << ", "; + } + std::cout << std::endl; +#endif + ancestor = findLCCA(tmpStates); + } + +#if VERBOSE + std::cout << "Ancestor: " << ATTR(ancestor, "id") << std::endl;; +#endif + + for (int j = 0; j < _configuration.size(); j++) { + if (isDescendant(_configuration[j], ancestor)) + statesToExit.push_back(_configuration[j]); + } + } + } + +#if VERBOSE + std::cout << "States to exit: "; + for (int i = 0; i < statesToExit.size(); i++) { + std::cout << LOCALNAME(statesToExit[i]) << ":" << ATTR(statesToExit[i], "id") << ", "; + } + std::cout << std::endl; + +#endif + + // remove statesToExit from _statesToInvoke + std::list<Arabica::DOM::Node<std::string> > tmp; + for (int i = 0; i < _statesToInvoke.size(); i++) { + if (!isMember(_statesToInvoke[i], statesToExit)) { + tmp.push_back(_statesToInvoke[i]); + } + } + _statesToInvoke = NodeSet<std::string>(); + _statesToInvoke.insert(_statesToInvoke.end(), tmp.begin(), tmp.end()); + + statesToExit.to_document_order(); + statesToExit.reverse(); + + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + try { + (*monIter)->beforeExitingStates(this, statesToExit); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeExitingStates on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeExitingStates on monitors"; + } + monIter++; + } + + for (int i = 0; i < statesToExit.size(); i++) { + NodeSet<std::string> histories = filterChildElements(_xmlNSPrefix + "history", statesToExit[i]); + for (int j = 0; j < histories.size(); j++) { + Arabica::DOM::Element<std::string> historyElem = (Arabica::DOM::Element<std::string>)histories[j]; + std::string historyType = (historyElem.hasAttribute("type") ? historyElem.getAttribute("type") : "shallow"); + NodeSet<std::string> historyNodes; + for (int k = 0; k < _configuration.size(); k++) { + if (boost::iequals(historyType, "deep")) { + if (isAtomic(_configuration[k]) && isDescendant(_configuration[k], statesToExit[i])) + historyNodes.push_back(_configuration[k]); + } else { + if (_configuration[k].getParentNode() == statesToExit[i]) + historyNodes.push_back(_configuration[k]); + } + } + _historyValue[historyElem.getAttribute("id")] = historyNodes; +#if 0 + std::cout << "History node " << ATTR(historyElem, "id") << " contains: "; + for (int i = 0; i < historyNodes.size(); i++) { + std::cout << ATTR(historyNodes[i], "id") << ", "; + } + std::cout << std::endl; + +#endif + + } + } + + for (int i = 0; i < statesToExit.size(); i++) { + NodeSet<std::string> onExits = filterChildElements(_xmlNSPrefix + "onExit", statesToExit[i]); + for (int j = 0; j < onExits.size(); j++) { + Arabica::DOM::Element<std::string> onExitElem = (Arabica::DOM::Element<std::string>)onExits[j]; + executeContent(onExitElem); + } + NodeSet<std::string> invokes = filterChildElements(_xmlNSPrefix + "invoke", statesToExit[i]); + for (int j = 0; j < invokes.size(); j++) { + Arabica::DOM::Element<std::string> invokeElem = (Arabica::DOM::Element<std::string>)invokes[j]; + cancelInvoke(invokeElem); + } + } + + // remove statesToExit from _configuration + tmp.clear(); + for (int i = 0; i < _configuration.size(); i++) { + if (!isMember(_configuration[i], statesToExit)) { + tmp.push_back(_configuration[i]); + } + } + _configuration = NodeSet<std::string>(); + _configuration.insert(_configuration.end(), tmp.begin(), tmp.end()); + + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + try { + (*monIter)->afterExitingStates(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling afterExitingStates on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling afterExitingStates on monitors"; + } + monIter++; + } + +} + +void InterpreterDraft6::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { + NodeSet<std::string> statesToEnter; + NodeSet<std::string> statesForDefaultEntry; + std::set<InterpreterMonitor*>::iterator monIter; + +#if VERBOSE + std::cout << "Enabled enter transitions: " << std::endl; + for (int i = 0; i < enabledTransitions.size(); i++) { + std::cout << enabledTransitions[i] << std::endl; + } + std::cout << std::endl; +#endif + + for (int i = 0; i < enabledTransitions.size(); i++) { + Arabica::DOM::Element<std::string> transition = ((Arabica::DOM::Element<std::string>)enabledTransitions[i]); + if (!isTargetless(transition)) { + std::string transitionType = (boost::iequals(transition.getAttribute("type"), "internal") ? "internal" : "external"); + NodeSet<std::string> tStates = getTargetStates(transition); + +#if VERBOSE + std::cout << "Target States: "; + for (int i = 0; i < tStates.size(); i++) { + std::cout << ATTR(tStates[i], "id") << ", "; + } + std::cout << std::endl; +#endif + + Arabica::DOM::Node<std::string> ancestor; + Arabica::DOM::Node<std::string> source = getSourceState(transition); +#if VERBOSE + std::cout << "Source States: " << ATTR(source, "id") << std::endl; +#endif + assert(source); + + bool allDescendants = true; + for (int j = 0; j < tStates.size(); j++) { + if (!isDescendant(tStates[j], source)) { + allDescendants = false; + break; + } + } + if (boost::iequals(transitionType, "internal") && + isCompound(source) && + allDescendants) { + ancestor = source; + } else { + NodeSet<std::string> tmpStates; + tmpStates.push_back(source); + tmpStates.insert(tmpStates.end(), tStates.begin(), tStates.end()); + + ancestor = findLCCA(tmpStates); + } + +#if VERBOSE + std::cout << "Ancestor: " << ATTR(ancestor, "id") << std::endl; +#endif + + for (int j = 0; j < tStates.size(); j++) { + addStatesToEnter(tStates[j], statesToEnter, statesForDefaultEntry); + } + +#if VERBOSE + std::cout << "States to enter: "; + for (int i = 0; i < statesToEnter.size(); i++) { + std::cout << LOCALNAME(statesToEnter[i]) << ":" << ATTR(statesToEnter[i], "id") << ", "; + } + std::cout << std::endl; +#endif + + for (int j = 0; j < tStates.size(); j++) { + NodeSet<std::string> ancestors = getProperAncestors(tStates[j], ancestor); + +#if VERBOSE + std::cout << "Proper Ancestors of " << ATTR(tStates[j], "id") << " and " << ATTR(ancestor, "id") << ": "; + for (int i = 0; i < ancestors.size(); i++) { + std::cout << ATTR(ancestors[i], "id") << ", "; + } + std::cout << std::endl; +#endif + + for (int k = 0; k < ancestors.size(); k++) { + statesToEnter.push_back(ancestors[k]); + if(isParallel(ancestors[k])) { + NodeSet<std::string> childs = getChildStates(ancestors[k]); + for (int l = 0; l < childs.size(); l++) { + bool someIsDescendant = false; + for (int m = 0; m < statesToEnter.size(); m++) { + if (isDescendant(statesToEnter[m], childs[l])) { + someIsDescendant = true; + break; + } + } + if (!someIsDescendant) { + addStatesToEnter(childs[l], statesToEnter, statesForDefaultEntry); + } + } + } + } + } + } + } + statesToEnter.to_document_order(); + + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + try { + (*monIter)->beforeEnteringStates(this, statesToEnter); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling beforeEnteringStates on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling beforeEnteringStates on monitors"; + } + monIter++; + } + + for (int i = 0; i < statesToEnter.size(); i++) { + Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)statesToEnter[i]; + _configuration.push_back(stateElem); + _statesToInvoke.push_back(stateElem); + if (_binding == LATE && stateElem.getAttribute("isFirstEntry").size() > 0) { + NodeSet<std::string> dataModelElems = filterChildElements(_xmlNSPrefix + "datamodel", stateElem); + if(dataModelElems.size() > 0 && _dataModel) { + Arabica::XPath::NodeSet<std::string> dataElems = filterChildElements(_xmlNSPrefix + "data", dataModelElems[0]); + for (int j = 0; j < dataElems.size(); j++) { + initializeData(dataElems[j]); + } + } + stateElem.setAttribute("isFirstEntry", ""); + } + // execute onentry executable content + NodeSet<std::string> onEntryElems = filterChildElements(_xmlNSPrefix + "onEntry", stateElem); + executeContent(onEntryElems); + + if (isMember(stateElem, statesForDefaultEntry)) { + // execute initial transition content for compund states + Arabica::XPath::NodeSet<std::string> transitions = _xpath.evaluate("" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", stateElem).asNodeSet(); + for (int j = 0; j < transitions.size(); j++) { + executeContent(transitions[j]); + } + } + + if (isFinal(stateElem)) { + internalDoneSend(stateElem); + Arabica::DOM::Element<std::string> parent = (Arabica::DOM::Element<std::string>)stateElem.getParentNode(); + + if (isParallel(parent.getParentNode())) { + Arabica::DOM::Element<std::string> grandParent = (Arabica::DOM::Element<std::string>)parent.getParentNode(); + + Arabica::XPath::NodeSet<std::string> childs = getChildStates(grandParent); + bool inFinalState = true; + for (int j = 0; j < childs.size(); j++) { + if (!isInFinalState(childs[j])) { + inFinalState = false; + break; + } + } + if (inFinalState) { + internalDoneSend(parent); + } + } + } + } + for (int i = 0; i < _configuration.size(); i++) { + Arabica::DOM::Element<std::string> stateElem = (Arabica::DOM::Element<std::string>)_configuration[i]; + if (isFinal(stateElem) && parentIsScxmlState(stateElem)) { + _running = false; + _done = true; + } + } + + monIter = _monitors.begin(); + while(monIter != _monitors.end()) { + try { + (*monIter)->afterEnteringStates(this); + } catch (Event e) { + LOG(ERROR) << "Syntax error when calling afterEnteringStates on monitors: " << std::endl << e << std::endl; + } catch (...) { + LOG(ERROR) << "An exception occured when calling afterEnteringStates on monitors"; + } + monIter++; + } + +} + +void InterpreterDraft6::addStatesToEnter(const Arabica::DOM::Node<std::string>& state, + Arabica::XPath::NodeSet<std::string>& statesToEnter, + Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { + std::string stateId = ((Arabica::DOM::Element<std::string>)state).getAttribute("id"); + +#if VERBOSE + std::cout << "Adding state to enter: " << stateId << std::endl; +#endif + if (isHistory(state)) { + if (_historyValue.find(stateId) != _historyValue.end()) { + Arabica::XPath::NodeSet<std::string> historyValue = _historyValue[stateId]; + +#if VERBOSE + std::cout << "History State " << ATTR(state, "id") << ": "; + for (int i = 0; i < historyValue.size(); i++) { + std::cout << ATTR(historyValue[i], "id") << ", "; + } + std::cout << std::endl; +#endif + + for (int i = 0; i < historyValue.size(); i++) { + addStatesToEnter(historyValue[i], statesToEnter, statesForDefaultEntry); + NodeSet<std::string> ancestors = getProperAncestors(historyValue[i], state); + +#if VERBOSE + std::cout << "Proper Ancestors: "; + for (int i = 0; i < ancestors.size(); i++) { + std::cout << ATTR(ancestors[i], "id") << ", "; + } + std::cout << std::endl; +#endif + + for (int j = 0; j < ancestors.size(); j++) { + statesToEnter.push_back(ancestors[j]); + } + } + } else { + NodeSet<std::string> transitions = filterChildElements(_xmlNSPrefix + "transition", state); + for (int i = 0; i < transitions.size(); i++) { + NodeSet<std::string> targets = getTargetStates(transitions[i]); + for (int j = 0; j < targets.size(); j++) { + addStatesToEnter(targets[j], statesToEnter, statesForDefaultEntry); + + // Modifications from chris nuernberger + NodeSet<std::string> ancestors = getProperAncestors(targets[j], state); + for (int k = 0; k < ancestors.size(); k++) { + statesToEnter.push_back(ancestors[k]); + } + } + } + } + } else { + statesToEnter.push_back(state); + if (isCompound(state)) { + statesForDefaultEntry.push_back(state); + + NodeSet<std::string> tStates = getInitialStates(state); + for (int i = 0; i < tStates.size(); i++) { + addStatesToEnter(tStates[i], statesToEnter, statesForDefaultEntry); + } + + // addStatesToEnter(getInitialState(state), statesToEnter, statesForDefaultEntry); + // NodeSet<std::string> tStates = getTargetStates(getInitialState(state)); + + } else if(isParallel(state)) { + NodeSet<std::string> childStates = getChildStates(state); + for (int i = 0; i < childStates.size(); i++) { + addStatesToEnter(childStates[i], statesToEnter, statesForDefaultEntry); + } + } + } +} + + +}
\ No newline at end of file diff --git a/src/uscxml/interpreter/InterpreterDraft6.h b/src/uscxml/interpreter/InterpreterDraft6.h new file mode 100644 index 0000000..10ff10a --- /dev/null +++ b/src/uscxml/interpreter/InterpreterDraft6.h @@ -0,0 +1,31 @@ +#ifndef INTERPRETERDRAFT6_H_JAXK9FE1 +#define INTERPRETERDRAFT6_H_JAXK9FE1 + +#include "uscxml/Interpreter.h" + +namespace uscxml { + +class InterpreterDraft6 : public Interpreter { + void interpret(); + void mainEventLoop(); + + void initializeData(const Arabica::DOM::Node<std::string>& data); + void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + void addStatesToEnter(const Arabica::DOM::Node<std::string>& state, + Arabica::XPath::NodeSet<std::string>& statesToEnter, + Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + + void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + void exitInterpreter(); + + Arabica::XPath::NodeSet<std::string> selectEventlessTransitions(); + Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event); + Arabica::XPath::NodeSet<std::string> filterPreempted(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + bool isPreemptingTransition(const Arabica::DOM::Node<std::string>& t1, const Arabica::DOM::Node<std::string>& t2); + +}; + +} + +#endif /* end of include guard: INTERPRETERDRAFT6_H_JAXK9FE1 */ diff --git a/src/uscxml/interpreter/InterpreterDraft7.cpp b/src/uscxml/interpreter/InterpreterDraft7.cpp new file mode 100644 index 0000000..9542146 --- /dev/null +++ b/src/uscxml/interpreter/InterpreterDraft7.cpp @@ -0,0 +1,439 @@ +#include "InterpreterDraft7.h" + +#include <glog/logging.h> + +namespace uscxml { + +using namespace Arabica::XPath; +using namespace Arabica::DOM; + +/** +procedure interpret(doc): + if not valid(doc): failWithError() + expandScxmlSource(doc) + configuration = new OrderedSet() + statesToInvoke = new OrderedSet() + internalQueue = new Queue() + externalQueue = new BlockingQueue() + historyValue = new HashTable() + datamodel = new Datamodel(doc) + if doc.binding == "early": + initializeDatamodel(datamodel, doc) + running = true + executeGlobalScriptElement(doc) + enterStates([doc.initial.transition]) + mainEventLoop() + */ +void InterpreterDraft7::interpret() { + if (!_isInitialized) + init(); + + if (!_scxml) + return; + + _sessionId = getUUID(); + + std::string datamodelName; + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "datamodel")) + datamodelName = ATTR(_scxml, "datamodel"); + if (datamodelName.length() == 0 && HAS_ATTR(_scxml, "profile")) // SCION SCXML uses profile to specify datamodel + datamodelName = ATTR(_scxml, "profile"); + if(datamodelName.length() > 0) + _dataModel = Factory::createDataModel(datamodelName, this); + if(datamodelName.length() > 0 && !_dataModel) { + LOG(ERROR) << "No datamodel for " << datamodelName << " registered"; + } + + if (_dataModel) { + _dataModel.assign("_x.args", _cmdLineOptions); + if (_httpServlet) { + Data data; + data.compound["location"] = Data(_httpServlet->getURL(), Data::VERBATIM); + _dataModel.assign("_ioprocessors['http']", data); + } + } + + setupIOProcessors(); + + _running = true; + _binding = (HAS_ATTR(_scxml, "binding") && boost::iequals(ATTR(_scxml, "binding"), "late") ? LATE : EARLY); + + // @TODO: Reread http://www.w3.org/TR/scxml/#DataBinding + + if (_dataModel && _binding == EARLY) { + // initialize all data elements + NodeSet<std::string> dataElems = _xpath.evaluate("//" + _xpathPrefix + "data", _document).asNodeSet(); + for (unsigned int i = 0; i < dataElems.size(); i++) { + initializeData(dataElems[i]); + } + } else if(_dataModel) { + // initialize current data elements + NodeSet<std::string> topDataElems = filterChildElements(_xmlNSPrefix + "data", filterChildElements(_xmlNSPrefix + "datamodel", _scxml)); + for (unsigned int i = 0; i < topDataElems.size(); i++) { + initializeData(topDataElems[i]); + } + } + + // executeGlobalScriptElements + NodeSet<std::string> globalScriptElems = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "script", _document).asNodeSet(); + for (unsigned int i = 0; i < globalScriptElems.size(); i++) { + if (_dataModel) + executeContent(globalScriptElems[i]); + } + + // initial transition might be implict + NodeSet<std::string> initialTransitions = _xpath.evaluate("/" + _xpathPrefix + "scxml/" + _xpathPrefix + "initial/" + _xpathPrefix + "transition", _document).asNodeSet(); + if (initialTransitions.size() == 0) { + Arabica::XPath::NodeSet<std::string> initialStates = getInitialStates(); + + for (int i = 0; i < initialStates.size(); i++) { + Arabica::DOM::Element<std::string> initialElem = _document.createElementNS(_nsURL, "initial"); + initialElem.setAttribute("generated", "true"); + Arabica::DOM::Element<std::string> transitionElem = _document.createElementNS(_nsURL, "transition"); + transitionElem.setAttribute("target", ATTR(initialStates[i], "id")); + initialElem.appendChild(transitionElem); + _scxml.appendChild(initialElem); + initialTransitions.push_back(transitionElem); + } + } + enterStates(initialTransitions); + + // assert(hasLegalConfiguration()); + mainEventLoop(); + + if (_parentQueue) { + // send one final event to unblock eventual listeners + Event quit; + quit.name = "done.state.scxml"; + _parentQueue->push(quit); + } + + // set datamodel to null from this thread + if(_dataModel) + _dataModel = DataModel(); + +} + +/** + * Called with a single data element from the topmost datamodel element. + */ +void InterpreterDraft7::initializeData(const Arabica::DOM::Node<std::string>& data) { + if (!_dataModel) { + LOG(ERROR) << "Cannot initialize data when no datamodel is given!"; + return; + } + try { + if (!HAS_ATTR(data, "id")) { + LOG(ERROR) << "Data element has no id!"; + return; + } + + if (HAS_ATTR(data, "expr")) { + std::string value = ATTR(data, "expr"); + _dataModel.assign(ATTR(data, "id"), value); + } else if (HAS_ATTR(data, "src")) { + URL srcURL(ATTR(data, "src")); + if (!srcURL.isAbsolute()) + toAbsoluteURI(srcURL); + + std::stringstream ss; + if (_cachedURLs.find(srcURL.asString()) != _cachedURLs.end()) { + ss << _cachedURLs[srcURL.asString()]; + } else { + ss << srcURL; + _cachedURLs[srcURL.asString()] = srcURL; + } + _dataModel.assign(ATTR(data, "id"), ss.str()); + + } else if (data.hasChildNodes()) { + // search for the text node with the actual script + NodeList<std::string> dataChilds = data.getChildNodes(); + for (int i = 0; i < dataChilds.getLength(); i++) { + if (dataChilds.item(i).getNodeType() == Node_base::TEXT_NODE) { + Data value = Data(dataChilds.item(i).getNodeValue()); + _dataModel.assign(ATTR(data, "id"), value); + break; + } + } + } + + } catch (Event e) { + LOG(ERROR) << "Syntax error in data element:" << std::endl << e << std::endl; + } +} + +/** +procedure mainEventLoop(): + while running: + enabledTransitions = null + macrostepDone = false + # Here we handle eventless transitions and transitions + # triggered by internal events until macrostep is complete + while running and not macrostepDone: + enabledTransitions = selectEventlessTransitions() + if enabledTransitions.isEmpty(): + if internalQueue.isEmpty(): + macrostepDone = true + else: + internalEvent = internalQueue.dequeue() + datamodel["_event"] = internalEvent + enabledTransitions = selectTransitions(internalEvent) + if not enabledTransitions.isEmpty(): + microstep(enabledTransitions.toList()) + # either we're in a final state, and we break out of the loop + if not running: + break; + # or we've completed a macrostep, so we start a new macrostep by waiting for an external event + # Here we invoke whatever needs to be invoked. The implementation of 'invoke' is platform-specific + for state in statesToInvoke: + for inv in state.invoke: + invoke(inv) + statesToInvoke.clear() + # Invoking may have raised internal error events and we iterate to handle them + if not internalQueue.isEmpty(): + continue + # A blocking wait for an external event. Alternatively, if we have been invoked + # our parent session also might cancel us. The mechanism for this is platform specific, + # but here we assume it’s a special event we receive + externalEvent = externalQueue.dequeue() + if isCancelEvent(externalEvent): + running = false + continue + datamodel["_event"] = externalEvent + for state in configuration: + for inv in state.invoke: + if inv.invokeid == externalEvent.invokeid: + applyFinalize(inv, externalEvent) + if inv.autoforward: + send(inv.id, externalEvent) + enabledTransitions = selectTransitions(externalEvent) + if not enabledTransitions.isEmpty(): + microstep(enabledTransitions.toList()) + # End of outer while running loop. If we get here, we have reached a top-level final state or have been cancelled + exitInterpreter() + */ +void InterpreterDraft7::mainEventLoop() { +} + +/** +procedure exitInterpreter(): + statesToExit = configuration.toList().sort(exitOrder) + for s in statesToExit: + for content in s.onexit: + executeContent(content) + for inv in s.invoke: + cancelInvoke(inv) + configuration.delete(s) + if isFinalState(s) and isScxmlState(s.parent): + returnDoneEvent(s.donedata) + */ +void InterpreterDraft7::exitInterpreter() { +} + +/** +procedure microstep(enabledTransitions): + exitStates(enabledTransitions) + executeTransitionContent(enabledTransitions) + enterStates(enabledTransitions) + */ +void InterpreterDraft7::microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { +} + +/** +function selectEventlessTransitions(): + enabledTransitions = new OrderedSet() + atomicStates = configuration.toList().filter(isAtomicState).sort(documentOrder) + for state in atomicStates: + loop: for s in [state].append(getProperAncestors(state, null)): + for t in s.transition: + if not t.event and conditionMatch(t): + enabledTransitions.add(t) + break loop + enabledTransitions = filterPreempted(enabledTransitions) + return enabledTransitions + */ +Arabica::XPath::NodeSet<std::string> InterpreterDraft7::selectEventlessTransitions() { +} + +/** +function selectTransitions(event): + enabledTransitions = new OrderedSet() + atomicStates = configuration.toList().filter(isAtomicState).sort(documentOrder) + for state in atomicStates: + loop: for s in [state].append(getProperAncestors(state, null)): + for t in s.transition: + if t.event and nameMatch(t.event, event.name) and conditionMatch(t): + enabledTransitions.add(t) + break loop + enabledTransitions = removeConflictingTransitions(enabledTransitions) + return enabledTransitions + */ +Arabica::XPath::NodeSet<std::string> InterpreterDraft7::selectTransitions(const std::string& event) { +} + +/** +procedure enterStates(enabledTransitions): + statesToEnter = new OrderedSet() + statesForDefaultEntry = new OrderedSet() + computeEntrySet(enabledTransitions, statesToEnter, statesForDefaultEntry) + for s in statesToEnter.toList().sort(entryOrder): + configuration.add(s) + statesToInvoke.add(s) + if binding == "late" and s.isFirstEntry: + initializeDataModel(datamodel.s,doc.s) + s.isFirstEntry = false + for content in s.onentry: + executeContent(content) + if statesForDefaultEntry.isMember(s): + executeContent(s.initial.transition) + if isFinalState(s): + if isSCXMLElement(s.parent): + running = false + else: + parent = s.parent + grandparent = parent.parent + internalQueue.enqueue(new Event("done.state." + parent.id, s.donedata)) + if isParallelState(grandparent): + if getChildStates(grandparent).every(isInFinalState): + internalQueue.enqueue(new Event("done.state." + grandparent.id)) + */ +void InterpreterDraft7::enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { +} + +/** +procedure exitStates(enabledTransitions): + statesToExit = computeExitSet(enabledTransitions) + for s in statesToExit: + statesToInvoke.delete(s) + statesToExit = statesToExit.toList().sort(exitOrder) + for s in statesToExit: + for h in s.history: + if h.type == "deep": + f = lambda s0: isAtomicState(s0) and isDescendant(s0,s) + else: + f = lambda s0: s0.parent == s + historyValue[h.id] = configuration.toList().filter(f) + for s in statesToExit: + for content in s.onexit: + executeContent(content) + for inv in s.invoke: + cancelInvoke(inv) + configuration.delete(s) + */ +void InterpreterDraft7::exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { +} + +/** +function computeExitSet(transitions) + statesToExit = new OrderedSet + for t in transitions: + domain = getTransitionDomain(t) + for s in configuration: + if isDescendant(s,domain): + statesToExit.add(s) + return statesToExit + */ +Arabica::XPath::NodeSet<std::string> InterpreterDraft7::computeExitSet(const Arabica::XPath::NodeSet<std::string>& enabledTransitions, + const Arabica::XPath::NodeSet<std::string>& statesToExit) { +} + +/** + procedure computeEntrySet(transitions, statesToEnter, statesForDefaultEntry) + for t in transitions: + statesToEnter.union(getTargetStates(t.target)) + for s in statesToEnter: + addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry) + for t in transitions: + ancestor = getTransitionDomain(t) + for s in getTargetStates(t.target)): + addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry) + */ +Arabica::XPath::NodeSet<std::string> InterpreterDraft7::computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions, + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { +} + +/** +function removeConflictingTransitions(enabledTransitions): + filteredTransitions = new OrderedSet() + // toList sorts the transitions in the order of the states that selected them + for t1 in enabledTransitions.toList(): + t1Preempted = false; + transitionsToRemove = new OrderedSet() + for t2 in filteredTransitions.toList(): + if computeExitSet(t1).hasIntersection(computeExitSet(t2)): + if isDescendent(t1.source, t2.source): + transitionsToRemove.add(t2) + else: + t1Preempted = true + break + if not t1Preempted: + for t3 in transitionsToRemove.toList(): + filteredTransitions.delete(t3) + filteredTransitions.add(t1) + + return filteredTransitions + */ +Arabica::XPath::NodeSet<std::string> InterpreterDraft7::removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions) { +} + +/** + function getTransitionDomain(t) + tstates = getTargetStates(t.target) + if not tstates + return t.source + elif t.type == "internal" and isCompoundState(t.source) and tstates.every(lambda s: isDescendant(s,t.source)): + return t.source + else: + return findLCCA([t.source].append(tstates)) + */ +Arabica::DOM::Node<std::string> InterpreterDraft7::getTransitionDomain(const Arabica::DOM::Node<std::string>& transition) { +} + +/** + procedure addDescendentStatesToEnter(state,statesToEnter,statesForDefaultEntry): + if isHistoryState(state): + if historyValue[state.id]: + for s in historyValue[state.id]: + addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry) + addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry) + else: + for t in state.transition: + for s in getTargetStates(t.target): + addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry) + addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry) + else: + statesToEnter.add(state) + if isCompoundState(state): + statesForDefaultEntry.add(state) + for s in getTargetStates(state.initial): + addDescendentStatesToEnter(s,statesToEnter,statesForDefaultEntry) + addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry) + else: + if isParallelState(state): + for child in getChildStates(state): + if not statesToEnter.some(lambda s: isDescendant(s,child)): + addDescendentStatesToEnter(child,statesToEnter,statesForDefaultEntry) + */ +Arabica::XPath::NodeSet<std::string> InterpreterDraft7::addDescendentStatesToEnter(const Arabica::DOM::Node<std::string>& state, + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { +} + +/** + procedure addAncestorsToEnter(state, ancestor, statesToEnter, statesForDefaultEntry) + for anc in getProperAncestors(state,ancestor): + statesToEnter.add(anc) + if isParallelState(anc): + for child in getChildStates(anc): + if not statesToEnter.some(lambda s: isDescendant(s,child)): + addStatesToEnter(child,statesToEnter,statesForDefaultEntry) + */ +Arabica::XPath::NodeSet<std::string> InterpreterDraft7::addAncestorsStatesToEnter(const Arabica::DOM::Node<std::string>& state, + const Arabica::DOM::Node<std::string>& ancestor, + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry) { +} + + +}
\ No newline at end of file diff --git a/src/uscxml/interpreter/InterpreterDraft7.h b/src/uscxml/interpreter/InterpreterDraft7.h new file mode 100644 index 0000000..e784e84 --- /dev/null +++ b/src/uscxml/interpreter/InterpreterDraft7.h @@ -0,0 +1,46 @@ +#ifndef INTERPRETERDRAFT7_H_WLJEI019 +#define INTERPRETERDRAFT7_H_WLJEI019 + +#include "uscxml/Interpreter.h" + +namespace uscxml { + +class InterpreterDraft7 : public Interpreter { + void interpret(); + void mainEventLoop(); + void exitInterpreter(); + + void microstep(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + + Arabica::XPath::NodeSet<std::string> selectEventlessTransitions(); + Arabica::XPath::NodeSet<std::string> selectTransitions(const std::string& event); + + void enterStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + void exitStates(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + + Arabica::XPath::NodeSet<std::string> computeExitSet(const Arabica::XPath::NodeSet<std::string>& enabledTransitions, + const Arabica::XPath::NodeSet<std::string>& statesToExit); + + Arabica::XPath::NodeSet<std::string> computeEntrySet(const Arabica::XPath::NodeSet<std::string>& transitions, + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + + Arabica::XPath::NodeSet<std::string> removeConflictingTransitions(const Arabica::XPath::NodeSet<std::string>& enabledTransitions); + Arabica::DOM::Node<std::string> getTransitionDomain(const Arabica::DOM::Node<std::string>& transition); + + Arabica::XPath::NodeSet<std::string> addDescendentStatesToEnter(const Arabica::DOM::Node<std::string>& state, + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + + Arabica::XPath::NodeSet<std::string> addAncestorsStatesToEnter(const Arabica::DOM::Node<std::string>& state, + const Arabica::DOM::Node<std::string>& ancestor, + const Arabica::XPath::NodeSet<std::string>& statesToEnter, + const Arabica::XPath::NodeSet<std::string>& statesForDefaultEntry); + + void initializeData(const Arabica::DOM::Node<std::string>& data); + +}; + +} + +#endif /* end of include guard: INTERPRETERDRAFT7_H_WLJEI019 */ |