statemachine.js
5.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/******************************************************************************
* Statemachine implementation
* ***************************************************************************/
/**
* Describes a statemachine
* @param obj {object} an object describing a state machine. If obj is empty then the statemachine is empty
* @class
*/
ARCS.Statemachine = new ARCS.Component.create(function (obj) {
// dynamic construction: properties are initial state that have properties
// that are tokens and value that are the final state
var initial = "", final = "", transitions = {}, currentState = "", self= this;
var astTokens = {}; // it keeps AST from event logic expressions
var tokenEvents = {}; // it keeps promises for tokens.
var network = {};
var addToken = function(t) {
if ( self.slots.indexOf(t) < 0 ) {
self.slots.push(t);
self[t] = function( s ) {
return function() {
self.setToken(s);
};
} (t);
}
};
/* this function aims at preparing transition networks for a given state*/
var setSheet = function(s) {
// we build promise trees using ast
var t;
if (transitions.hasOwnProperty(s)) {
tokenEvents = {};
for (t in transitions[s]) {
if (transitions[s].hasOwnProperty(t)) {
network = ARCS.TransitionNetwork.build(astTokens[t],tokenEvents).promise;
network.then(
function() {
var token;
// clean up remaining promises
for (token in tokenEvents) {
if (tokenEvents.hasOwnProperty(token)) tokenEvents[token].abort();
}
// then activate next sheet
setSheet(transitions[s][t]);
}
);
}
}
}
currentState = s;
self.emit('requestSheet', currentState);
if (currentState === final) {
self.emit('requestTermination');
}
};
/**
* Sets the initial state of the statemachine
* @param string {string} name of the initial state
*/
this.setInitialState = function (string) {
initial = string;
currentState = initial;
};
/**
* Sets the final state of the statemachine
* @param string {string} name of the final state
*/
this.setFinalState = function (string) { final = string; };
/**
* Adds a transition to the state machine
* @param start {string} name of the state at the beginning of the transition
* @param token {string} name of the token triggering the transition
* @param end {string} name of the state reached at the end of the transition
*/
this.addTransition = function (start, token, end) {
var re = /([A-Za-z_]\w*)/g;
var t, tsd, ts, tsc;
try {
var tsd = ARCS.EventLogicParser.parse(token);
if (typeof tsd === "string") {
addToken(tsd);
} else {
while( (t = re.exec(token)) !== null) {
addToken(t[0]);
}
}
astTokens[token] = tsd;
if (transitions[start] === undefined) {
transitions[start] = {};
}
transitions[start][token] = end;
} catch (e) { }
};
/**
* Gives a token to the statemachine. According to its list of transitions
* and the current state, it may trigger a transition
* @param token {string} name of the token
*/
this.setToken = function (token) {
if (tokenEvents.hasOwnProperty(token)) {
tokenEvents[token].accept();
}
};
/**
* Sets transitions from a list of transitions
* @param obj {object[]} list of transitions
*/
this.setTransitions = function (obj) {
// this function is no longuer a simple affectation
// transitions = obj;
var p, t, i;
for (p in obj) {
if (obj.hasOwnProperty(p)) {
for (t in obj[p]) {
if (obj[p].hasOwnProperty(t)) {
this.addTransition(p, t, obj[p][t]);
}
}
}
}
// we will temporay dump properties in order to understand how the statemachine is built
/*
for (p in transitions) {
if (transitions.hasOwnProperty(p)) {
for (t in transitions[p]) {
if (transitions[p].hasOwnProperty(t)) {
console.log("\t" + p + "\t----\t" + t + "\t--->\t" + transitions[p][t]);
}
}
}
}*/
};
/**
* Initialize and starts the statemachine, setting its current state to
* the initial state (by default, it is the departure of the first transition
*/
this.start = function () {
console.log("statemachine", this, initial,obj);
setSheet(initial);
};
// INIT CODE
if (obj !== undefined) {
initial = obj.initial;
final = obj.final;
this.setTransitions(obj.transitions);
currentState = "";
}
},
['setToken'],
['requestSheet', 'requestTermination']
);