Skip to content
This repository has been archived by the owner on Sep 25, 2018. It is now read-only.

Commit

Permalink
Merge pull request #13 from mattoshry/system_vars
Browse files Browse the repository at this point in the history
add _ioprocessors and _x described in 5.10 of SCXML
  • Loading branch information
jbeard4 committed Jan 22, 2013
2 parents ccf9af2 + 62957d4 commit 46a77d0
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 62 deletions.
62 changes: 33 additions & 29 deletions lib/core/scxml/SCXML.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ function SCXMLInterpreter(model, opts){
this.opts.priorityComparisonFn = this.opts.priorityComparisonFn || getTransitionWithHigherSourceChildPriority(this.opts.model);

this._sessionid = this.opts.sessionid || "";
this._platformvars = this.opts.platformvars || {};
this._ioprocessors = this.opts.ioprocessors || {};

this._configuration = new this.opts.BasicStateSet();
this._historyValue = {};
Expand All @@ -76,13 +78,13 @@ SCXMLInterpreter.prototype = {
//the require of the module importing SCION
//the require of the main module
//this module's require
var actionCodeRequire =
this.opts.require ||
(module.parent &&
module.parent.parent &&
var actionCodeRequire =
this.opts.require ||
(module.parent &&
module.parent.parent &&
module.parent.parent.require &&
module.parent.parent.require.bind(module.parent.parent)) ||
(require.main &&
module.parent.parent.require.bind(module.parent.parent)) ||
(require.main &&
require.main.require &&
require.main.require.bind(require.main)) ||
require;
Expand All @@ -96,7 +98,9 @@ SCXMLInterpreter.prototype = {
this.isIn.bind(this),
actionCodeRequire,
pm.platform.parseDocumentFromString,
this._sessionid);
this._sessionid,
this._ioprocessors,
this._platformvars);
this._actions = tmp.actions;
this._datamodel = tmp.datamodel;

Expand Down Expand Up @@ -175,12 +179,12 @@ SCXMLInterpreter.prototype = {
//filter out targetless transitions here - we will only use these to execute transition actions
var selectedTransitionsWithTargets = new this.opts.TransitionSet(selectedTransitions.iter().filter(function(t){return t.targets;}));

var exitedTuple = this._getStatesExited(selectedTransitionsWithTargets),
basicStatesExited = exitedTuple[0],
var exitedTuple = this._getStatesExited(selectedTransitionsWithTargets),
basicStatesExited = exitedTuple[0],
statesExited = exitedTuple[1];

var enteredTuple = this._getStatesEntered(selectedTransitionsWithTargets),
basicStatesEntered = enteredTuple[0],
var enteredTuple = this._getStatesEntered(selectedTransitionsWithTargets),
basicStatesEntered = enteredTuple[0],
statesEntered = enteredTuple[1];

if (printTrace) pm.platform.log("basicStatesExited ", basicStatesExited);
Expand All @@ -199,7 +203,7 @@ SCXMLInterpreter.prototype = {

//invoke listeners
this._listeners.forEach(function(l){
if(l.onExit) l.onExit(state.id);
if(l.onExit) l.onExit(state.id);
});

if(state.onexit !== undefined) this._evaluateAction(state.onexit,eventSet, datamodelForNextStep, eventsToAddToInnerQueue);
Expand Down Expand Up @@ -235,20 +239,20 @@ SCXMLInterpreter.prototype = {
var targetIds = transition.targets && transition.targets.map(function(target){return target.id;});

this._listeners.forEach(function(l){
if(l.onTransition) l.onTransition(transition.source.id,targetIds);
if(l.onTransition) l.onTransition(transition.source.id,targetIds);
});

if(transition.actions !== undefined) this._evaluateAction(transition.actions,eventSet, datamodelForNextStep, eventsToAddToInnerQueue);
},this);

if (printTrace) pm.platform.log("executing state enter actions");

statesEntered.forEach(function(state){

if (printTrace || this.opts.logStatesEnteredAndExited) pm.platform.log("entering", state.id);

this._listeners.forEach(function(l){
if(l.onEntry) l.onEntry(state.id);
if(l.onEntry) l.onEntry(state.id);
});

if(state.onentry !== undefined) this._evaluateAction(state.onentry, eventSet, datamodelForNextStep, eventsToAddToInnerQueue);
Expand All @@ -262,15 +266,15 @@ SCXMLInterpreter.prototype = {
this._configuration.union(basicStatesEntered);

if (printTrace) pm.platform.log("new configuration ", this._configuration);

//add set of generated events to the innerEventQueue -> Event Lifelines: Next small-step
if (!eventsToAddToInnerQueue.isEmpty()) {
if (printTrace) pm.platform.log("adding triggered events to inner queue ", eventsToAddToInnerQueue);
this._innerEventQueue.push(eventsToAddToInnerQueue);
}

if (printTrace) pm.platform.log("updating datamodel for next small step :");

//update the datamodel
for (var key in datamodelForNextStep) {
this._setData(key,datamodelForNextStep[key]);
Expand Down Expand Up @@ -310,7 +314,7 @@ SCXMLInterpreter.prototype = {
//States exited are defined to be active states that are
//descendants of the scope of each priority-enabled transition.
//Here, we iterate through the transitions, and collect states
//that match this condition.
//that match this condition.
transitions.iter().forEach(function(transition){
var lca = transition.lca,
scope = transition.scope,
Expand Down Expand Up @@ -360,7 +364,7 @@ SCXMLInterpreter.prototype = {
}else{
//everything else can just be passed through as normal
processState(s);
}
}
});
}).bind(this);

Expand All @@ -384,13 +388,13 @@ SCXMLInterpreter.prototype = {
statesToProcess.push.apply(statesToProcess,
s.children.filter(function(s){return s.kind !== stateKinds.HISTORY;}));
} else if (s.kind === stateKinds.COMPOSITE) {
statesToProcess.push(s.initial);
statesToProcess.push(s.initial);
} else if (s.kind === stateKinds.INITIAL || s.kind === stateKinds.BASIC || s.kind === stateKinds.FINAL) {
basicStatesToEnter.add(s);
}
}

statesProcessed.add(s);
statesProcessed.add(s);

}).bind(this);

Expand Down Expand Up @@ -426,7 +430,7 @@ SCXMLInterpreter.prototype = {

//get full configuration, unordered
//this means we may select transitions from parents before children

this._configuration.iter().forEach(function(basicState){
statesAndParents.add(basicState);
this.opts.model.getAncestors(basicState).forEach(function(ancestor){
Expand Down Expand Up @@ -457,16 +461,16 @@ SCXMLInterpreter.prototype = {
var priorityEnabledTransitions = this._selectPriorityEnabledTransitions(enabledTransitions);

if (printTrace) pm.platform.log("priorityEnabledTransitions", priorityEnabledTransitions);

return priorityEnabledTransitions;
},

/** @private */
_selectPriorityEnabledTransitions : function(enabledTransitions) {
var priorityEnabledTransitions = new this.opts.TransitionSet();

var tuple = this._getInconsistentTransitions(enabledTransitions),
consistentTransitions = tuple[0],
var tuple = this._getInconsistentTransitions(enabledTransitions),
consistentTransitions = tuple[0],
inconsistentTransitionsPairs = tuple[1];

priorityEnabledTransitions.union(consistentTransitions);
Expand All @@ -475,13 +479,13 @@ SCXMLInterpreter.prototype = {
if (printTrace) pm.platform.log("consistentTransitions", consistentTransitions);
if (printTrace) pm.platform.log("inconsistentTransitionsPairs", inconsistentTransitionsPairs);
if (printTrace) pm.platform.log("priorityEnabledTransitions", priorityEnabledTransitions);

while (!inconsistentTransitionsPairs.isEmpty()) {
enabledTransitions = new this.opts.TransitionSet(
inconsistentTransitionsPairs.iter().map(function(t){return this.opts.priorityComparisonFn(t);},this));

tuple = this._getInconsistentTransitions(enabledTransitions);
consistentTransitions = tuple[0];
consistentTransitions = tuple[0];
inconsistentTransitionsPairs = tuple[1];

priorityEnabledTransitions.union(consistentTransitions);
Expand All @@ -490,7 +494,7 @@ SCXMLInterpreter.prototype = {
if (printTrace) pm.platform.log("consistentTransitions", consistentTransitions);
if (printTrace) pm.platform.log("inconsistentTransitionsPairs", inconsistentTransitionsPairs);
if (printTrace) pm.platform.log("priorityEnabledTransitions", priorityEnabledTransitions);

}
return priorityEnabledTransitions;
},
Expand Down Expand Up @@ -541,7 +545,7 @@ SCXMLInterpreter.prototype = {
registerListener provides a generic mechanism to subscribe to state change notifications.
Can be used for logging and debugging. For example, can attache a logger that simply logs the state changes.
Or can attach a network debugging client that sends state change notifications to a debugging server.
listener is of the form:
{
onEntry : function(stateId){},
Expand Down
66 changes: 33 additions & 33 deletions lib/core/util/code-gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function actionTagToFnBody(action){

if(!(generator && generatorFn)) throw new Error("Element " + pm.platform.dom.namespaceURI(action) + ':' + pm.platform.dom.localName(action) + " not yet supported");

return generatorFn(action);
return generatorFn(action);
}

var actionTags = {
Expand All @@ -55,7 +55,7 @@ var actionTags = {
for(var i = 0; i < childNodes.length; i++){
var child = childNodes[i];

if(pm.platform.dom.localName(child) === "elseif" || pm.platform.dom.localName(child) === "else"){
if(pm.platform.dom.localName(child) === "elseif" || pm.platform.dom.localName(child) === "else"){
break;
}else{
s += actionTagToFnBody(child) + "\n;;\n";
Expand Down Expand Up @@ -118,26 +118,26 @@ var actionTags = {

"send" : function(action){
var target = (pm.platform.dom.hasAttribute(action,"targetexpr") ? pm.platform.dom.getAttribute(action,"targetexpr") : JSON.stringify(pm.platform.dom.getAttribute(action,"target"))),
targetVariableName = '_scionTargetRef',
targetVariableName = '_scionTargetRef',
targetDeclaration = 'var ' + targetVariableName + ' = ' + target + ';\n';

var event = "{\n" +
var event = "{\n" +
"target: " + targetVariableName + ",\n" +
"name: " + (pm.platform.dom.hasAttribute(action,"eventexpr") ? pm.platform.dom.getAttribute(action,"eventexpr") : JSON.stringify(pm.platform.dom.getAttribute(action,"event"))) + ",\n" +
"name: " + (pm.platform.dom.hasAttribute(action,"eventexpr") ? pm.platform.dom.getAttribute(action,"eventexpr") : JSON.stringify(pm.platform.dom.getAttribute(action,"event"))) + ",\n" +
"type: " + (pm.platform.dom.hasAttribute(action,"typeexpr") ? pm.platform.dom.getAttribute(action,"typeexpr") : JSON.stringify(pm.platform.dom.getAttribute(action,"type"))) + ",\n" +
"data: " + constructSendEventData(action) + ",\n" +
"origin: $origin\n" +
"}";

var send =
var send =
targetDeclaration +
"if(" + targetVariableName + " === '#_internal'){\n" +
"$raise(" + event + ");\n" +
"if(" + targetVariableName + " === '#_internal'){\n" +
"$raise(" + event + ");\n" +
"}else{\n" +
"$send(" + event + ", {\n" +
"delay: " + (pm.platform.dom.hasAttribute(action,"delayexpr") ? pm.platform.dom.getAttribute(action,"delayexpr") : getDelayInMs(pm.platform.dom.getAttribute(action,"delay"))) + ",\n" +
"sendId: " + (pm.platform.dom.hasAttribute(action,"idlocation") ? pm.platform.dom.getAttribute(action,"idlocation") : JSON.stringify(pm.platform.dom.getAttribute(action,"id"))) + "\n" +
"});" +
"$send(" + event + ", {\n" +
"delay: " + (pm.platform.dom.hasAttribute(action,"delayexpr") ? pm.platform.dom.getAttribute(action,"delayexpr") : getDelayInMs(pm.platform.dom.getAttribute(action,"delay"))) + ",\n" +
"sendId: " + (pm.platform.dom.hasAttribute(action,"idlocation") ? pm.platform.dom.getAttribute(action,"idlocation") : JSON.stringify(pm.platform.dom.getAttribute(action,"id"))) + "\n" +
"});" +
"}";

return send;
Expand All @@ -150,18 +150,18 @@ var actionTags = {
arr = pm.platform.dom.getAttribute(action,"array"),
foreachBody = pm.platform.dom.getElementChildren(action).map(actionTagToFnBody).join("\n;;\n");

return "(function(){\n" +
return "(function(){\n" +
"if(Array.isArray(" + arr + ")){\n" +
arr + ".forEach(function(" + item + "," + index + "){\n" +
foreachBody +
"\n});\n" +
"\n});\n" +
"}else{\n" +
//assume object
"Object.keys(" + arr + ").forEach(function(" + index + "){\n" +
item + " = " + arr + "[" + index + "];\n" +
item + " = " + arr + "[" + index + "];\n" +
foreachBody +
"\n});\n" +
"}\n" +
"\n});\n" +
"}\n" +
"})();";
}
}
Expand Down Expand Up @@ -190,13 +190,13 @@ function getDatamodelExpression(id, datamodelObject){
s += ' = ';

switch(datamodelObject.type){
case 'xml' :
case 'xml' :
s += '$parseXml(' + JSON.stringify(datamodelObject.content) + ')';
break;
case 'json' :
case 'json' :
s += 'JSON.parse(' + JSON.stringify(datamodelObject.content) + ')';
break;
case 'expr' :
case 'expr' :
s += datamodelObject.content;
break;
default :
Expand Down Expand Up @@ -224,47 +224,47 @@ function makeDatamodelDeclaration(datamodel){
function makeDatamodelClosures(datamodel){
var vars = [];
for(var id in datamodel){
vars.push( '"' + id + '" : {\n' +
'"set" : function(v){ return ' + id + ' = v; },\n' +
'"get" : function(){ return ' + id + ';}' +
vars.push( '"' + id + '" : {\n' +
'"set" : function(v){ return ' + id + ' = v; },\n' +
'"get" : function(){ return ' + id + ';}' +
'\n}');
}
return '{\n' + vars.join(',\n') + '\n}';
}

function wrapFunctionBodyInDeclaration(action,isExpression){
return "function(getData,setData,_events,$raise){var _event = _events[0];\n" +
(isExpression ? "return" : "") + " " + action +
(isExpression ? "return" : "") + " " + action +
"\n}";
}


function makeTopLevelFunctionBody(datamodelDeclaration,topLevelScripts,datamodelClosures,actionStrings){
return datamodelDeclaration +
(topLevelScripts.length ? topLevelScripts.join("\n") : "") +
"var $datamodel = " + datamodelClosures + ";\n" +
"return {\n" +
"datamodel:$datamodel,\n" +
return datamodelDeclaration +
(topLevelScripts.length ? topLevelScripts.join("\n") : "") +
"var $datamodel = " + datamodelClosures + ";\n" +
"return {\n" +
"datamodel:$datamodel,\n" +
"actions:[\n" + actionStrings.join(",\n") + "\n]" + //return all functions which get called during execution
"\n};";
}

function wrapTopLevelFunctionBodyInDeclaration(fnBody){
return "function($log,$cancel,$send,$origin,In,require,$parseXml,_sessionid){\n" + fnBody + "\n}";
return "function($log,$cancel,$send,$origin,In,require,$parseXml,_sessionid,_ioprocessors,_x){\n" + fnBody + "\n}";
}

//this function ensures that the code in each SCXML document will run in "document scope".
//SCXML embeds js code as strings in the document, hence the use of "eval" to dynamically evaluate things.
//This function ensures that eval() is only called once, when the model is parsed. It will not be called during execution of the statechart.
//However, each SCXML interpreter instance will have its own copies of the functions declared in the document.
//However, each SCXML interpreter instance will have its own copies of the functions declared in the document.
//This is similar to the way HTML works - each page has its own copies of evaluated scripts.
function makeActionFactory(topLevelScripts,actionStrings,datamodel){
var datamodelDeclaration = makeDatamodelDeclaration(datamodel);
var datamodelClosures = makeDatamodelClosures(datamodel);
var topLevelFnBody = makeTopLevelFunctionBody(datamodelDeclaration,topLevelScripts,datamodelClosures,actionStrings);
var fnStr = wrapTopLevelFunctionBodyInDeclaration(topLevelFnBody);
//require('fs').writeFileSync('out.js',fnStr);
return fnStr;
return fnStr;
}


Expand All @@ -273,7 +273,7 @@ function constructSendEventData(action){
var namelist = pm.platform.dom.hasAttribute(action,"namelist") ? pm.platform.dom.getAttribute(action,"namelist").trim().split(/ +/) : null,
params = pm.platform.dom.getChildren(action).filter(function(child){return pm.platform.dom.localName(child) === 'param';}),
content = pm.platform.dom.getChildren(action).filter(function(child){return pm.platform.dom.localName(child) === 'content';});

if(content.length){
//TODO: instead of using textContent, serialize the XML
content = content[0];
Expand Down

0 comments on commit 46a77d0

Please sign in to comment.