jssm 5.45.2 → 5.48.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.
Files changed (82) hide show
  1. package/dist/es6/jssm.d.ts +3 -1
  2. package/dist/es6/jssm.js +10 -0
  3. package/dist/es6/jssm_types.d.ts +6 -5
  4. package/dist/es6/version.js +1 -1
  5. package/dist/jssm.es5.cjs.js +1 -1
  6. package/jssm.d.ts +3 -1
  7. package/jssm_types.d.ts +6 -5
  8. package/package.json +1 -1
  9. package/.codeclimate.yml +0 -22
  10. package/.editorconfig +0 -12
  11. package/.eslintrc +0 -20
  12. package/.nycrc +0 -6
  13. package/.travis.yml +0 -9
  14. package/dist/jssm.es5.iife.js +0 -1
  15. package/jest-spec.config.js +0 -27
  16. package/jest-stoch.config.js +0 -27
  17. package/rollup.config.iife.js +0 -44
  18. package/rollup.config.js +0 -44
  19. package/src/demo/index.html +0 -38
  20. package/src/demo/style.css +0 -2
  21. package/src/ts/jssm-dot.peg +0 -928
  22. package/src/ts/jssm.ts +0 -1243
  23. package/src/ts/jssm_types.ts +0 -385
  24. package/src/ts/jssm_util.ts +0 -117
  25. package/src/ts/tests/actions.spec.ts +0 -167
  26. package/src/ts/tests/arrange.spec.ts +0 -72
  27. package/src/ts/tests/arrange.stoch.ts +0 -4
  28. package/src/ts/tests/array_box_if_string.spec.ts +0 -30
  29. package/src/ts/tests/array_transitions.spec.ts +0 -129
  30. package/src/ts/tests/arrow unicode.spec.ts +0 -88
  31. package/src/ts/tests/arrow.spec.ts +0 -124
  32. package/src/ts/tests/colors.spec.ts +0 -58
  33. package/src/ts/tests/comment.spec.ts +0 -134
  34. package/src/ts/tests/compile.spec.ts +0 -79
  35. package/src/ts/tests/constants.spec.ts +0 -98
  36. package/src/ts/tests/cycles.spec.ts +0 -153
  37. package/src/ts/tests/dot_preamble.spec.ts +0 -16
  38. package/src/ts/tests/embedded_sm.spec.ts +0 -36
  39. package/src/ts/tests/flow.spec.ts +0 -22
  40. package/src/ts/tests/forced transitions.spec.ts +0 -26
  41. package/src/ts/tests/general.spec.ts +0 -933
  42. package/src/ts/tests/graph node lists.spec.ts +0 -21
  43. package/src/ts/tests/histo.spec.ts +0 -24
  44. package/src/ts/tests/hooks.spec.ts +0 -209
  45. package/src/ts/tests/language.spec.ts +0 -37
  46. package/src/ts/tests/language_data/belarussian.json +0 -14
  47. package/src/ts/tests/language_data/bengali.json +0 -16
  48. package/src/ts/tests/language_data/emoji.json +0 -22
  49. package/src/ts/tests/language_data/english.json +0 -17
  50. package/src/ts/tests/language_data/french.json +0 -17
  51. package/src/ts/tests/language_data/german.json +0 -17
  52. package/src/ts/tests/language_data/hebrew.json +0 -16
  53. package/src/ts/tests/language_data/portuguese.json +0 -13
  54. package/src/ts/tests/language_data/russian.json +0 -13
  55. package/src/ts/tests/language_data/spanish.json +0 -17
  56. package/src/ts/tests/language_data/ukrainian.json +0 -19
  57. package/src/ts/tests/layout.spec.ts +0 -29
  58. package/src/ts/tests/machine_attributes.spec.ts +0 -398
  59. package/src/ts/tests/machine_name.spec.ts +0 -14
  60. package/src/ts/tests/named lists.spec.ts +0 -24
  61. package/src/ts/tests/nominated states.spec.ts +0 -133
  62. package/src/ts/tests/parse actions.spec.ts +0 -32
  63. package/src/ts/tests/parse.spec.ts +0 -94
  64. package/src/ts/tests/probability.spec.ts +0 -146
  65. package/src/ts/tests/r639.spec.ts +0 -27
  66. package/src/ts/tests/sample_select.spec.ts +0 -173
  67. package/src/ts/tests/seq.spec.ts +0 -14
  68. package/src/ts/tests/seq.stoch.ts +0 -83
  69. package/src/ts/tests/shapes.spec.ts +0 -63
  70. package/src/ts/tests/sm_tag.spec.ts +0 -37
  71. package/src/ts/tests/special characters.spec.ts +0 -39
  72. package/src/ts/tests/state_declaration.spec.ts +0 -214
  73. package/src/ts/tests/state_style.spec.ts +0 -82
  74. package/src/ts/tests/stop light.spec.ts +0 -157
  75. package/src/ts/tests/stripes.spec.ts +0 -52
  76. package/src/ts/tests/theme.spec.ts +0 -45
  77. package/src/ts/tests/weighted_histo_key.spec.ts +0 -22
  78. package/src/ts/tests/weighted_rand_select.spec.ts +0 -27
  79. package/src/ts/tests/weighted_sample_select.spec.ts +0 -24
  80. package/src/ts/version.ts +0 -3
  81. package/tree.txt +0 -1794
  82. 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
- };