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

Commit

Permalink
Rewrote core SCXML interpreter in JavaScript.
Browse files Browse the repository at this point in the history
  • Loading branch information
jbeard4 committed Mar 29, 2012
1 parent 726e243 commit 26aba97
Show file tree
Hide file tree
Showing 8 changed files with 1,065 additions and 0 deletions.
636 changes: 636 additions & 0 deletions lib/scxml/SCXML.js

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions lib/scxml/default-transition-selector.js
@@ -0,0 +1,21 @@
// Copyright 2011-2012 Jacob Beard, INFICON, and other SCION contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var _ = require('underscore');

module.exports = function(state,eventNames,evaluator){
return _.filter(state.transitions,function(t){
return !t.event || ( _.contains(eventNames,t.event) && (!t.cond || evaluator(t)) );
});
};
133 changes: 133 additions & 0 deletions lib/scxml/json2model.js
@@ -0,0 +1,133 @@
// Copyright 2011-2012 Jacob Beard, INFICON, and other SCION contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var _ = require('underscore');

function getDelayInMs(delayString){
if (!delayString) {
return 0;
} else {
if (delayString.slice(-2) === "ms") {
return parseFloat(delayString.slice(0, -2));
} else if (delayString.slice(-1) === "s") {
return parseFloat(delayString.slice(0, -1)) * 1000;
} else {
return parseFloat(delayString);
}
}
}

function makeEvaluationFn(s, isExpression) {
return new Function(
"getData",
"setData",
"In",
"_events",
"datamodel",
"var _event = _events[0]; with(datamodel){" + (isExpression ? "return" : "") + " " + s + "}");
}

module.exports = function(json) {

function stateIdToReference(stateId){
return idToStateMap[stateId];
}

var idToStateMap = {};
_.forEach(json.states,function(state){
idToStateMap[state.id] = state;
});

_.forEach(json.transitions,function(transition){
transition.evaluateCondition = makeEvaluationFn(transition.cond,true);
});

_.forEach(json.states,function(state){
state.transitions = _.map(state.transitions,function(transitionNum){ return json.transitions[transitionNum];});

var actions = state.onentry.concat(state.onexit);

_.forEach(state.transitions,function(transition){
_.forEach(transition.actions,function(action){
actions.push(action);
});

if(transition.lca){
transition.lca = idToStateMap[transition.lca];
}
});

_.forEach(actions,function(action){
switch (action.type) {
case "script":
action.evaluate = makeEvaluationFn(action.script);
break;
case "assign":
action.evaluate = makeEvaluationFn(action.expr, true);
break;
case "send":
_.forEach(['contentexpr', 'eventexpr', 'targetexpr', 'typeexpr', 'delayexpr'],function(attributeName){
if (action[attributeName]) {
action[attributeName] = {
evaluate: makeEvaluationFn(action[attributeName], true)
};
}
});

_.forEach(action.params,function(param){
if (param.expr) {
param.expr = {
evaluate: makeEvaluationFn(param.expr, true)
};
}
});
break;
case "log":
action.evaluate = makeEvaluationFn(action.expr, true);
break;
default : break;
}

if (action.type === "send" && action.delay) {
action.delay = getDelayInMs(action.delay);
}

});

state.initial = idToStateMap[state.initial];
state.history = idToStateMap[state.history];

state.children = _.map(state.children,stateIdToReference);

state.parent = idToStateMap[state.parent];

if (state.ancestors) {
state.ancestors = _.map(state.ancestors,stateIdToReference);
}

if (state.descendants) {
state.descendants = _.map(state.descendants,stateIdToReference);
}

_.forEach(state.transitions,function(t){
t.source = idToStateMap[t.source];
t.targets = t.targets && _.map(t.targets,stateIdToReference);
});
});

json.root = idToStateMap[json.root];

return json;
};

100 changes: 100 additions & 0 deletions lib/scxml/model.js
@@ -0,0 +1,100 @@
// Copyright 2011-2012 Jacob Beard, INFICON, and other SCION contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var _ = require('underscore');

var stateKinds = require('./state-kinds-enum');

module.exports = {
getDepth: function(s) {
var count, state;
if (s.depth !== undefined) {
return s.depth;
} else {
count = 0;
state = s.parent;
while (state) {
count = count + 1;
state = state.parent;
}
return count;
}
},
getAncestors: function(s, root) {
var ancestors, index, state;
if (s.ancestors) {
if (_.contains(s.ancestors,root)) {
return s.ancestors.slice(0, index);
} else {
return s.ancestors;
}
} else {
ancestors = [];
state = s.parent;
while (state && !(state === root)) {
ancestors.push(state);
state = state.parent;
}
return ancestors;
}
},
getAncestorsOrSelf: function(s, root) {
return [s].concat(this.getAncestors(s, root));
},
getDescendants: function(s) {
var child, descendants, queue, state, _i, _len, _ref;
if (s.descendants) {
return s.descendants;
} else {
descendants = [];
queue = s.children.slice();
while (queue.length) {
state = queue.shift();
descendants.push(state);
queue.push.apply(queue,state.children);
}
return descendants;
}
},
getDescendantsOrSelf: function(s) {
return [s].concat(this.getDescendants(s));
},
isOrthogonalTo: function(s1, s2) {
//Two control states are orthogonal if they are not ancestrally
//related, and their smallest, mutual parent is a Concurrent-state.
return !this.isAncestrallyRelatedTo(s1, s2) && this.getLCA(s1, s2).kind === stateKinds.PARALLEL;
},
isAncestrallyRelatedTo: function(s1, s2) {
//Two control states are ancestrally related if one is child/grandchild of another.
return _.contains(this.getAncestorsOrSelf(s2), s1) || _.contains(this.getAncestorsOrSelf(s1), s2);
},
getLCA: function(tOrS1, s2) {
if (tOrS1.lca) {
return tOrS1.lca;
} else {
//can take one or two arguments: either 1 transition, or two states
if (arguments.length === 1) {
var transition = tOrS1;
return this.getLCA(transition.source, transition.targets[0]);
} else {
var s1 = tOrS1;
var commonAncestors = _.filter(this.getAncestors(s1),_.bind(function(a){
return _.contains(this.getDescendants(a),s2);
},this));
return commonAncestors[0];
}
}
}
};

44 changes: 44 additions & 0 deletions lib/scxml/scxml-dynamic-name-match-transition-selector.js
@@ -0,0 +1,44 @@
// Copyright 2011-2012 Jacob Beard, INFICON, and other SCION contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var _ = require('underscore');

var eventNameReCache = {};

function eventNameToRe(name) {
return new RegExp("^" + (name.replace(/\./g, "\\.")) + "(\\.[0-9a-zA-Z]+)*$");
}

function retrieveEventRe(name) {
return eventNameReCache[name] ? eventNameReCache[name] : eventNameReCache[name] = eventNameToRe(name);
}

function nameMatch(t, eventNames) {
var tEvents = t.events;
var f =
_.contains(tEvents, "*") ?
function() { return true; } :
function(name) {
return _(tEvents).filter(function(tEvent){
return retrieveEventRe(tEvent).test(name);
}).length;
};
return _(eventNames).filter(f).length;
}

module.exports = function(state, eventNames, evaluator) {
return _(state.transitions).filter(function(t){
return (!t.events || nameMatch(t,eventNames)) && (!t.cond || evaluator(t));
});
};
81 changes: 81 additions & 0 deletions lib/scxml/set/ArraySet.js
@@ -0,0 +1,81 @@
// Copyright 2011-2012 Jacob Beard, INFICON, and other SCION contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var _ = require('underscore');

module.exports = function(l) {
l = l || [];
this.o = [];

_.forEach(l,_.bind(function(x){
this.add(x);
},this));
};

//TODO: delegate to underscore's union and difference
module.exports.prototype = {

add : function(x) {
if (!this.contains(x)) return this.o.push(x);
},

remove : function(x) {
var i = _.indexOf(this.o,x);
if(i === -1){
return false;
}else{
this.o.splice(i, 1);
}
return true;
},

union : function(l) {
l = l.iter ? l.iter() : l;
_.forEach(l,_.bind(function(x){
this.add(x);
},this));
return this;
},

difference : function(l) {
l = l.iter ? l.iter() : l;

_.forEach(l,_.bind(function(x){
this.remove(x);
},this));
return this;
},

contains : function(x) {
return _.indexOf(this.o, x) >= 0;
},

iter : function() {
return this.o;
},

isEmpty : function() {
return !this.o.length;
},

equals : function(s2) {
var l2 = s2.iter();

return _.difference(this.o,l2).length === 0;
},

toString : function() {
return "Set(" + this.o.toString() + ")";
}
};

0 comments on commit 26aba97

Please sign in to comment.