jssm 5.45.2 → 5.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es6/jssm.d.ts +2 -1
- package/dist/es6/jssm.js +5 -0
- package/dist/es6/jssm_types.d.ts +6 -5
- package/dist/es6/version.js +1 -1
- package/dist/jssm.es5.cjs.js +1 -1
- package/jssm.d.ts +2 -1
- package/jssm_types.d.ts +6 -5
- package/package.json +1 -1
- package/.codeclimate.yml +0 -22
- package/.editorconfig +0 -12
- package/.eslintrc +0 -20
- package/.nycrc +0 -6
- package/.travis.yml +0 -9
- package/dist/jssm.es5.iife.js +0 -1
- package/jest-spec.config.js +0 -27
- package/jest-stoch.config.js +0 -27
- package/rollup.config.iife.js +0 -44
- package/rollup.config.js +0 -44
- package/src/demo/index.html +0 -38
- package/src/demo/style.css +0 -2
- package/src/ts/jssm-dot.peg +0 -928
- package/src/ts/jssm.ts +0 -1243
- package/src/ts/jssm_types.ts +0 -385
- package/src/ts/jssm_util.ts +0 -117
- package/src/ts/tests/actions.spec.ts +0 -167
- package/src/ts/tests/arrange.spec.ts +0 -72
- package/src/ts/tests/arrange.stoch.ts +0 -4
- package/src/ts/tests/array_box_if_string.spec.ts +0 -30
- package/src/ts/tests/array_transitions.spec.ts +0 -129
- package/src/ts/tests/arrow unicode.spec.ts +0 -88
- package/src/ts/tests/arrow.spec.ts +0 -124
- package/src/ts/tests/colors.spec.ts +0 -58
- package/src/ts/tests/comment.spec.ts +0 -134
- package/src/ts/tests/compile.spec.ts +0 -79
- package/src/ts/tests/constants.spec.ts +0 -98
- package/src/ts/tests/cycles.spec.ts +0 -153
- package/src/ts/tests/dot_preamble.spec.ts +0 -16
- package/src/ts/tests/embedded_sm.spec.ts +0 -36
- package/src/ts/tests/flow.spec.ts +0 -22
- package/src/ts/tests/forced transitions.spec.ts +0 -26
- package/src/ts/tests/general.spec.ts +0 -933
- package/src/ts/tests/graph node lists.spec.ts +0 -21
- package/src/ts/tests/histo.spec.ts +0 -24
- package/src/ts/tests/hooks.spec.ts +0 -209
- package/src/ts/tests/language.spec.ts +0 -37
- package/src/ts/tests/language_data/belarussian.json +0 -14
- package/src/ts/tests/language_data/bengali.json +0 -16
- package/src/ts/tests/language_data/emoji.json +0 -22
- package/src/ts/tests/language_data/english.json +0 -17
- package/src/ts/tests/language_data/french.json +0 -17
- package/src/ts/tests/language_data/german.json +0 -17
- package/src/ts/tests/language_data/hebrew.json +0 -16
- package/src/ts/tests/language_data/portuguese.json +0 -13
- package/src/ts/tests/language_data/russian.json +0 -13
- package/src/ts/tests/language_data/spanish.json +0 -17
- package/src/ts/tests/language_data/ukrainian.json +0 -19
- package/src/ts/tests/layout.spec.ts +0 -29
- package/src/ts/tests/machine_attributes.spec.ts +0 -398
- package/src/ts/tests/machine_name.spec.ts +0 -14
- package/src/ts/tests/named lists.spec.ts +0 -24
- package/src/ts/tests/nominated states.spec.ts +0 -133
- package/src/ts/tests/parse actions.spec.ts +0 -32
- package/src/ts/tests/parse.spec.ts +0 -94
- package/src/ts/tests/probability.spec.ts +0 -146
- package/src/ts/tests/r639.spec.ts +0 -27
- package/src/ts/tests/sample_select.spec.ts +0 -173
- package/src/ts/tests/seq.spec.ts +0 -14
- package/src/ts/tests/seq.stoch.ts +0 -83
- package/src/ts/tests/shapes.spec.ts +0 -63
- package/src/ts/tests/sm_tag.spec.ts +0 -37
- package/src/ts/tests/special characters.spec.ts +0 -39
- package/src/ts/tests/state_declaration.spec.ts +0 -214
- package/src/ts/tests/state_style.spec.ts +0 -82
- package/src/ts/tests/stop light.spec.ts +0 -157
- package/src/ts/tests/stripes.spec.ts +0 -52
- package/src/ts/tests/theme.spec.ts +0 -45
- package/src/ts/tests/weighted_histo_key.spec.ts +0 -22
- package/src/ts/tests/weighted_rand_select.spec.ts +0 -27
- package/src/ts/tests/weighted_sample_select.spec.ts +0 -24
- package/src/ts/version.ts +0 -3
- package/tree.txt +0 -1794
- package/tsconfig.json +0 -27
package/src/ts/jssm.ts
DELETED
|
@@ -1,1243 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
// whargarbl lots of these return arrays could/should be sets
|
|
3
|
-
|
|
4
|
-
type StateType = string;
|
|
5
|
-
|
|
6
|
-
import { reduce as reduce_to_639 } from 'reduce-to-639-1';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
JssmGenericState, JssmGenericConfig,
|
|
15
|
-
JssmTransition, JssmTransitionList, // JssmTransitionRule,
|
|
16
|
-
JssmMachineInternalState,
|
|
17
|
-
JssmParseTree,
|
|
18
|
-
JssmStateDeclaration, JssmStateDeclarationRule,
|
|
19
|
-
JssmCompileSe, JssmCompileSeStart, JssmCompileRule,
|
|
20
|
-
JssmArrow, JssmArrowDirection, JssmArrowKind,
|
|
21
|
-
JssmLayout,
|
|
22
|
-
FslDirection, FslTheme,
|
|
23
|
-
HookDescription
|
|
24
|
-
|
|
25
|
-
} from './jssm_types';
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
import {
|
|
32
|
-
seq, weighted_rand_select, weighted_sample_select, histograph,
|
|
33
|
-
weighted_histo_key, array_box_if_string, hook_name, named_hook_name
|
|
34
|
-
} from './jssm_util';
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
import { parse } from './jssm-dot'; // TODO FIXME WHARGARBL this could be post-typed
|
|
41
|
-
|
|
42
|
-
import { version } from './version'; // replaced from package.js in build
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
/* eslint-disable complexity */
|
|
49
|
-
|
|
50
|
-
function arrow_direction(arrow: JssmArrow): JssmArrowDirection {
|
|
51
|
-
|
|
52
|
-
switch ( String(arrow) ) {
|
|
53
|
-
|
|
54
|
-
case '->' : case '→' :
|
|
55
|
-
case '=>' : case '⇒' :
|
|
56
|
-
case '~>' : case '↛' :
|
|
57
|
-
return 'right';
|
|
58
|
-
|
|
59
|
-
case '<-' : case '←' :
|
|
60
|
-
case '<=' : case '⇐' :
|
|
61
|
-
case '<~' : case '↚' :
|
|
62
|
-
return 'left';
|
|
63
|
-
|
|
64
|
-
case '<->' : case '↔' :
|
|
65
|
-
case '<-=>' : case '←⇒' : case '←=>' : case '<-⇒' :
|
|
66
|
-
case '<-~>' : case '←↛' : case '←~>' : case '<-↛' :
|
|
67
|
-
|
|
68
|
-
case '<=>' : case '⇔' :
|
|
69
|
-
case '<=->' : case '⇐→' : case '⇐->' : case '<=→' :
|
|
70
|
-
case '<=~>' : case '⇐↛' : case '⇐~>' : case '<=↛' :
|
|
71
|
-
|
|
72
|
-
case '<~>' : case '↮' :
|
|
73
|
-
case '<~->' : case '↚→' : case '↚->' : case '<~→' :
|
|
74
|
-
case '<~=>' : case '↚⇒' : case '↚=>' : case '<~⇒' :
|
|
75
|
-
return 'both';
|
|
76
|
-
|
|
77
|
-
default:
|
|
78
|
-
throw new Error(`arrow_direction: unknown arrow type ${arrow}`);
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/* eslint-enable complexity */
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/* eslint-disable complexity */
|
|
91
|
-
|
|
92
|
-
function arrow_left_kind(arrow: JssmArrow): JssmArrowKind {
|
|
93
|
-
|
|
94
|
-
switch ( String(arrow) ) {
|
|
95
|
-
|
|
96
|
-
case '->' : case '→' :
|
|
97
|
-
case '=>' : case '⇒' :
|
|
98
|
-
case '~>' : case '↛' :
|
|
99
|
-
return 'none';
|
|
100
|
-
|
|
101
|
-
case '<-': case '←' :
|
|
102
|
-
case '<->': case '↔' :
|
|
103
|
-
case '<-=>': case '←⇒' :
|
|
104
|
-
case '<-~>': case '←↛' :
|
|
105
|
-
return 'legal';
|
|
106
|
-
|
|
107
|
-
case '<=': case '⇐' :
|
|
108
|
-
case '<=>': case '⇔' :
|
|
109
|
-
case '<=->': case '⇐→' :
|
|
110
|
-
case '<=~>': case '⇐↛' :
|
|
111
|
-
return 'main';
|
|
112
|
-
|
|
113
|
-
case '<~': case '↚' :
|
|
114
|
-
case '<~>': case '↮' :
|
|
115
|
-
case '<~->': case '↚→' :
|
|
116
|
-
case '<~=>': case '↚⇒' :
|
|
117
|
-
return 'forced';
|
|
118
|
-
|
|
119
|
-
default:
|
|
120
|
-
throw new Error(`arrow_direction: unknown arrow type ${arrow}`);
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/* eslint-enable complexity */
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/* eslint-disable complexity */
|
|
133
|
-
|
|
134
|
-
function arrow_right_kind(arrow: JssmArrow): JssmArrowKind {
|
|
135
|
-
|
|
136
|
-
switch ( String(arrow) ) {
|
|
137
|
-
|
|
138
|
-
case '<-' : case '←' :
|
|
139
|
-
case '<=' : case '⇐' :
|
|
140
|
-
case '<~' : case '↚' :
|
|
141
|
-
return 'none';
|
|
142
|
-
|
|
143
|
-
case '->' : case '→' :
|
|
144
|
-
case '<->': case '↔' :
|
|
145
|
-
case '<=->': case '⇐→' :
|
|
146
|
-
case '<~->': case '↚→' :
|
|
147
|
-
return 'legal';
|
|
148
|
-
|
|
149
|
-
case '=>' : case '⇒' :
|
|
150
|
-
case '<=>': case '⇔' :
|
|
151
|
-
case '<-=>': case '←⇒' :
|
|
152
|
-
case '<~=>': case '↚⇒' :
|
|
153
|
-
return 'main';
|
|
154
|
-
|
|
155
|
-
case '~>' : case '↛' :
|
|
156
|
-
case '<~>': case '↮' :
|
|
157
|
-
case '<-~>': case '←↛' :
|
|
158
|
-
case '<=~>': case '⇐↛' :
|
|
159
|
-
return 'forced';
|
|
160
|
-
|
|
161
|
-
default:
|
|
162
|
-
throw new Error(`arrow_direction: unknown arrow type ${arrow}`);
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/* eslint-enable complexity */
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
function makeTransition<mDT>(
|
|
175
|
-
this_se : JssmCompileSe,
|
|
176
|
-
from : string,
|
|
177
|
-
to : string,
|
|
178
|
-
isRight : boolean,
|
|
179
|
-
_wasList? : Array<string>,
|
|
180
|
-
_wasIndex? : number
|
|
181
|
-
) : JssmTransition<mDT> {
|
|
182
|
-
|
|
183
|
-
const kind : JssmArrowKind = isRight? arrow_right_kind(this_se.kind) : arrow_left_kind(this_se.kind),
|
|
184
|
-
edge : JssmTransition<mDT> = {
|
|
185
|
-
from,
|
|
186
|
-
to,
|
|
187
|
-
kind,
|
|
188
|
-
forced_only : kind === 'forced',
|
|
189
|
-
main_path : kind === 'main'
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
// if ((wasList !== undefined) && (wasIndex === undefined)) { throw new TypeError("Must have an index if transition was in a list"); }
|
|
193
|
-
// if ((wasIndex !== undefined) && (wasList === undefined)) { throw new TypeError("Must be in a list if transition has an index"); }
|
|
194
|
-
/*
|
|
195
|
-
if (typeof edge.to === 'object') {
|
|
196
|
-
|
|
197
|
-
if (edge.to.key === 'cycle') {
|
|
198
|
-
if (wasList === undefined) { throw "Must have a waslist if a to is type cycle"; }
|
|
199
|
-
const nextIndex = wrapBy(wasIndex, edge.to.value, wasList.length);
|
|
200
|
-
edge.to = wasList[nextIndex];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
*/
|
|
205
|
-
const action : string = isRight? 'r_action' : 'l_action',
|
|
206
|
-
probability : string = isRight? 'r_probability' : 'l_probability';
|
|
207
|
-
|
|
208
|
-
if (this_se[action]) { edge.action = this_se[action]; }
|
|
209
|
-
if (this_se[probability]) { edge.probability = this_se[probability]; }
|
|
210
|
-
|
|
211
|
-
return edge;
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
function wrap_parse(input: string, options?: Object) {
|
|
220
|
-
return parse(input, options || {});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
function compile_rule_transition_step<mDT>(
|
|
228
|
-
acc : Array< JssmTransition<mDT> >,
|
|
229
|
-
from : string,
|
|
230
|
-
to : string,
|
|
231
|
-
this_se : JssmCompileSe,
|
|
232
|
-
next_se : JssmCompileSe
|
|
233
|
-
) : Array< JssmTransition<mDT> > { // todo typescript describe the parser representation of a transition step extension
|
|
234
|
-
|
|
235
|
-
const edges : Array< JssmTransition<mDT> > = [];
|
|
236
|
-
|
|
237
|
-
const uFrom : Array< string > = (Array.isArray(from)? from : [from]),
|
|
238
|
-
uTo : Array< string > = (Array.isArray(to)? to : [to] );
|
|
239
|
-
|
|
240
|
-
uFrom.map( (f: string) => {
|
|
241
|
-
uTo.map( (t: string) => {
|
|
242
|
-
|
|
243
|
-
const right: JssmTransition<mDT> = makeTransition(this_se, f, t, true);
|
|
244
|
-
if (right.kind !== 'none') { edges.push(right); }
|
|
245
|
-
|
|
246
|
-
const left: JssmTransition<mDT> = makeTransition(this_se, t, f, false);
|
|
247
|
-
if (left.kind !== 'none') { edges.push(left); }
|
|
248
|
-
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
const new_acc: Array< JssmTransition<mDT> > = acc.concat(edges);
|
|
253
|
-
|
|
254
|
-
if (next_se) {
|
|
255
|
-
return compile_rule_transition_step(new_acc, to, next_se.to, next_se, next_se.se);
|
|
256
|
-
} else {
|
|
257
|
-
return new_acc;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
function compile_rule_handle_transition(rule: JssmCompileSeStart<StateType>): any { // TODO FIXME no any // todo typescript describe the parser representation of a transition
|
|
265
|
-
return compile_rule_transition_step([], rule.from, rule.se.to, rule.se, rule.se.se);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
function compile_rule_handler(rule: JssmCompileSeStart<StateType>): JssmCompileRule {
|
|
271
|
-
|
|
272
|
-
if (rule.key === 'transition') {
|
|
273
|
-
return { agg_as: 'transition', val: compile_rule_handle_transition(rule) };
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (rule.key === 'machine_language') {
|
|
277
|
-
return { agg_as: 'machine_language', val: reduce_to_639(rule.value) };
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (rule.key === 'state_declaration') {
|
|
281
|
-
if (!rule.name) { throw new Error('State declarations must have a name'); }
|
|
282
|
-
return { agg_as: 'state_declaration', val: { state: rule.name, declarations: rule.value } };
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (['arrange_declaration', 'arrange_start_declaration',
|
|
286
|
-
'arrange_end_declaration'].includes(rule.key)) {
|
|
287
|
-
return { agg_as: rule.key, val: [rule.value] };
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const tautologies : Array<string> = [
|
|
291
|
-
'graph_layout', 'start_states', 'end_states', 'machine_name', 'machine_version',
|
|
292
|
-
'machine_comment', 'machine_author', 'machine_contributor', 'machine_definition',
|
|
293
|
-
'machine_reference', 'machine_license', 'fsl_version', 'state_config', 'theme',
|
|
294
|
-
'flow', 'dot_preamble'
|
|
295
|
-
];
|
|
296
|
-
|
|
297
|
-
if (tautologies.includes(rule.key)) {
|
|
298
|
-
return { agg_as: rule.key, val: rule.value };
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
throw new Error(`compile_rule_handler: Unknown rule: ${JSON.stringify(rule)}`);
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
function compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT> {
|
|
310
|
-
|
|
311
|
-
const results : {
|
|
312
|
-
graph_layout : Array< JssmLayout >,
|
|
313
|
-
transition : Array< JssmTransition<mDT> >,
|
|
314
|
-
start_states : Array< string >,
|
|
315
|
-
end_states : Array< string >,
|
|
316
|
-
state_config : Array< any >, // TODO COMEBACK no any
|
|
317
|
-
state_declaration : Array< string >,
|
|
318
|
-
fsl_version : Array< string >,
|
|
319
|
-
machine_author : Array< string >,
|
|
320
|
-
machine_comment : Array< string >,
|
|
321
|
-
machine_contributor : Array< string >,
|
|
322
|
-
machine_definition : Array< string >,
|
|
323
|
-
machine_language : Array< string >,
|
|
324
|
-
machine_license : Array< string >,
|
|
325
|
-
machine_name : Array< string >,
|
|
326
|
-
machine_reference : Array< string >,
|
|
327
|
-
theme : Array< string >,
|
|
328
|
-
flow : Array< string >,
|
|
329
|
-
dot_preamble : Array< string >,
|
|
330
|
-
arrange_declaration : Array< Array< string > >, // TODO COMEBACK CHECKME
|
|
331
|
-
arrange_start_declaration : Array< Array< string > >, // TODO COMEBACK CHECKME
|
|
332
|
-
arrange_end_declaration : Array< Array< string > >, // TODO COMEBACK CHECKME
|
|
333
|
-
machine_version : Array< string > // TODO COMEBACK semver
|
|
334
|
-
} = {
|
|
335
|
-
graph_layout : [],
|
|
336
|
-
transition : [],
|
|
337
|
-
start_states : [],
|
|
338
|
-
end_states : [],
|
|
339
|
-
state_config : [],
|
|
340
|
-
state_declaration : [],
|
|
341
|
-
fsl_version : [],
|
|
342
|
-
machine_author : [],
|
|
343
|
-
machine_comment : [],
|
|
344
|
-
machine_contributor : [],
|
|
345
|
-
machine_definition : [],
|
|
346
|
-
machine_language : [],
|
|
347
|
-
machine_license : [],
|
|
348
|
-
machine_name : [],
|
|
349
|
-
machine_reference : [],
|
|
350
|
-
theme : [],
|
|
351
|
-
flow : [],
|
|
352
|
-
dot_preamble : [],
|
|
353
|
-
arrange_declaration : [],
|
|
354
|
-
arrange_start_declaration : [],
|
|
355
|
-
arrange_end_declaration : [],
|
|
356
|
-
machine_version : []
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
tree.map( (tr : JssmCompileSeStart<StateType>) => {
|
|
360
|
-
|
|
361
|
-
const rule : JssmCompileRule = compile_rule_handler(tr),
|
|
362
|
-
agg_as : string = rule.agg_as,
|
|
363
|
-
val : any = rule.val; // TODO FIXME no any
|
|
364
|
-
|
|
365
|
-
results[agg_as] = results[agg_as].concat(val);
|
|
366
|
-
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
const assembled_transitions : Array< JssmTransition<mDT> > = [].concat(... results['transition']);
|
|
370
|
-
|
|
371
|
-
const result_cfg : JssmGenericConfig<mDT> = {
|
|
372
|
-
start_states : results.start_states.length? results.start_states : [assembled_transitions[0].from],
|
|
373
|
-
transitions : assembled_transitions
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
const oneOnlyKeys : Array<string> = [
|
|
377
|
-
'graph_layout', 'machine_name', 'machine_version', 'machine_comment',
|
|
378
|
-
'fsl_version', 'machine_license', 'machine_definition', 'machine_language',
|
|
379
|
-
'theme', 'flow', 'dot_preamble'
|
|
380
|
-
];
|
|
381
|
-
|
|
382
|
-
oneOnlyKeys.map( (oneOnlyKey : string) => {
|
|
383
|
-
if (results[oneOnlyKey].length > 1) {
|
|
384
|
-
throw new Error(
|
|
385
|
-
`May only have one ${oneOnlyKey} statement maximum: ${JSON.stringify(results[oneOnlyKey])}`
|
|
386
|
-
);
|
|
387
|
-
} else {
|
|
388
|
-
if (results[oneOnlyKey].length) {
|
|
389
|
-
result_cfg[oneOnlyKey] = results[oneOnlyKey][0];
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
['arrange_declaration', 'arrange_start_declaration', 'arrange_end_declaration',
|
|
395
|
-
'machine_author', 'machine_contributor', 'machine_reference', 'state_declaration'].map(
|
|
396
|
-
(multiKey : string) => {
|
|
397
|
-
if (results[multiKey].length) {
|
|
398
|
-
result_cfg[multiKey] = results[multiKey];
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
return result_cfg;
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
function make<mDT>(plan: string): JssmGenericConfig<mDT> {
|
|
412
|
-
return compile(wrap_parse(plan));
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
function transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration {
|
|
420
|
-
|
|
421
|
-
state_decl.declarations.map( (d: JssmStateDeclarationRule) => {
|
|
422
|
-
switch (d.key) {
|
|
423
|
-
|
|
424
|
-
case 'shape' : state_decl.shape = d.value; break;
|
|
425
|
-
case 'color' : state_decl.color = d.value; break;
|
|
426
|
-
case 'corners' : state_decl.corners = d.value; break;
|
|
427
|
-
case 'linestyle' : state_decl.linestyle = d.value; break;
|
|
428
|
-
|
|
429
|
-
case 'text-color' : state_decl.textColor = d.value; break;
|
|
430
|
-
case 'background-color' : state_decl.backgroundColor = d.value; break;
|
|
431
|
-
case 'border-color' : state_decl.borderColor = d.value; break;
|
|
432
|
-
|
|
433
|
-
default: throw new Error(`Unknown state property: '${JSON.stringify(d)}'`);
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
return state_decl;
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
class Machine<mDT> {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
_state : StateType;
|
|
450
|
-
_states : Map<StateType, JssmGenericState>;
|
|
451
|
-
_edges : Array<JssmTransition<mDT>>;
|
|
452
|
-
_edge_map : Map<StateType, Map<StateType, number>>;
|
|
453
|
-
_named_transitions : Map<StateType, number>;
|
|
454
|
-
_actions : Map<StateType, Map<StateType, number>>;
|
|
455
|
-
_reverse_actions : Map<StateType, Map<StateType, number>>;
|
|
456
|
-
_reverse_action_targets : Map<StateType, Map<StateType, number>>;
|
|
457
|
-
|
|
458
|
-
_machine_author? : Array<string>;
|
|
459
|
-
_machine_comment? : string;
|
|
460
|
-
_machine_contributor? : Array<string>;
|
|
461
|
-
_machine_definition? : string;
|
|
462
|
-
_machine_language? : string;
|
|
463
|
-
_machine_license? : string;
|
|
464
|
-
_machine_name? : string;
|
|
465
|
-
_machine_version? : string;
|
|
466
|
-
_fsl_version? : string;
|
|
467
|
-
_raw_state_declaration? : Array<Object>;
|
|
468
|
-
_state_declarations : Map<StateType, JssmStateDeclaration>;
|
|
469
|
-
|
|
470
|
-
_graph_layout : JssmLayout;
|
|
471
|
-
_dot_preamble : string;
|
|
472
|
-
_arrange_declaration : Array<Array<StateType>>;
|
|
473
|
-
_arrange_start_declaration : Array<Array<StateType>>;
|
|
474
|
-
_arrange_end_declaration : Array<Array<StateType>>;
|
|
475
|
-
|
|
476
|
-
_theme : FslTheme;
|
|
477
|
-
_flow : FslDirection;
|
|
478
|
-
|
|
479
|
-
_has_hooks : boolean;
|
|
480
|
-
_hooks : Map<string, Function>;
|
|
481
|
-
_named_hooks : Map<string, Function>;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
// whargarbl this badly needs to be broken up, monolith master
|
|
485
|
-
constructor({
|
|
486
|
-
start_states,
|
|
487
|
-
complete = [],
|
|
488
|
-
transitions,
|
|
489
|
-
machine_author,
|
|
490
|
-
machine_comment,
|
|
491
|
-
machine_contributor,
|
|
492
|
-
machine_definition,
|
|
493
|
-
machine_language,
|
|
494
|
-
machine_license,
|
|
495
|
-
machine_name,
|
|
496
|
-
machine_version,
|
|
497
|
-
state_declaration,
|
|
498
|
-
fsl_version,
|
|
499
|
-
dot_preamble = undefined,
|
|
500
|
-
arrange_declaration = [],
|
|
501
|
-
arrange_start_declaration = [],
|
|
502
|
-
arrange_end_declaration = [],
|
|
503
|
-
theme = 'default',
|
|
504
|
-
flow = 'down',
|
|
505
|
-
graph_layout = 'dot'
|
|
506
|
-
} : JssmGenericConfig<mDT>) {
|
|
507
|
-
|
|
508
|
-
this._state = start_states[0];
|
|
509
|
-
this._states = new Map();
|
|
510
|
-
this._state_declarations = new Map();
|
|
511
|
-
this._edges = [];
|
|
512
|
-
this._edge_map = new Map();
|
|
513
|
-
this._named_transitions = new Map();
|
|
514
|
-
this._actions = new Map();
|
|
515
|
-
this._reverse_actions = new Map();
|
|
516
|
-
this._reverse_action_targets = new Map(); // todo
|
|
517
|
-
|
|
518
|
-
this._machine_author = array_box_if_string(machine_author);
|
|
519
|
-
this._machine_comment = machine_comment;
|
|
520
|
-
this._machine_contributor = array_box_if_string(machine_contributor);
|
|
521
|
-
this._machine_definition = machine_definition;
|
|
522
|
-
this._machine_language = machine_language;
|
|
523
|
-
this._machine_license = machine_license;
|
|
524
|
-
this._machine_name = machine_name;
|
|
525
|
-
this._machine_version = machine_version;
|
|
526
|
-
this._raw_state_declaration = state_declaration || [];
|
|
527
|
-
this._fsl_version = fsl_version;
|
|
528
|
-
|
|
529
|
-
this._arrange_declaration = arrange_declaration;
|
|
530
|
-
this._arrange_start_declaration = arrange_start_declaration;
|
|
531
|
-
this._arrange_end_declaration = arrange_end_declaration;
|
|
532
|
-
|
|
533
|
-
this._dot_preamble = dot_preamble;
|
|
534
|
-
this._theme = theme;
|
|
535
|
-
this._flow = flow;
|
|
536
|
-
this._graph_layout = graph_layout;
|
|
537
|
-
|
|
538
|
-
this._has_hooks = false;
|
|
539
|
-
this._hooks = new Map();
|
|
540
|
-
this._named_hooks = new Map();
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (state_declaration) {
|
|
544
|
-
state_declaration.map( (state_decl: JssmStateDeclaration) => {
|
|
545
|
-
|
|
546
|
-
if (this._state_declarations.has(state_decl.state)) { // no repeats
|
|
547
|
-
throw new Error(`Added the same state declaration twice: ${JSON.stringify(state_decl.state)}`);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
this._state_declarations.set( state_decl.state, transfer_state_properties(state_decl) );
|
|
551
|
-
|
|
552
|
-
} );
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
transitions.map( (tr:JssmTransition<mDT>) => {
|
|
557
|
-
|
|
558
|
-
if (tr.from === undefined) { throw new Error(`transition must define 'from': ${JSON.stringify(tr)}`); }
|
|
559
|
-
if (tr.to === undefined) { throw new Error(`transition must define 'to': ${ JSON.stringify(tr)}`); }
|
|
560
|
-
|
|
561
|
-
// get the cursors. what a mess
|
|
562
|
-
const cursor_from: JssmGenericState
|
|
563
|
-
= this._states.get(tr.from)
|
|
564
|
-
|| { name: tr.from, from: [], to: [], complete: complete.includes(tr.from) };
|
|
565
|
-
|
|
566
|
-
if (!(this._states.has(tr.from))) {
|
|
567
|
-
this._new_state(cursor_from);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
const cursor_to: JssmGenericState
|
|
571
|
-
= this._states.get(tr.to)
|
|
572
|
-
|| {name: tr.to, from: [], to: [], complete: complete.includes(tr.to) };
|
|
573
|
-
|
|
574
|
-
if (!(this._states.has(tr.to))) {
|
|
575
|
-
this._new_state(cursor_to);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// guard against existing connections being re-added
|
|
579
|
-
if (cursor_from.to.includes(tr.to)) {
|
|
580
|
-
throw new Error(`already has ${JSON.stringify(tr.from)} to ${JSON.stringify(tr.to)}`);
|
|
581
|
-
} else {
|
|
582
|
-
cursor_from.to.push(tr.to);
|
|
583
|
-
cursor_to.from.push(tr.from);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// add the edge; note its id
|
|
587
|
-
this._edges.push(tr);
|
|
588
|
-
const thisEdgeId: number = this._edges.length - 1;
|
|
589
|
-
|
|
590
|
-
// guard against repeating a transition name
|
|
591
|
-
if (tr.name) {
|
|
592
|
-
if (this._named_transitions.has(tr.name)) {
|
|
593
|
-
throw new Error(`named transition "${JSON.stringify(tr.name)}" already created`);
|
|
594
|
-
} else {
|
|
595
|
-
this._named_transitions.set(tr.name, thisEdgeId);
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// set up the mapping, so that edges can be looked up by endpoint pairs
|
|
600
|
-
const from_mapping: Map<StateType, number> = this._edge_map.get(tr.from) || new Map();
|
|
601
|
-
if (!(this._edge_map.has(tr.from))) {
|
|
602
|
-
this._edge_map.set(tr.from, from_mapping);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// const to_mapping = from_mapping.get(tr.to);
|
|
606
|
-
from_mapping.set(tr.to, thisEdgeId); // already checked that this mapping doesn't exist, above
|
|
607
|
-
|
|
608
|
-
// set up the action mapping, so that actions can be looked up by origin
|
|
609
|
-
if (tr.action) {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
// forward mapping first by action name
|
|
613
|
-
let actionMap: Map<StateType, number> = this._actions.get(tr.action);
|
|
614
|
-
if (!(actionMap)) {
|
|
615
|
-
actionMap = new Map();
|
|
616
|
-
this._actions.set(tr.action, actionMap);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
if (actionMap.has(tr.from)) {
|
|
620
|
-
throw new Error(`action ${JSON.stringify(tr.action)} already attached to origin ${JSON.stringify(tr.from)}`);
|
|
621
|
-
} else {
|
|
622
|
-
actionMap.set(tr.from, thisEdgeId);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
// reverse mapping first by state origin name
|
|
627
|
-
let rActionMap: Map<StateType, number> = this._reverse_actions.get(tr.from);
|
|
628
|
-
if (!(rActionMap)) {
|
|
629
|
-
rActionMap = new Map();
|
|
630
|
-
this._reverse_actions.set(tr.from, rActionMap);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// no need to test for reverse mapping pre-presence;
|
|
634
|
-
// forward mapping already covers collisions
|
|
635
|
-
rActionMap.set(tr.action, thisEdgeId);
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
// reverse mapping first by state target name
|
|
639
|
-
if (!(this._reverse_action_targets.has(tr.to))) {
|
|
640
|
-
this._reverse_action_targets.set(tr.to, new Map());
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
/* todo comeback
|
|
644
|
-
fundamental problem is roActionMap needs to be a multimap
|
|
645
|
-
const roActionMap = this._reverse_action_targets.get(tr.to); // wasteful - already did has - refactor
|
|
646
|
-
if (roActionMap) {
|
|
647
|
-
if (roActionMap.has(tr.action)) {
|
|
648
|
-
throw new Error(`ro-action ${tr.to} already attached to action ${tr.action}`);
|
|
649
|
-
} else {
|
|
650
|
-
roActionMap.set(tr.action, thisEdgeId);
|
|
651
|
-
}
|
|
652
|
-
} else {
|
|
653
|
-
throw new Error('should be impossible - flow doesn\'t know .set precedes .get yet again. severe error?');
|
|
654
|
-
}
|
|
655
|
-
*/
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
_new_state(state_config: JssmGenericState): StateType {
|
|
663
|
-
|
|
664
|
-
if (this._states.has(state_config.name)) {
|
|
665
|
-
throw new Error(`state ${JSON.stringify(state_config.name)} already exists`);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
this._states.set(state_config.name, state_config);
|
|
669
|
-
return state_config.name;
|
|
670
|
-
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
state(): StateType {
|
|
676
|
-
return this._state;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/* whargarbl todo major
|
|
680
|
-
when we reimplement this, reintroduce this change to the is_final call
|
|
681
|
-
|
|
682
|
-
is_changing(): boolean {
|
|
683
|
-
return true; // todo whargarbl
|
|
684
|
-
}
|
|
685
|
-
*/
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
state_is_final(whichState: StateType): boolean {
|
|
689
|
-
return ( (this.state_is_terminal(whichState)) && (this.state_is_complete(whichState)) );
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
is_final(): boolean {
|
|
693
|
-
// return ((!this.is_changing()) && this.state_is_final(this.state()));
|
|
694
|
-
return this.state_is_final(this.state());
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
graph_layout(): string {
|
|
698
|
-
return this._graph_layout;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
dot_preamble(): string {
|
|
702
|
-
return this._dot_preamble;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
machine_author(): Array<string> {
|
|
708
|
-
return this._machine_author;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
machine_comment(): string {
|
|
712
|
-
return this._machine_comment;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
machine_contributor(): Array<string> {
|
|
716
|
-
return this._machine_contributor;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
machine_definition(): string {
|
|
720
|
-
return this._machine_definition;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
machine_language(): string {
|
|
724
|
-
return this._machine_language;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
machine_license(): string {
|
|
728
|
-
return this._machine_license;
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
machine_name(): string {
|
|
732
|
-
return this._machine_name;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
machine_version(): string {
|
|
736
|
-
return this._machine_version;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
raw_state_declarations(): Array<Object> {
|
|
740
|
-
return this._raw_state_declaration;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
state_declaration(which: StateType): JssmStateDeclaration {
|
|
744
|
-
return this._state_declarations.get(which);
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
state_declarations(): Map<StateType, JssmStateDeclaration> {
|
|
748
|
-
return this._state_declarations;
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
fsl_version(): string {
|
|
752
|
-
return this._fsl_version;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
machine_state(): JssmMachineInternalState<mDT> {
|
|
758
|
-
|
|
759
|
-
return {
|
|
760
|
-
internal_state_impl_version : 1,
|
|
761
|
-
|
|
762
|
-
actions : this._actions,
|
|
763
|
-
edge_map : this._edge_map,
|
|
764
|
-
edges : this._edges,
|
|
765
|
-
named_transitions : this._named_transitions,
|
|
766
|
-
reverse_actions : this._reverse_actions,
|
|
767
|
-
// reverse_action_targets : this._reverse_action_targets,
|
|
768
|
-
state : this._state,
|
|
769
|
-
states : this._states
|
|
770
|
-
};
|
|
771
|
-
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/*
|
|
775
|
-
load_machine_state(): boolean {
|
|
776
|
-
return false; // todo whargarbl
|
|
777
|
-
}
|
|
778
|
-
*/
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
states(): Array<StateType> {
|
|
782
|
-
return Array.from(this._states.keys());
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
state_for(whichState: StateType): JssmGenericState {
|
|
786
|
-
const state: JssmGenericState = this._states.get(whichState);
|
|
787
|
-
if (state) { return state; }
|
|
788
|
-
else { throw new Error(`no such state ${JSON.stringify(state)}`); }
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
has_state(whichState: StateType): boolean {
|
|
792
|
-
return this._states.get(whichState) !== undefined;
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
list_edges(): Array< JssmTransition<mDT> > {
|
|
798
|
-
return this._edges;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
list_named_transitions(): Map<StateType, number> {
|
|
802
|
-
return this._named_transitions;
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
list_actions(): Array<StateType> {
|
|
806
|
-
return Array.from(this._actions.keys());
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
theme(): FslTheme {
|
|
812
|
-
return this._theme; // constructor sets this to "default" otherwise
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
flow(): FslDirection {
|
|
816
|
-
return this._flow;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
get_transition_by_state_names(from: StateType, to: StateType): number {
|
|
822
|
-
|
|
823
|
-
const emg : Map<StateType, number> = this._edge_map.get(from);
|
|
824
|
-
|
|
825
|
-
if (emg) {
|
|
826
|
-
return emg.get(to);
|
|
827
|
-
} else {
|
|
828
|
-
return undefined;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
lookup_transition_for(from: StateType, to: StateType): JssmTransition<mDT> {
|
|
836
|
-
const id : number = this.get_transition_by_state_names(from, to);
|
|
837
|
-
return ((id === undefined) || (id === null))? undefined : this._edges[id];
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
list_transitions(whichState: StateType = this.state()): JssmTransitionList {
|
|
843
|
-
return {entrances: this.list_entrances(whichState), exits: this.list_exits(whichState)};
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
list_entrances(whichState: StateType = this.state()): Array<StateType> {
|
|
847
|
-
return (this._states.get(whichState)
|
|
848
|
-
|| {from: undefined}).from
|
|
849
|
-
|| [];
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
list_exits(whichState: StateType = this.state()): Array<StateType> {
|
|
853
|
-
return (this._states.get(whichState)
|
|
854
|
-
|| {to: undefined}).to
|
|
855
|
-
|| [];
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
probable_exits_for(whichState: StateType): Array< JssmTransition<mDT> > {
|
|
861
|
-
|
|
862
|
-
const wstate: JssmGenericState = this._states.get(whichState);
|
|
863
|
-
if (!(wstate)) { throw new Error(`No such state ${JSON.stringify(whichState)} in probable_exits_for`); }
|
|
864
|
-
|
|
865
|
-
const wstate_to : Array<StateType> = wstate.to,
|
|
866
|
-
|
|
867
|
-
wtf : Array< JssmTransition<mDT> > // wstate_to_filtered -> wtf
|
|
868
|
-
= wstate_to
|
|
869
|
-
.map( (ws) : JssmTransition<mDT> => this.lookup_transition_for(this.state(), ws))
|
|
870
|
-
.filter(Boolean);
|
|
871
|
-
|
|
872
|
-
return wtf;
|
|
873
|
-
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
probabilistic_transition(): boolean {
|
|
877
|
-
const selected : JssmTransition<mDT> = weighted_rand_select(this.probable_exits_for(this.state()));
|
|
878
|
-
return this.transition( selected.to );
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
probabilistic_walk(n: number): Array<StateType> {
|
|
882
|
-
return seq(n)
|
|
883
|
-
.map(() : StateType => {
|
|
884
|
-
const state_was: StateType = this.state();
|
|
885
|
-
this.probabilistic_transition();
|
|
886
|
-
return state_was;
|
|
887
|
-
})
|
|
888
|
-
.concat([this.state()]);
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
probabilistic_histo_walk(n: number): Map<StateType, number> {
|
|
892
|
-
return histograph(this.probabilistic_walk(n));
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
actions(whichState: StateType = this.state() ): Array<StateType> {
|
|
898
|
-
const wstate : Map<StateType, number> = this._reverse_actions.get(whichState);
|
|
899
|
-
if (wstate) { return Array.from(wstate.keys()); }
|
|
900
|
-
else { throw new Error(`No such state ${JSON.stringify(whichState)}`); }
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
list_states_having_action(whichState: StateType): Array<StateType> {
|
|
904
|
-
const wstate : Map<StateType, number> = this._actions.get(whichState);
|
|
905
|
-
if (wstate) { return Array.from(wstate.keys()); }
|
|
906
|
-
else { throw new Error(`No such state ${JSON.stringify(whichState)}`); }
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
// comeback
|
|
910
|
-
/*
|
|
911
|
-
list_entrance_actions(whichState: mNT = this.state() ) : Array<mNT> {
|
|
912
|
-
return [... (this._reverse_action_targets.get(whichState) || new Map()).values()] // wasteful
|
|
913
|
-
.map( (edgeId:any) => (this._edges[edgeId] : any)) // whargarbl burn out any
|
|
914
|
-
.filter( (o:any) => o.to === whichState)
|
|
915
|
-
.map( filtered => filtered.from );
|
|
916
|
-
}
|
|
917
|
-
*/
|
|
918
|
-
list_exit_actions(whichState: StateType = this.state() ): Array<StateType> { // these are mNT, not ?mNT
|
|
919
|
-
const ra_base: Map<StateType, number> = this._reverse_actions.get(whichState);
|
|
920
|
-
if (!(ra_base)) { throw new Error(`No such state ${JSON.stringify(whichState)}`); }
|
|
921
|
-
|
|
922
|
-
return Array.from(ra_base.values())
|
|
923
|
-
.map ( (edgeId: number) : JssmTransition<mDT> => this._edges[edgeId] )
|
|
924
|
-
.filter ( (o: JssmTransition<mDT>) : boolean => o.from === whichState )
|
|
925
|
-
.map ( (filtered: JssmTransition<mDT>) : StateType => filtered.action );
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
probable_action_exits(whichState: StateType = this.state() ) : Array<any> { // these are mNT // TODO FIXME no any
|
|
929
|
-
const ra_base: Map<StateType, number> = this._reverse_actions.get(whichState);
|
|
930
|
-
if (!(ra_base)) { throw new Error(`No such state ${JSON.stringify(whichState)}`); }
|
|
931
|
-
|
|
932
|
-
return Array.from(ra_base.values())
|
|
933
|
-
.map ( (edgeId: number): JssmTransition<mDT> => this._edges[edgeId] )
|
|
934
|
-
.filter ( (o: JssmTransition<mDT>): boolean => o.from === whichState )
|
|
935
|
-
.map ( (filtered): any => ( { action : filtered.action, // TODO FIXME no any
|
|
936
|
-
probability : filtered.probability
|
|
937
|
-
} )
|
|
938
|
-
);
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
// TODO FIXME test that is_unenterable on non-state throws
|
|
944
|
-
is_unenterable(whichState: StateType): boolean {
|
|
945
|
-
if (!(this.has_state(whichState))) { throw new Error(`No such state ${whichState}`); }
|
|
946
|
-
return this.list_entrances(whichState).length === 0;
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
has_unenterables(): boolean {
|
|
950
|
-
return this.states().some( (x: StateType): boolean => this.is_unenterable(x));
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
is_terminal(): boolean {
|
|
956
|
-
return this.state_is_terminal(this.state());
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// TODO FIXME test that state_is_terminal on non-state throws
|
|
960
|
-
state_is_terminal(whichState: StateType): boolean {
|
|
961
|
-
if (!(this.has_state(whichState))) { throw new Error(`No such state ${whichState}`); }
|
|
962
|
-
return this.list_exits(whichState).length === 0;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
has_terminals(): boolean {
|
|
966
|
-
return this.states().some( (x): boolean => this.state_is_terminal(x));
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
is_complete(): boolean {
|
|
972
|
-
return this.state_is_complete(this.state());
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
state_is_complete(whichState: StateType) : boolean {
|
|
976
|
-
const wstate: JssmGenericState = this._states.get(whichState);
|
|
977
|
-
if (wstate) { return wstate.complete; }
|
|
978
|
-
else { throw new Error(`No such state ${JSON.stringify(whichState)}`); }
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
has_completes(): boolean {
|
|
982
|
-
return this.states().some( (x): boolean => this.state_is_complete(x) );
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
// basic toolable hook call. convenience wrappers will follow, like
|
|
988
|
-
// hook(from, to, handler) and exit_hook(from, handler) and etc
|
|
989
|
-
set_hook(HookDesc: HookDescription) {
|
|
990
|
-
|
|
991
|
-
switch (HookDesc.kind) {
|
|
992
|
-
|
|
993
|
-
case 'hook':
|
|
994
|
-
this._hooks.set(hook_name(HookDesc.from, HookDesc.to), HookDesc.handler);
|
|
995
|
-
this._has_hooks = true;
|
|
996
|
-
break;
|
|
997
|
-
|
|
998
|
-
case 'named':
|
|
999
|
-
this._named_hooks.set(named_hook_name(HookDesc.from, HookDesc.to, HookDesc.action), HookDesc.handler);
|
|
1000
|
-
this._has_hooks = true;
|
|
1001
|
-
break;
|
|
1002
|
-
|
|
1003
|
-
// case 'entry':
|
|
1004
|
-
// console.log('TODO: Should add entry hook here');
|
|
1005
|
-
// throw 'TODO: Should add entry hook here';
|
|
1006
|
-
|
|
1007
|
-
// case 'exit':
|
|
1008
|
-
// console.log('TODO: Should add exit hook here');
|
|
1009
|
-
// throw 'TODO: Should add exit hook here';
|
|
1010
|
-
|
|
1011
|
-
default:
|
|
1012
|
-
console.log(`Unknown hook type ${(HookDesc as any).kind}, should be impossible`);
|
|
1013
|
-
throw new RangeError(`Unknown hook type ${(HookDesc as any).kind}, should be impossible`);
|
|
1014
|
-
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// remove_hook(HookDesc: HookDescription) {
|
|
1019
|
-
// throw 'TODO: Should remove hook here';
|
|
1020
|
-
// }
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
action(name: StateType, newData?: mDT): boolean {
|
|
1025
|
-
// todo whargarbl implement hooks
|
|
1026
|
-
// todo whargarbl implement data stuff
|
|
1027
|
-
// todo major incomplete whargarbl comeback
|
|
1028
|
-
if (this.valid_action(name, newData)) {
|
|
1029
|
-
|
|
1030
|
-
const edge : JssmTransition<mDT> = this.current_action_edge_for(name);
|
|
1031
|
-
|
|
1032
|
-
if (this._has_hooks) {
|
|
1033
|
-
|
|
1034
|
-
let hook_permits : boolean | undefined = undefined;
|
|
1035
|
-
|
|
1036
|
-
const nhn : string = named_hook_name(this._state, edge.to, name),
|
|
1037
|
-
maybe_hook = this._named_hooks.get(nhn);
|
|
1038
|
-
|
|
1039
|
-
if (maybe_hook === undefined) { hook_permits = true; }
|
|
1040
|
-
else { hook_permits = maybe_hook( { from: this._state, to: edge.to, action: name } ); }
|
|
1041
|
-
|
|
1042
|
-
if (hook_permits !== false) {
|
|
1043
|
-
this._state = edge.to;
|
|
1044
|
-
return true;
|
|
1045
|
-
} else {
|
|
1046
|
-
return false;
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
} else {
|
|
1050
|
-
this._state = edge.to;
|
|
1051
|
-
return true;
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
} else {
|
|
1055
|
-
return false;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
transition(newState: StateType, newData?: mDT): boolean {
|
|
1062
|
-
|
|
1063
|
-
// todo whargarbl implement hooks
|
|
1064
|
-
// todo whargarbl implement data stuff
|
|
1065
|
-
// todo major incomplete whargarbl comeback
|
|
1066
|
-
if (this.valid_transition(newState, newData)) {
|
|
1067
|
-
|
|
1068
|
-
if (this._has_hooks) {
|
|
1069
|
-
|
|
1070
|
-
let hook_permits : boolean | undefined = undefined;
|
|
1071
|
-
|
|
1072
|
-
const hn : string = hook_name(this._state, newState),
|
|
1073
|
-
maybe_hook : Function | undefined = this._hooks.get(hn);
|
|
1074
|
-
|
|
1075
|
-
if (maybe_hook === undefined) { hook_permits = true; }
|
|
1076
|
-
else { hook_permits = maybe_hook( { from: this._state, to: newState } ); }
|
|
1077
|
-
|
|
1078
|
-
if (hook_permits !== false) {
|
|
1079
|
-
this._state = newState;
|
|
1080
|
-
return true;
|
|
1081
|
-
} else {
|
|
1082
|
-
return false;
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
} else {
|
|
1086
|
-
|
|
1087
|
-
this._state = newState;
|
|
1088
|
-
return true;
|
|
1089
|
-
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
} else {
|
|
1093
|
-
return false;
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
// can leave machine in inconsistent state. generally do not use
|
|
1101
|
-
|
|
1102
|
-
force_transition(newState: StateType, newData?: mDT): boolean {
|
|
1103
|
-
|
|
1104
|
-
// todo whargarbl implement hooks
|
|
1105
|
-
// todo whargarbl implement data stuff
|
|
1106
|
-
// todo major incomplete whargarbl comeback
|
|
1107
|
-
if (this.valid_force_transition(newState, newData)) {
|
|
1108
|
-
|
|
1109
|
-
if (this._has_hooks) {
|
|
1110
|
-
|
|
1111
|
-
let hook_permits : boolean | undefined = undefined;
|
|
1112
|
-
|
|
1113
|
-
const hn : string = hook_name(this._state, newState),
|
|
1114
|
-
maybe_hook : Function | undefined = this._hooks.get(hn);
|
|
1115
|
-
|
|
1116
|
-
if (maybe_hook === undefined) { hook_permits = true; }
|
|
1117
|
-
else { hook_permits = maybe_hook({ from: this._state, to: newState, forced: true }); }
|
|
1118
|
-
|
|
1119
|
-
if (hook_permits !== false) {
|
|
1120
|
-
this._state = newState;
|
|
1121
|
-
return true;
|
|
1122
|
-
} else {
|
|
1123
|
-
return false;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
} else {
|
|
1127
|
-
|
|
1128
|
-
this._state = newState;
|
|
1129
|
-
return true;
|
|
1130
|
-
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
} else {
|
|
1134
|
-
return false;
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
current_action_for(action: StateType): number {
|
|
1142
|
-
const action_base: Map<StateType, number> = this._actions.get(action);
|
|
1143
|
-
return action_base
|
|
1144
|
-
? action_base.get(this.state())
|
|
1145
|
-
: undefined;
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
current_action_edge_for(action: StateType): JssmTransition<mDT> {
|
|
1149
|
-
const idx: number = this.current_action_for(action);
|
|
1150
|
-
if ((idx === undefined) || (idx === null)) { throw new Error(`No such action ${JSON.stringify(action)}`); }
|
|
1151
|
-
return this._edges[idx];
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
valid_action(action: StateType, _newData?: mDT): boolean { // todo comeback unignore newData
|
|
1155
|
-
// todo whargarbl implement data stuff
|
|
1156
|
-
// todo major incomplete whargarbl comeback
|
|
1157
|
-
return this.current_action_for(action) !== undefined;
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
valid_transition(newState: StateType, _newData?: mDT): boolean { // todo comeback unignore newData
|
|
1161
|
-
// todo whargarbl implement data stuff
|
|
1162
|
-
// todo major incomplete whargarbl comeback
|
|
1163
|
-
const transition_for: JssmTransition<mDT> = this.lookup_transition_for(this.state(), newState);
|
|
1164
|
-
|
|
1165
|
-
if (!(transition_for)) { return false; }
|
|
1166
|
-
if (transition_for.forced_only) { return false; }
|
|
1167
|
-
|
|
1168
|
-
return true;
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
valid_force_transition(newState: StateType, _newData?: mDT): boolean { // todo comeback unignore newData
|
|
1173
|
-
// todo whargarbl implement data stuff
|
|
1174
|
-
// todo major incomplete whargarbl comeback
|
|
1175
|
-
return (this.lookup_transition_for(this.state(), newState) !== undefined);
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
/* eslint-disable no-use-before-define */
|
|
1179
|
-
/* eslint-disable class-methods-use-this */
|
|
1180
|
-
sm(template_strings: TemplateStringsArray, ... remainder /* , arguments */): Machine<mDT> {
|
|
1181
|
-
return sm(template_strings, ... remainder);
|
|
1182
|
-
}
|
|
1183
|
-
/* eslint-enable class-methods-use-this */
|
|
1184
|
-
/* eslint-enable no-use-before-define */
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
function sm<mDT>(template_strings: TemplateStringsArray, ... remainder /* , arguments */): Machine<mDT> {
|
|
1194
|
-
|
|
1195
|
-
// foo`a${1}b${2}c` will come in as (['a','b','c'],1,2)
|
|
1196
|
-
// this includes when a and c are empty strings
|
|
1197
|
-
// therefore template_strings will always have one more el than template_args
|
|
1198
|
-
// therefore map the smaller container and toss the last one on on the way out
|
|
1199
|
-
|
|
1200
|
-
return new Machine(make(template_strings.reduce(
|
|
1201
|
-
|
|
1202
|
-
// in general avoiding `arguments` is smart. however with the template
|
|
1203
|
-
// string notation, as designed, it's not really worth the hassle
|
|
1204
|
-
|
|
1205
|
-
/* eslint-disable prefer-rest-params */
|
|
1206
|
-
(acc, val, idx): string =>
|
|
1207
|
-
`${acc}${remainder[idx-1]}${val}` // arguments[0] is never loaded, so args doesn't need to be gated
|
|
1208
|
-
/* eslint-enable prefer-rest-params */
|
|
1209
|
-
|
|
1210
|
-
)));
|
|
1211
|
-
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
export {
|
|
1219
|
-
|
|
1220
|
-
version,
|
|
1221
|
-
|
|
1222
|
-
transfer_state_properties,
|
|
1223
|
-
|
|
1224
|
-
Machine,
|
|
1225
|
-
|
|
1226
|
-
make,
|
|
1227
|
-
wrap_parse as parse,
|
|
1228
|
-
compile,
|
|
1229
|
-
|
|
1230
|
-
sm,
|
|
1231
|
-
|
|
1232
|
-
arrow_direction,
|
|
1233
|
-
arrow_left_kind,
|
|
1234
|
-
arrow_right_kind,
|
|
1235
|
-
|
|
1236
|
-
// WHARGARBL TODO these should be exported to a utility library
|
|
1237
|
-
seq,
|
|
1238
|
-
weighted_rand_select,
|
|
1239
|
-
histograph,
|
|
1240
|
-
weighted_sample_select,
|
|
1241
|
-
weighted_histo_key
|
|
1242
|
-
|
|
1243
|
-
};
|