jssm 5.85.8 → 5.85.9
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/CHANGELOG.md +18 -22
- package/README.md +2 -2
- package/dist/es6/jssm.d.ts +10 -131
- package/dist/es6/jssm.js +4 -355
- package/dist/es6/jssm_compiler.d.ts +135 -0
- package/dist/es6/jssm_compiler.js +363 -0
- package/dist/es6/jssm_types.d.ts +15 -15
- package/dist/es6/version.js +1 -1
- package/dist/jssm.es5.cjs.js +1 -1
- package/dist/jssm.es5.iife.js +1 -1
- package/jssm.d.ts +10 -131
- package/jssm_compiler.d.ts +135 -0
- package/jssm_types.d.ts +15 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
1056 merges; 167 releases; Changlogging the last 10 commits; Full changelog at [CHANGELOG.long.md](CHANGELOG.long.md)
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
@@ -18,6 +18,22 @@ Published tags:
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## [Untagged] - 9/12/2022 8:18:05 PM
|
|
26
|
+
|
|
27
|
+
Commit [013999a77ce43ceed5eb982754ffe480fdddf159](https://github.com/StoneCypher/jssm/commit/013999a77ce43ceed5eb982754ffe480fdddf159)
|
|
28
|
+
|
|
29
|
+
Author: `John Haugeland <stonecypher@gmail.com>`
|
|
30
|
+
|
|
31
|
+
* Pull arrows out into modules
|
|
32
|
+
* Fixes StoneCypher/fsl#1206
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
21
37
|
|
|
22
38
|
|
|
23
39
|
|
|
@@ -159,24 +175,4 @@ Commit [6475296d979dab3d227828b80319d60c4f6ab2f5](https://github.com/StoneCypher
|
|
|
159
175
|
|
|
160
176
|
Author: `John Haugeland <stonecypher@gmail.com>`
|
|
161
177
|
|
|
162
|
-
* Reintroduce display text, lost in a bad merge
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
<a name="5__85__2" />
|
|
172
|
-
|
|
173
|
-
## [5.85.2] - 9/12/2022 10:03:27 AM
|
|
174
|
-
|
|
175
|
-
Commit [98b7b14217ea7e83550e4ed15b6b6be80799e246](https://github.com/StoneCypher/jssm/commit/98b7b14217ea7e83550e4ed15b6b6be80799e246)
|
|
176
|
-
|
|
177
|
-
Author: `John Haugeland <stonecypher@gmail.com>`
|
|
178
|
-
|
|
179
|
-
Merges [461a287, 0f3025a]
|
|
180
|
-
|
|
181
|
-
* Merge pull request #539 from StoneCypher/TrimTweet
|
|
182
|
-
* Shorten the tweet notice
|
|
178
|
+
* Reintroduce display text, lost in a bad merge
|
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Please edit the file it's derived from, instead: `./src/md/readme_base.md`
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
* Generated for version 5.85.
|
|
21
|
+
* Generated for version 5.85.9 at 9/12/2022, 9:03:57 PM
|
|
22
22
|
|
|
23
23
|
-->
|
|
24
24
|
# jssm
|
|
@@ -29,7 +29,7 @@ share online. Easy to embed.
|
|
|
29
29
|
|
|
30
30
|
Readable, useful state machines as one-liner strings.
|
|
31
31
|
|
|
32
|
-
***4,
|
|
32
|
+
***4,866 tests*** run 5,757 times. 4,857 specs with 100.0% coverage, 9 fuzz tests with 13.6% coverage. With 2,754 lines, that's about 1.8 tests per line, or 2.1 generated tests per line.
|
|
33
33
|
|
|
34
34
|
***Meet your new state machine library.***
|
|
35
35
|
|
package/dist/es6/jssm.d.ts
CHANGED
|
@@ -1,133 +1,12 @@
|
|
|
1
1
|
declare type StateType = string;
|
|
2
2
|
import { JssmGenericState, JssmGenericConfig, JssmStateConfig, JssmTransition, JssmTransitionList, // JssmTransitionRule,
|
|
3
|
-
JssmMachineInternalState,
|
|
3
|
+
JssmMachineInternalState, JssmStateDeclaration, JssmStateStyleKeyList, JssmLayout, JssmHistory, JssmSerialization, FslDirection, FslDirections, FslTheme, HookDescription, HookHandler, HookContext, HookResult, HookComplexResult } from './jssm_types';
|
|
4
4
|
import { arrow_direction, arrow_left_kind, arrow_right_kind } from './jssm_arrow';
|
|
5
|
+
import { compile, make, wrap_parse } from './jssm_compiler';
|
|
5
6
|
import { seq, unique, find_repeated, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key } from './jssm_util';
|
|
6
7
|
import * as constants from './jssm_constants';
|
|
7
8
|
declare const shapes: string[], gviz_shapes: string[], named_colors: string[];
|
|
8
9
|
import { version, build_time } from './version';
|
|
9
|
-
/*********
|
|
10
|
-
*
|
|
11
|
-
* This method wraps the parser call that comes from the peg grammar,
|
|
12
|
-
* {@link parse}. Generally neither this nor that should be used directly
|
|
13
|
-
* unless you mean to develop plugins or extensions for the machine.
|
|
14
|
-
*
|
|
15
|
-
* Parses the intermediate representation of a compiled string down to a
|
|
16
|
-
* machine configuration object. If you're using this (probably don't,) you're
|
|
17
|
-
* probably also using {@link compile} and {@link Machine.constructor}.
|
|
18
|
-
*
|
|
19
|
-
* ```typescript
|
|
20
|
-
* import { parse, compile, Machine } from 'jssm';
|
|
21
|
-
*
|
|
22
|
-
* const intermediate = wrap_parse('a -> b;', {});
|
|
23
|
-
* // [ {key:'transition', from:'a', se:{kind:'->',to:'b'}} ]
|
|
24
|
-
*
|
|
25
|
-
* const cfg = compile(intermediate);
|
|
26
|
-
* // { start_states:['a'], transitions: [{ from:'a', to:'b', kind:'legal', forced_only:false, main_path:false }] }
|
|
27
|
-
*
|
|
28
|
-
* const machine = new Machine(cfg);
|
|
29
|
-
* // Machine { _instance_name: undefined, _state: 'a', ...
|
|
30
|
-
* ```
|
|
31
|
-
*
|
|
32
|
-
* This method is mostly for plugin and intermediate tool authors, or people
|
|
33
|
-
* who need to work with the machine's intermediate representation.
|
|
34
|
-
*
|
|
35
|
-
* # Hey!
|
|
36
|
-
*
|
|
37
|
-
* Most people looking at this want either the `sm` operator or method `from`,
|
|
38
|
-
* which perform all the steps in the chain. The library's author mostly uses
|
|
39
|
-
* operator `sm`, and mostly falls back to `.from` when needing to parse
|
|
40
|
-
* strings dynamically instead of from template literals.
|
|
41
|
-
*
|
|
42
|
-
* Operator {@link sm}:
|
|
43
|
-
*
|
|
44
|
-
* ```typescript
|
|
45
|
-
* import { sm } from 'jssm';
|
|
46
|
-
*
|
|
47
|
-
* const lswitch = sm`on <=> off;`;
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* Method {@link from}:
|
|
51
|
-
*
|
|
52
|
-
* ```typescript
|
|
53
|
-
* import * as jssm from 'jssm';
|
|
54
|
-
*
|
|
55
|
-
* const toggle = jssm.from('up <=> down;');
|
|
56
|
-
* ```
|
|
57
|
-
*
|
|
58
|
-
* `wrap_parse` itself is an internal convenience method for alting out an
|
|
59
|
-
* object as the options call. Not generally meant for external use.
|
|
60
|
-
*
|
|
61
|
-
* @param input The FSL code to be evaluated
|
|
62
|
-
*
|
|
63
|
-
* @param options Things to control about the instance
|
|
64
|
-
*
|
|
65
|
-
*/
|
|
66
|
-
declare function wrap_parse(input: string, options?: Object): any;
|
|
67
|
-
/*********
|
|
68
|
-
*
|
|
69
|
-
* Compile a machine's JSON intermediate representation to a config object. If
|
|
70
|
-
* you're using this (probably don't,) you're probably also using
|
|
71
|
-
* {@link parse} to get the IR, and the object constructor
|
|
72
|
-
* {@link Machine.construct} to turn the config object into a workable machine.
|
|
73
|
-
*
|
|
74
|
-
* ```typescript
|
|
75
|
-
* import { parse, compile, Machine } from 'jssm';
|
|
76
|
-
*
|
|
77
|
-
* const intermediate = parse('a -> b;');
|
|
78
|
-
* // [ {key:'transition', from:'a', se:{kind:'->',to:'b'}} ]
|
|
79
|
-
*
|
|
80
|
-
* const cfg = compile(intermediate);
|
|
81
|
-
* // { start_states:['a'], transitions: [{ from:'a', to:'b', kind:'legal', forced_only:false, main_path:false }] }
|
|
82
|
-
*
|
|
83
|
-
* const machine = new Machine(cfg);
|
|
84
|
-
* // Machine { _instance_name: undefined, _state: 'a', ...
|
|
85
|
-
* ```
|
|
86
|
-
*
|
|
87
|
-
* This method is mostly for plugin and intermediate tool authors, or people
|
|
88
|
-
* who need to work with the machine's intermediate representation.
|
|
89
|
-
*
|
|
90
|
-
* # Hey!
|
|
91
|
-
*
|
|
92
|
-
* Most people looking at this want either the `sm` operator or method `from`,
|
|
93
|
-
* which perform all the steps in the chain. The library's author mostly uses
|
|
94
|
-
* operator `sm`, and mostly falls back to `.from` when needing to parse
|
|
95
|
-
* strings dynamically instead of from template literals.
|
|
96
|
-
*
|
|
97
|
-
* Operator {@link sm}:
|
|
98
|
-
*
|
|
99
|
-
* ```typescript
|
|
100
|
-
* import { sm } from 'jssm';
|
|
101
|
-
*
|
|
102
|
-
* const lswitch = sm`on <=> off;`;
|
|
103
|
-
* ```
|
|
104
|
-
*
|
|
105
|
-
* Method {@link from}:
|
|
106
|
-
*
|
|
107
|
-
* ```typescript
|
|
108
|
-
* import * as jssm from 'jssm';
|
|
109
|
-
*
|
|
110
|
-
* const toggle = jssm.from('up <=> down;');
|
|
111
|
-
* ```
|
|
112
|
-
*
|
|
113
|
-
* @typeparam mDT The type of the machine data member; usually omitted
|
|
114
|
-
*
|
|
115
|
-
* @param tree The parse tree to be boiled down into a machine config
|
|
116
|
-
*
|
|
117
|
-
*/
|
|
118
|
-
declare function compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>;
|
|
119
|
-
/*********
|
|
120
|
-
*
|
|
121
|
-
* An internal convenience wrapper for parsing then compiling a machine string.
|
|
122
|
-
* Not generally meant for external use. Please see {@link compile} or
|
|
123
|
-
* {@link sm}.
|
|
124
|
-
*
|
|
125
|
-
* @typeparam mDT The type of the machine data member; usually omitted
|
|
126
|
-
*
|
|
127
|
-
* @param plan The FSL code to be evaluated and built into a machine config
|
|
128
|
-
*
|
|
129
|
-
*/
|
|
130
|
-
declare function make<mDT>(plan: string): JssmGenericConfig<mDT>;
|
|
131
10
|
/*********
|
|
132
11
|
*
|
|
133
12
|
* An internal method meant to take a series of declarations and fold them into
|
|
@@ -142,7 +21,7 @@ declare function state_style_condense(jssk: JssmStateStyleKeyList): JssmStateCon
|
|
|
142
21
|
declare class Machine<mDT> {
|
|
143
22
|
_state: StateType;
|
|
144
23
|
_states: Map<StateType, JssmGenericState>;
|
|
145
|
-
_edges: Array<JssmTransition<mDT>>;
|
|
24
|
+
_edges: Array<JssmTransition<StateType, mDT>>;
|
|
146
25
|
_edge_map: Map<StateType, Map<StateType, number>>;
|
|
147
26
|
_named_transitions: Map<StateType, number>;
|
|
148
27
|
_actions: Map<StateType, Map<StateType, number>>;
|
|
@@ -217,7 +96,7 @@ declare class Machine<mDT> {
|
|
|
217
96
|
_start_state_style: JssmStateConfig;
|
|
218
97
|
_end_state_style: JssmStateConfig;
|
|
219
98
|
_state_labels: Map<string, string>;
|
|
220
|
-
constructor({ start_states, end_states, complete, transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, state_declaration, property_definition, state_property, fsl_version, dot_preamble, arrange_declaration, arrange_start_declaration, arrange_end_declaration, theme, flow, graph_layout, instance_name, history, data, default_state_config, default_active_state_config, default_hooked_state_config, default_terminal_state_config, default_start_state_config, default_end_state_config }: JssmGenericConfig<mDT>);
|
|
99
|
+
constructor({ start_states, end_states, complete, transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, state_declaration, property_definition, state_property, fsl_version, dot_preamble, arrange_declaration, arrange_start_declaration, arrange_end_declaration, theme, flow, graph_layout, instance_name, history, data, default_state_config, default_active_state_config, default_hooked_state_config, default_terminal_state_config, default_start_state_config, default_end_state_config }: JssmGenericConfig<StateType, mDT>);
|
|
221
100
|
/********
|
|
222
101
|
*
|
|
223
102
|
* Internal method for fabricating states. Not meant for external use.
|
|
@@ -598,7 +477,7 @@ declare class Machine<mDT> {
|
|
|
598
477
|
* @typeparam mDT The type of the machine data member; usually omitted
|
|
599
478
|
*
|
|
600
479
|
*/
|
|
601
|
-
list_edges(): Array<JssmTransition<mDT>>;
|
|
480
|
+
list_edges(): Array<JssmTransition<StateType, mDT>>;
|
|
602
481
|
list_named_transitions(): Map<StateType, number>;
|
|
603
482
|
list_actions(): Array<StateType>;
|
|
604
483
|
get uses_actions(): boolean;
|
|
@@ -607,7 +486,7 @@ declare class Machine<mDT> {
|
|
|
607
486
|
set themes(to: FslTheme | FslTheme[]);
|
|
608
487
|
flow(): FslDirection;
|
|
609
488
|
get_transition_by_state_names(from: StateType, to: StateType): number;
|
|
610
|
-
lookup_transition_for(from: StateType, to: StateType): JssmTransition<mDT>;
|
|
489
|
+
lookup_transition_for(from: StateType, to: StateType): JssmTransition<StateType, mDT>;
|
|
611
490
|
/********
|
|
612
491
|
*
|
|
613
492
|
* List all transitions attached to the current state, sorted by entrance and
|
|
@@ -669,7 +548,7 @@ declare class Machine<mDT> {
|
|
|
669
548
|
*
|
|
670
549
|
*/
|
|
671
550
|
list_exits(whichState?: StateType): Array<StateType>;
|
|
672
|
-
probable_exits_for(whichState: StateType): Array<JssmTransition<mDT>>;
|
|
551
|
+
probable_exits_for(whichState: StateType): Array<JssmTransition<StateType, mDT>>;
|
|
673
552
|
probabilistic_transition(): boolean;
|
|
674
553
|
probabilistic_walk(n: number): Array<StateType>;
|
|
675
554
|
probabilistic_histo_walk(n: number): Map<StateType, number>;
|
|
@@ -762,7 +641,7 @@ declare class Machine<mDT> {
|
|
|
762
641
|
post_hook_any_transition(handler: HookHandler<mDT>): Machine<mDT>;
|
|
763
642
|
post_hook_entry(to: string, handler: HookHandler<mDT>): Machine<mDT>;
|
|
764
643
|
post_hook_exit(from: string, handler: HookHandler<mDT>): Machine<mDT>;
|
|
765
|
-
edges_between(from: string, to: string): JssmTransition<mDT>[];
|
|
644
|
+
edges_between(from: string, to: string): JssmTransition<StateType, mDT>[];
|
|
766
645
|
transition_impl(newStateOrAction: StateType, newData: mDT | undefined, wasForced: boolean, wasAction: boolean): boolean;
|
|
767
646
|
/*********
|
|
768
647
|
*
|
|
@@ -1130,7 +1009,7 @@ declare class Machine<mDT> {
|
|
|
1130
1009
|
*/
|
|
1131
1010
|
force_transition(newState: StateType, newData?: mDT): boolean;
|
|
1132
1011
|
current_action_for(action: StateType): number;
|
|
1133
|
-
current_action_edge_for(action: StateType): JssmTransition<mDT>;
|
|
1012
|
+
current_action_edge_for(action: StateType): JssmTransition<StateType, mDT>;
|
|
1134
1013
|
valid_action(action: StateType, _newData?: mDT): boolean;
|
|
1135
1014
|
valid_transition(newState: StateType, _newData?: mDT): boolean;
|
|
1136
1015
|
valid_force_transition(newState: StateType, _newData?: mDT): boolean;
|
|
@@ -1183,7 +1062,7 @@ declare function sm<mDT>(template_strings: TemplateStringsArray, ...remainder: a
|
|
|
1183
1062
|
* @param ExtraConstructorFields Extra non-code configuration to pass at creation time
|
|
1184
1063
|
*
|
|
1185
1064
|
*/
|
|
1186
|
-
declare function from<mDT>(MachineAsString: string, ExtraConstructorFields?: Partial<JssmGenericConfig<mDT>> | undefined): Machine<mDT>;
|
|
1065
|
+
declare function from<mDT>(MachineAsString: string, ExtraConstructorFields?: Partial<JssmGenericConfig<StateType, mDT>> | undefined): Machine<mDT>;
|
|
1187
1066
|
declare function is_hook_complex_result<mDT>(hr: unknown): hr is HookComplexResult<mDT>;
|
|
1188
1067
|
declare function is_hook_rejection<mDT>(hr: HookResult<mDT>): boolean;
|
|
1189
1068
|
declare function abstract_hook_step<mDT>(maybe_hook: HookHandler<mDT> | undefined, hook_args: HookContext<mDT>): HookComplexResult<mDT>;
|
package/dist/es6/jssm.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// whargarbl lots of these return arrays could/should be sets
|
|
2
|
-
import { reduce as reduce_to_639 } from 'reduce-to-639-1';
|
|
3
2
|
import { circular_buffer } from 'circular_buffer_js';
|
|
4
3
|
import { FslDirections } from './jssm_types';
|
|
5
4
|
import { arrow_direction, arrow_left_kind, arrow_right_kind } from './jssm_arrow';
|
|
5
|
+
import { compile, make, wrap_parse } from './jssm_compiler';
|
|
6
|
+
// compile_rule_handler,
|
|
7
|
+
// compile_rule_transition_step,
|
|
8
|
+
// compile_rule_handle_transition,
|
|
6
9
|
import { base_theme } from './themes/jssm_base_stylesheet';
|
|
7
10
|
import { default_theme } from './themes/jssm_theme_default';
|
|
8
11
|
import { modern_theme } from './themes/jssm_theme_modern';
|
|
@@ -18,362 +21,8 @@ theme_mapping.set('bold', bold_theme);
|
|
|
18
21
|
import { seq, unique, find_repeated, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key, array_box_if_string, name_bind_prop_and_state, hook_name, named_hook_name } from './jssm_util';
|
|
19
22
|
import * as constants from './jssm_constants';
|
|
20
23
|
const { shapes, gviz_shapes, named_colors } = constants;
|
|
21
|
-
import { parse } from './fsl_parser';
|
|
22
24
|
import { version, build_time } from './version'; // replaced from package.js in build
|
|
23
25
|
import { JssmError } from './jssm_error';
|
|
24
|
-
/*********
|
|
25
|
-
*
|
|
26
|
-
* Internal method meant to perform factory assembly of an edge. Not meant for
|
|
27
|
-
* external use.
|
|
28
|
-
*
|
|
29
|
-
* @internal
|
|
30
|
-
*
|
|
31
|
-
* @typeparam mDT The type of the machine data member; usually omitted
|
|
32
|
-
*
|
|
33
|
-
*/
|
|
34
|
-
// TODO add at-param to docblock
|
|
35
|
-
function makeTransition(this_se, from, to, isRight, _wasList, _wasIndex) {
|
|
36
|
-
const kind = isRight
|
|
37
|
-
? arrow_right_kind(this_se.kind)
|
|
38
|
-
: arrow_left_kind(this_se.kind), edge = {
|
|
39
|
-
from,
|
|
40
|
-
to,
|
|
41
|
-
kind,
|
|
42
|
-
forced_only: kind === 'forced',
|
|
43
|
-
main_path: kind === 'main'
|
|
44
|
-
};
|
|
45
|
-
// if ((wasList !== undefined) && (wasIndex === undefined)) { throw new JssmError(undefined, `Must have an index if transition was in a list"); }
|
|
46
|
-
// if ((wasIndex !== undefined) && (wasList === undefined)) { throw new JssmError(undefined, `Must be in a list if transition has an index"); }
|
|
47
|
-
/*
|
|
48
|
-
if (typeof edge.to === 'object') {
|
|
49
|
-
|
|
50
|
-
if (edge.to.key === 'cycle') {
|
|
51
|
-
if (wasList === undefined) { throw new JssmError(undefined, "Must have a waslist if a to is type cycle"); }
|
|
52
|
-
const nextIndex = wrapBy(wasIndex, edge.to.value, wasList.length);
|
|
53
|
-
edge.to = wasList[nextIndex];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
*/
|
|
58
|
-
const action = isRight ? 'r_action' : 'l_action', probability = isRight ? 'r_probability' : 'l_probability';
|
|
59
|
-
if (this_se[action]) {
|
|
60
|
-
edge.action = this_se[action];
|
|
61
|
-
}
|
|
62
|
-
if (this_se[probability]) {
|
|
63
|
-
edge.probability = this_se[probability];
|
|
64
|
-
}
|
|
65
|
-
return edge;
|
|
66
|
-
}
|
|
67
|
-
/*********
|
|
68
|
-
*
|
|
69
|
-
* This method wraps the parser call that comes from the peg grammar,
|
|
70
|
-
* {@link parse}. Generally neither this nor that should be used directly
|
|
71
|
-
* unless you mean to develop plugins or extensions for the machine.
|
|
72
|
-
*
|
|
73
|
-
* Parses the intermediate representation of a compiled string down to a
|
|
74
|
-
* machine configuration object. If you're using this (probably don't,) you're
|
|
75
|
-
* probably also using {@link compile} and {@link Machine.constructor}.
|
|
76
|
-
*
|
|
77
|
-
* ```typescript
|
|
78
|
-
* import { parse, compile, Machine } from 'jssm';
|
|
79
|
-
*
|
|
80
|
-
* const intermediate = wrap_parse('a -> b;', {});
|
|
81
|
-
* // [ {key:'transition', from:'a', se:{kind:'->',to:'b'}} ]
|
|
82
|
-
*
|
|
83
|
-
* const cfg = compile(intermediate);
|
|
84
|
-
* // { start_states:['a'], transitions: [{ from:'a', to:'b', kind:'legal', forced_only:false, main_path:false }] }
|
|
85
|
-
*
|
|
86
|
-
* const machine = new Machine(cfg);
|
|
87
|
-
* // Machine { _instance_name: undefined, _state: 'a', ...
|
|
88
|
-
* ```
|
|
89
|
-
*
|
|
90
|
-
* This method is mostly for plugin and intermediate tool authors, or people
|
|
91
|
-
* who need to work with the machine's intermediate representation.
|
|
92
|
-
*
|
|
93
|
-
* # Hey!
|
|
94
|
-
*
|
|
95
|
-
* Most people looking at this want either the `sm` operator or method `from`,
|
|
96
|
-
* which perform all the steps in the chain. The library's author mostly uses
|
|
97
|
-
* operator `sm`, and mostly falls back to `.from` when needing to parse
|
|
98
|
-
* strings dynamically instead of from template literals.
|
|
99
|
-
*
|
|
100
|
-
* Operator {@link sm}:
|
|
101
|
-
*
|
|
102
|
-
* ```typescript
|
|
103
|
-
* import { sm } from 'jssm';
|
|
104
|
-
*
|
|
105
|
-
* const lswitch = sm`on <=> off;`;
|
|
106
|
-
* ```
|
|
107
|
-
*
|
|
108
|
-
* Method {@link from}:
|
|
109
|
-
*
|
|
110
|
-
* ```typescript
|
|
111
|
-
* import * as jssm from 'jssm';
|
|
112
|
-
*
|
|
113
|
-
* const toggle = jssm.from('up <=> down;');
|
|
114
|
-
* ```
|
|
115
|
-
*
|
|
116
|
-
* `wrap_parse` itself is an internal convenience method for alting out an
|
|
117
|
-
* object as the options call. Not generally meant for external use.
|
|
118
|
-
*
|
|
119
|
-
* @param input The FSL code to be evaluated
|
|
120
|
-
*
|
|
121
|
-
* @param options Things to control about the instance
|
|
122
|
-
*
|
|
123
|
-
*/
|
|
124
|
-
function wrap_parse(input, options) {
|
|
125
|
-
return parse(input, options || {});
|
|
126
|
-
}
|
|
127
|
-
/*********
|
|
128
|
-
*
|
|
129
|
-
* Internal method performing one step in compiling rules for transitions. Not
|
|
130
|
-
* generally meant for external use.
|
|
131
|
-
*
|
|
132
|
-
* @internal
|
|
133
|
-
*
|
|
134
|
-
* @typeparam mDT The type of the machine data member; usually omitted
|
|
135
|
-
*
|
|
136
|
-
*/
|
|
137
|
-
function compile_rule_transition_step(acc, from, to, this_se, next_se) {
|
|
138
|
-
const edges = [];
|
|
139
|
-
const uFrom = (Array.isArray(from) ? from : [from]), uTo = (Array.isArray(to) ? to : [to]);
|
|
140
|
-
uFrom.map((f) => {
|
|
141
|
-
uTo.map((t) => {
|
|
142
|
-
const right = makeTransition(this_se, f, t, true);
|
|
143
|
-
if (right.kind !== 'none') {
|
|
144
|
-
edges.push(right);
|
|
145
|
-
}
|
|
146
|
-
const left = makeTransition(this_se, t, f, false);
|
|
147
|
-
if (left.kind !== 'none') {
|
|
148
|
-
edges.push(left);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
const new_acc = acc.concat(edges);
|
|
153
|
-
if (next_se) {
|
|
154
|
-
return compile_rule_transition_step(new_acc, to, next_se.to, next_se, next_se.se);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
return new_acc;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
/*********
|
|
161
|
-
*
|
|
162
|
-
* Internal method performing one step in compiling rules for transitions. Not
|
|
163
|
-
* generally meant for external use.
|
|
164
|
-
*
|
|
165
|
-
* @internal
|
|
166
|
-
*
|
|
167
|
-
*/
|
|
168
|
-
function compile_rule_handle_transition(rule) {
|
|
169
|
-
return compile_rule_transition_step([], rule.from, rule.se.to, rule.se, rule.se.se);
|
|
170
|
-
}
|
|
171
|
-
/*********
|
|
172
|
-
*
|
|
173
|
-
* Internal method performing one step in compiling rules for transitions. Not
|
|
174
|
-
* generally meant for external use.
|
|
175
|
-
*
|
|
176
|
-
* @internal
|
|
177
|
-
*
|
|
178
|
-
*/
|
|
179
|
-
function compile_rule_handler(rule) {
|
|
180
|
-
if (rule.key === 'transition') {
|
|
181
|
-
return { agg_as: 'transition', val: compile_rule_handle_transition(rule) };
|
|
182
|
-
}
|
|
183
|
-
if (rule.key === 'machine_language') {
|
|
184
|
-
return { agg_as: 'machine_language', val: reduce_to_639(rule.value) };
|
|
185
|
-
}
|
|
186
|
-
// manually rehandled to make `undefined` as a property safe
|
|
187
|
-
if (rule.key === 'property_definition') {
|
|
188
|
-
const ret = { agg_as: 'property_definition', val: { name: rule.name } };
|
|
189
|
-
if (rule.hasOwnProperty('default_value')) {
|
|
190
|
-
ret.val.default_value = rule.default_value;
|
|
191
|
-
}
|
|
192
|
-
if (rule.hasOwnProperty('required')) {
|
|
193
|
-
ret.val.required = rule.required;
|
|
194
|
-
}
|
|
195
|
-
return ret;
|
|
196
|
-
}
|
|
197
|
-
// state properties are in here
|
|
198
|
-
if (rule.key === 'state_declaration') {
|
|
199
|
-
if (!rule.name) {
|
|
200
|
-
throw new JssmError(undefined, 'State declarations must have a name');
|
|
201
|
-
}
|
|
202
|
-
return { agg_as: 'state_declaration', val: { state: rule.name, declarations: rule.value } };
|
|
203
|
-
}
|
|
204
|
-
if (['arrange_declaration', 'arrange_start_declaration',
|
|
205
|
-
'arrange_end_declaration'].includes(rule.key)) {
|
|
206
|
-
return { agg_as: rule.key, val: [rule.value] };
|
|
207
|
-
}
|
|
208
|
-
// things that can only exist once and are just a value under their own name
|
|
209
|
-
const tautologies = [
|
|
210
|
-
'graph_layout', 'start_states', 'end_states', 'machine_name', 'machine_version',
|
|
211
|
-
'machine_comment', 'machine_author', 'machine_contributor', 'machine_definition',
|
|
212
|
-
'machine_reference', 'machine_license', 'fsl_version', 'state_config', 'theme',
|
|
213
|
-
'flow', 'dot_preamble', 'default_state_config', 'default_start_state_config',
|
|
214
|
-
'default_end_state_config', 'default_hooked_state_config',
|
|
215
|
-
'default_active_state_config', 'default_terminal_state_config'
|
|
216
|
-
];
|
|
217
|
-
if (tautologies.includes(rule.key)) {
|
|
218
|
-
return { agg_as: rule.key, val: rule.value };
|
|
219
|
-
}
|
|
220
|
-
throw new JssmError(undefined, `compile_rule_handler: Unknown rule: ${JSON.stringify(rule)}`);
|
|
221
|
-
}
|
|
222
|
-
/*********
|
|
223
|
-
*
|
|
224
|
-
* Compile a machine's JSON intermediate representation to a config object. If
|
|
225
|
-
* you're using this (probably don't,) you're probably also using
|
|
226
|
-
* {@link parse} to get the IR, and the object constructor
|
|
227
|
-
* {@link Machine.construct} to turn the config object into a workable machine.
|
|
228
|
-
*
|
|
229
|
-
* ```typescript
|
|
230
|
-
* import { parse, compile, Machine } from 'jssm';
|
|
231
|
-
*
|
|
232
|
-
* const intermediate = parse('a -> b;');
|
|
233
|
-
* // [ {key:'transition', from:'a', se:{kind:'->',to:'b'}} ]
|
|
234
|
-
*
|
|
235
|
-
* const cfg = compile(intermediate);
|
|
236
|
-
* // { start_states:['a'], transitions: [{ from:'a', to:'b', kind:'legal', forced_only:false, main_path:false }] }
|
|
237
|
-
*
|
|
238
|
-
* const machine = new Machine(cfg);
|
|
239
|
-
* // Machine { _instance_name: undefined, _state: 'a', ...
|
|
240
|
-
* ```
|
|
241
|
-
*
|
|
242
|
-
* This method is mostly for plugin and intermediate tool authors, or people
|
|
243
|
-
* who need to work with the machine's intermediate representation.
|
|
244
|
-
*
|
|
245
|
-
* # Hey!
|
|
246
|
-
*
|
|
247
|
-
* Most people looking at this want either the `sm` operator or method `from`,
|
|
248
|
-
* which perform all the steps in the chain. The library's author mostly uses
|
|
249
|
-
* operator `sm`, and mostly falls back to `.from` when needing to parse
|
|
250
|
-
* strings dynamically instead of from template literals.
|
|
251
|
-
*
|
|
252
|
-
* Operator {@link sm}:
|
|
253
|
-
*
|
|
254
|
-
* ```typescript
|
|
255
|
-
* import { sm } from 'jssm';
|
|
256
|
-
*
|
|
257
|
-
* const lswitch = sm`on <=> off;`;
|
|
258
|
-
* ```
|
|
259
|
-
*
|
|
260
|
-
* Method {@link from}:
|
|
261
|
-
*
|
|
262
|
-
* ```typescript
|
|
263
|
-
* import * as jssm from 'jssm';
|
|
264
|
-
*
|
|
265
|
-
* const toggle = jssm.from('up <=> down;');
|
|
266
|
-
* ```
|
|
267
|
-
*
|
|
268
|
-
* @typeparam mDT The type of the machine data member; usually omitted
|
|
269
|
-
*
|
|
270
|
-
* @param tree The parse tree to be boiled down into a machine config
|
|
271
|
-
*
|
|
272
|
-
*/
|
|
273
|
-
function compile(tree) {
|
|
274
|
-
const results = {
|
|
275
|
-
graph_layout: [],
|
|
276
|
-
transition: [],
|
|
277
|
-
start_states: [],
|
|
278
|
-
end_states: [],
|
|
279
|
-
state_config: [],
|
|
280
|
-
state_declaration: [],
|
|
281
|
-
fsl_version: [],
|
|
282
|
-
machine_author: [],
|
|
283
|
-
machine_comment: [],
|
|
284
|
-
machine_contributor: [],
|
|
285
|
-
machine_definition: [],
|
|
286
|
-
machine_language: [],
|
|
287
|
-
machine_license: [],
|
|
288
|
-
machine_name: [],
|
|
289
|
-
machine_reference: [],
|
|
290
|
-
property_definition: [],
|
|
291
|
-
state_property: {},
|
|
292
|
-
theme: [],
|
|
293
|
-
flow: [],
|
|
294
|
-
dot_preamble: [],
|
|
295
|
-
arrange_declaration: [],
|
|
296
|
-
arrange_start_declaration: [],
|
|
297
|
-
arrange_end_declaration: [],
|
|
298
|
-
machine_version: [],
|
|
299
|
-
default_state_config: [],
|
|
300
|
-
default_active_state_config: [],
|
|
301
|
-
default_hooked_state_config: [],
|
|
302
|
-
default_terminal_state_config: [],
|
|
303
|
-
default_start_state_config: [],
|
|
304
|
-
default_end_state_config: [],
|
|
305
|
-
};
|
|
306
|
-
tree.map((tr) => {
|
|
307
|
-
const rule = compile_rule_handler(tr), agg_as = rule.agg_as, val = rule.val; // TODO FIXME no any
|
|
308
|
-
results[agg_as] = results[agg_as].concat(val);
|
|
309
|
-
});
|
|
310
|
-
const property_keys = results['property_definition'].map(pd => pd.name), repeat_props = find_repeated(property_keys);
|
|
311
|
-
if (repeat_props.length) {
|
|
312
|
-
throw new JssmError(undefined, `Cannot repeat property definitions. Saw ${JSON.stringify(repeat_props)}`);
|
|
313
|
-
}
|
|
314
|
-
const assembled_transitions = [].concat(...results['transition']);
|
|
315
|
-
const result_cfg = {
|
|
316
|
-
start_states: results.start_states.length ? results.start_states : [assembled_transitions[0].from],
|
|
317
|
-
end_states: results.end_states,
|
|
318
|
-
transitions: assembled_transitions,
|
|
319
|
-
state_property: [],
|
|
320
|
-
};
|
|
321
|
-
const oneOnlyKeys = [
|
|
322
|
-
'graph_layout', 'machine_name', 'machine_version', 'machine_comment',
|
|
323
|
-
'fsl_version', 'machine_license', 'machine_definition', 'machine_language',
|
|
324
|
-
'flow', 'dot_preamble'
|
|
325
|
-
];
|
|
326
|
-
oneOnlyKeys.map((oneOnlyKey) => {
|
|
327
|
-
if (results[oneOnlyKey].length > 1) {
|
|
328
|
-
throw new JssmError(undefined, `May only have one ${oneOnlyKey} statement maximum: ${JSON.stringify(results[oneOnlyKey])}`);
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
if (results[oneOnlyKey].length) {
|
|
332
|
-
result_cfg[oneOnlyKey] = results[oneOnlyKey][0];
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
['arrange_declaration', 'arrange_start_declaration', 'arrange_end_declaration',
|
|
337
|
-
'machine_author', 'machine_contributor', 'machine_reference', 'theme',
|
|
338
|
-
'state_declaration', 'property_definition', 'default_state_config',
|
|
339
|
-
'default_start_state_config', 'default_end_state_config',
|
|
340
|
-
'default_hooked_state_config', 'default_terminal_state_config',
|
|
341
|
-
'default_active_state_config'].map((multiKey) => {
|
|
342
|
-
if (results[multiKey].length) {
|
|
343
|
-
result_cfg[multiKey] = results[multiKey];
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
// re-walk state declarations, already wrapped up, to get state properties,
|
|
347
|
-
// which go out in a different datastructure
|
|
348
|
-
results.state_declaration.forEach(sd => {
|
|
349
|
-
sd.declarations.forEach(decl => {
|
|
350
|
-
if (decl.key === 'state_property') {
|
|
351
|
-
const label = name_bind_prop_and_state(decl.name, sd.state);
|
|
352
|
-
if (result_cfg.state_property.findIndex(c => c.name === label) !== -1) {
|
|
353
|
-
throw new JssmError(undefined, `A state may only bind a property once (${sd.state} re-binds ${decl.name})`);
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
result_cfg.state_property.push({ name: label, default_value: decl.value });
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
return result_cfg;
|
|
362
|
-
}
|
|
363
|
-
/*********
|
|
364
|
-
*
|
|
365
|
-
* An internal convenience wrapper for parsing then compiling a machine string.
|
|
366
|
-
* Not generally meant for external use. Please see {@link compile} or
|
|
367
|
-
* {@link sm}.
|
|
368
|
-
*
|
|
369
|
-
* @typeparam mDT The type of the machine data member; usually omitted
|
|
370
|
-
*
|
|
371
|
-
* @param plan The FSL code to be evaluated and built into a machine config
|
|
372
|
-
*
|
|
373
|
-
*/
|
|
374
|
-
function make(plan) {
|
|
375
|
-
return compile(wrap_parse(plan));
|
|
376
|
-
}
|
|
377
26
|
/*********
|
|
378
27
|
*
|
|
379
28
|
* An internal method meant to take a series of declarations and fold them into
|