From c95411f2a86da0857f7927fa0fc63a7256b0f296 Mon Sep 17 00:00:00 2001 From: John Haugeland Date: Mon, 2 May 2022 22:39:05 -0700 Subject: [PATCH 1/5] underway --- benchmark/results/general.chart.html | 14 +- benchmark/results/general.json | 12 +- dist/es6/jssm.d.ts | 6 +- dist/es6/jssm.js | 26 + dist/es6/jssm_types.d.ts | 26 +- dist/es6/version.js | 2 +- dist/jssm.es5.cjs.js | 2 +- dist/jssm.es5.cjs.nonmin.js | 28 +- dist/jssm.es5.iife.js | 2 +- dist/jssm.es5.iife.nonmin.js | 28 +- docs/_config.yml | 1 - docs/docs/.nojekyll | 1 + docs/docs/assets/highlight.css | 85 ++ docs/docs/assets/icons.css | 1043 +++++++++++++++++++ docs/docs/assets/icons.png | Bin 0 -> 9615 bytes docs/docs/assets/icons@2x.png | Bin 0 -> 28144 bytes docs/docs/assets/main.js | 52 + docs/docs/assets/search.js | 1 + docs/docs/assets/style.css | 1414 ++++++++++++++++++++++++++ docs/docs/assets/widgets.png | Bin 0 -> 480 bytes docs/docs/assets/widgets@2x.png | Bin 0 -> 855 bytes docs/docs/classes/Machine.html | 1 + docs/docs/index.html | 850 ++++++++++++++++ docs/docs/modules.html | 1 + docs/index.html | 47 + docs/index.md | 37 - jssm.d.ts | 6 +- jssm_types.d.ts | 26 +- package.json | 2 +- src/ts/jssm.ts | 73 +- src/ts/jssm_types.ts | 41 +- src/ts/version.ts | 2 +- 32 files changed, 3761 insertions(+), 68 deletions(-) delete mode 100644 docs/_config.yml create mode 100644 docs/docs/.nojekyll create mode 100644 docs/docs/assets/highlight.css create mode 100644 docs/docs/assets/icons.css create mode 100644 docs/docs/assets/icons.png create mode 100644 docs/docs/assets/icons@2x.png create mode 100644 docs/docs/assets/main.js create mode 100644 docs/docs/assets/search.js create mode 100644 docs/docs/assets/style.css create mode 100644 docs/docs/assets/widgets.png create mode 100644 docs/docs/assets/widgets@2x.png create mode 100644 docs/docs/classes/Machine.html create mode 100644 docs/docs/index.html create mode 100644 docs/docs/modules.html create mode 100644 docs/index.html delete mode 100644 docs/index.md diff --git a/benchmark/results/general.chart.html b/benchmark/results/general.chart.html index 1951c052..d20e0f47 100644 --- a/benchmark/results/general.chart.html +++ b/benchmark/results/general.chart.html @@ -28,7 +28,7 @@
- +
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • remove_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/docs/index.html b/docs/docs/index.html new file mode 100644 index 00000000..9df5393c --- /dev/null +++ b/docs/docs/index.html @@ -0,0 +1,850 @@ +jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

+ +

jssm

+
+

Easy. Small. Fast. TS, es6, es5. Node, Browser. 100% coverage. Property +tests. Fuzz tests. Language tests for a dozen languages and emoji. Easy to +share online. Easy to embed.

+

Readable, useful state machines as one-liner strings.

+

Meet your new state machine library.

+ + +

TRY THE LIVE EDITOR

+ +

Discord community - Documentation - Issue tracker - CI build history

+

Discord community

+



+

Wouldn't it be nice if your TypeScript and Javascript state machines were simple and readable one-liners?

+
import { sm } from 'jssm';

const TrafficLight = sm`Red -> Green -> Yellow -> Red;`; +
+
+ +

Wouldn't it be great if they were easy to work with?

+
const log = s => console.log(s);

log( TrafficLight.state() ); // 'Red'

Machine.transition('Green'); // true
log( TrafficLight.state() ); // 'Green' +
+
+ +

What if the notation supported action names easily?

+
const TrafficLightWithActions = sm`Red 'next' -> Green 'next' -> Yellow 'next' -> Red;`;

log( TrafficLightWithActions.state() ); // 'Red'

TrafficLightWithActions.action('next'); // true
log( TrafficLightWithActions.state() ); // 'Green'

TrafficLightWithActions.action('next'); // true
log( TrafficLightWithActions.state() ); // 'Yellow'

TrafficLightWithActions.action('next'); // true
log( TrafficLightWithActions.state() ); // 'Red' +
+
+ +

What if the machine followed JS standards, and distinguished refusals as false from mistakes as thrown?

+
const AnotherTrafficLight = sm`Red -> Green -> Yellow -> Red;`;

log( AnotherTrafficLight.state() ); // 'Red' - uses 1st state unless told otherwise
AnotherTrafficLight.transition('Yellow'); // false (Yellow isn't allowed from Red)
AnotherTrafficLight.transition('Blue'); // throws (Blue isn't a state at all) +
+
+ +

What if there were easy convenience notations for lists, and for designating main-path => vs available path -> vs +only-when-forced ~> ?

+
const TrafficLightWithOff = sm`
Red => Green => Yellow => Red;
[Red Yellow Green] ~> Off -> Red;
`; +
+
+ +

What if that were easy to render visually?

+
const TrafficLightWithOff = sm`
Red => Green => Yellow => Red;
[Red Yellow Green] ~> Off -> Red;
`; +
+
+ + + +
+ +

What if that were easy to render visually, with styling, in PNG, JPEG, or SVG?

+
const TrafficLightWithOff = sm`
Red => Green => Yellow => Red;
[Red Yellow Green] ~> Off -> Red;

flow: left;

state Red : { background-color: pink; corners: rounded; };
state Yellow : { background-color: lightyellow; corners: rounded; };
state Green : { background-color: lightgreen; corners: rounded; };

state Off : {
background-color : steelblue;
text-color : white;
shape : octagon;
linestyle : dashed;
};
`; +
+
+ + + +
+ +

What if the machine was lighting fast, able to do tens of millions of transitions per second?

+ + +
+ +
    +
  • What if the machine and language had extensive 100% test coverage +with thousands of cases?
  • +
  • What if the machine gave extensive Typescript introspection support?
  • +
  • What if the machine had been around and active since May 2017?
  • +
  • What if the machine was MIT licensed, end to end?
  • +
+

But, above all else:

+

What if it was easy?

+



+ + +

Introducing JSSM

+
+

Meet JSSM: the Javascript State Machine.

+

State machines can make your code cleaner, safer, and more trustworthy.

+

And, with the right language, state machines can be easy and fun.

+

TRY THE LIVE EDITOR

+
+ + + +

What is JSSM?

+
+

JSSM is a Javascript state machine implementing Finite State Language, with a terse DSL and a simple API. +100% test coverage; typed with Flowtype. MIT licensed.

+

The NPM package includes pure es6, a cjs es5 bundle, and .d.ts typings. The repository includes the original typescript, the bundle, the es6, documentation, tests, tutorials, and so on.

+

Try it live!

+

Visualize with jssm-viz, or at the command line with jssm-viz-cli.

+

Language test cases for Belorussian, English, German, Hebrew, Italian, Russian, Spanish, Ukrainian, and Emoji. Please help to make sure that your language is well handled!

+
+ +

Actions Status

+

GitHub forks +GitHub watchers +GitHub stars +GitHub followers

+

License +Open issues +Closed issues +Travis status +Coveralls status

+

NPM version +CDNjs version +NPM downloads

+ + +
+ + + +



+ + +

TL;DR

+
+

Specify finite state machines with a brief syntax. Run them; they're fast. Make mistakes; they're strict. Derive +charts. Save and load states, and histories. Make machine factories to churn out dozens or thousands of instances. +Impress friends and loved ones. Cure corns and callouses.

+
Red 'Proceed' -> Green 'Proceed' -> Yellow 'Proceed' -> Red;
+
+

This will produce the following FSM (graphed with jssm-viz):

+

+

You'll build an executable state machine.

+

+



+ + +

Why

+
+

As usual, a valid question.

+
+ + + +

Why state machines

+
+

State machines are a method of making your software better able to prevent illegal states. Similar to type systems, SQL +constraints, and linters, state machines are a way to teach the software to catch mistakes in ways you define, to help +lead to better software.

+

The major mechanism of a state machine is to define states, the transitions between them, and sometimes associated +data and other niceties. The minor mechanism of state machines is to attach actions to the transitions, such that +the state machine can partially run itself.

+

+

So, to look at the same traffic light as above, you'll notice some things.

+
    +
  1. A sufficiently smart implementation will know that it's okay for Green to switch to Yellow, but not to Red
  2. +
  3. A sufficiently smart implementation knows there's no such thing as Blue
  4. +
  5. A sufficiently smart implementation knows that when in Green, to be told to Proceed means to go to Yellow, but +when in Yellow, it means to go to Red instead
  6. +
+

Along with other common sense things, a good state machine implementation can help eliminate large classes of error in +software. State machines are often applied when the stakes on having things correct are high.

+
+ + + +

Why this implementation

+
+

Brevity.

+

High quality testing. JSSM has 100% coverage, and has partial stochastic test coverage.

+

Feature parity, especially around the DSL and data control.

+

Data integrity. JSSM allows a much stricter form of state machine than is common, with a relatively low performance +and storage overhead. It also offers an extremely terse domain specific language (though it does not require said DSL) +to produce state machines in otherwise comparatively tiny and easily read code.

+



+ + +

Quick Start

+
+
+

A state machine in JSSM is defined in one of two ways: through the DSL, or through a datastructure.

+
+

So yeah, let's start by getting some terminology out of the way, and then we can go right back to that impenetrable +sentence, so that it'll make sense.

+
+ + + +

Quick Terminology

+
+

Finite state machines have been around forever, are used by everyone, and are hugely important. As a result, the +terminology is a mess, is in conflict, and is very poorly chosen, in accordince with everything-is-horrible law.

+

This section describes the terminology as used by this library. The author has done his best to choose a terminology +that matches common use and will be familiar to most. Conflicts are explained in the following section, to keep this +simple.

+

For this quick overview, we'll define six basic concepts:

+
    +
  1. Finite state machines
  2. +
  3. Machines
  4. +
  5. States
  6. +
  7. Current state
  8. +
  9. Transitions
  10. +
  11. Actions
  12. +
+

There's other stuff, of course, but these five are enough to wrap your head around finite state machines.

+
+ + + +

Basic concepts

+
+

This is a trivial traffic light FSM, with three states, three transitions, and one action:

+
Red 'Proceed' -> Green 'Proceed' -> Yellow 'Proceed' -> Red;
+
+

+

Let's review its pieces.

+
    +
  • finite state machines

    +
      +
    • A finite state machine (or FSM) is a collection of states, and rules about how you can transition between +the states.
    • +
    • We tend to refer to a design for a machine as "an FSM."
    • +
    • In this example, the traffic light's structure is "a traffic light FSM."
    • +
    +
  • +
  • states

    +
      +
    • FSMs always have at least one state, and nearly always many states
    • +
    • In this example,
        +
      • the states are Red, Yellow, and Green
      • +
      • Something made from this FSM will only ever be one of those colors - not, say, Blue
      • +
      +
    • +
    +
  • +
  • machines

    +
      +
    • Single instances of an FSM are referred to as a machine
    • +
    • We might have a thousand instances of the traffic light designed above
    • +
    • We would say "My intersection has four machines of the standard three color light FSM."
    • +
    +
  • +
  • current state

    +
      +
    • A machine has a current state, though an FSM does not
        +
      • "This specific traffic light is currently Red"
      • +
      +
    • +
    • Traffic lights in general do not have a current color, only specific lights
    • +
    • FSMs do not have a current state, only specific machines
    • +
    • A given machine will always have exactly one state - never multiple, never none
    • +
    +
  • +
  • transitions

    +
      +
    • FSMs nearly always have transitions
    • +
    • Transitions govern whether a state may be reached from another state
        +
      • This restriction is much of the value of FSMs
      • +
      +
    • +
    • In this example,
        +
      • the transitions are
          +
        • GreenYellow
        • +
        • YellowRed
        • +
        • RedGreen
        • +
        +
      • +
      • a machine whose current state is Green may switch to Yellow, because there is an appropriate transition
      • +
      • a machine whose current state is Green may not switch to Red, or to Green anew, because there is no +such transition
          +
        • A machine in Yellow which is told to transition to Green (which isn't legal) will know to refuse
        • +
        • This makes FSMs an effective tool for error prevention
        • +
        +
      • +
      +
    • +
    +
  • +
  • actions

    +
      +
    • Many FSMs have actions, which represent events from the outside world.
    • +
    • In this example, there is only one action - Proceed
        +
      • The action Proceed is available from all three colors
      • +
      +
    • +
    • At any time we may indicate to this light to go to its next color, without +taking the time to know what it is.
        +
      • This allows FSMs like the light to self-manage.
      • +
      • A machine in Yellow which is told to take the action Proceed will +know on its own to switch its current state to Red.
      • +
      • This makes FSMs an effective tool for complexity reduction
      • +
      +
    • +
    +
  • +
+

Those six ideas in hand - FSMs, states, machines, current state, transitions, and actions - and you're ready +to move forwards.

+

One other quick definition - a DSL, or domain specific language, is when someone makes a language and embeds it into +a different language, for the purpose of attacking a specific job. When React uses a precompiler to embed stuff that +looks like HTML in Javascript, that's a DSL.

+

This library implements a simple language for defining finite state machines inside of strings. For example, this +DSL defines that 'a -> b;' actually means "create two states, create a transition between them, assign the first as +the initial state", et cetera. That micro-language is the DSL that we'll be referring to a lot, coming up. This +DSL's formal name is jssm-dot, because it's a descendant-in-spirit of an older flowcharting language +DOT, from graphviz, which is also used to make the +visualizations in jssm-viz by way of viz-js.

+

Enough history lesson. On with the tooling.

+
+ + + +

And now, that Quick Start we were talking about

+
+

So let's put together a trivial four-state traffic light: the three colors, plus Off. This will give us an +opportunity to go over the basic facilities in the language.

+

At any time, you can take the code and put it into the +graph explorer for an opportunity to mess with the +code as you see fit.

+
+ + + +

0: Lights always have an off state

+
+

Our light will start in the Off state, with the ability to switch to the Red state.

+

Since that's a normal, not-notable thing, we'll just make it a regular -> legal transition.

+
Off -> Red;
+
+

We will give that transition an action, and call it TurnOn.

+
Off 'TurnOn' -> Red;
+
+

So far, our machine is simple:

+

+
+ + + +

1: Traffic lights have a three-color cycle

+
+

The main path of a traffic light is cycling from Green to Yellow, then to Red, then back again. Because +this is the main path, we'll mark these steps => main transitions.

+
Off 'TurnOn' -> Red => Green => Yellow => Red;
+
+

We will give those all the same action name, Proceed, indicating "next color" without needing to know what we're +currently on.

+
Off 'TurnOn' -> Red 'Proceed' => Green 'Proceed' => Yellow 'Proceed' => Red;
+
+

Machine's still pretty simple:

+

+
+ + + +

2: Traffic lights can be shut down

+
+

We'd also like to be able to turn this light back off. Because that's expected to be a rarity, we'll require that it +be a ~> forced transition.

+

We could write

+
Off 'TurnOn' -> Red 'Proceed' => Green 'Proceed' => Yellow 'Proceed' => Red;
Red ~> Off;
Yellow ~> Off;
Green ~> Off; +
+

But that takes a lot of space even with this short list, so, instead we'll use the array notation

+
Off 'TurnOn' -> Red 'Proceed' => Green 'Proceed' => Yellow 'Proceed' => Red;
[Red Yellow Green] ~> Off; +
+

And we'd like those all to have the action TurnOff, so

+
Off 'TurnOn' -> Red 'Proceed' => Green 'Proceed' => Yellow 'Proceed' => Red;
[Red Yellow Green] 'TurnOff' ~> Off; +
+

Machine's still not too bad:

+

+
+ + + +

Let's actually use the traffic light

+
+

That's actually the bulk of the language. There are other little add-ons here and there, but, primarily you now know +how to write a state machine.

+

Let's load it and use it! 😀

+ + +

loading into node

+
+ + +

loading into html

+
+ + +

jssm-viz

+
+ + +

redistribution on npm

+
+
+ + + +

An introduction to machine design

+
+

Let's make a state machine for ATMs. In the process, we will use a lot of core concepts of finite state machines +and of jssm-dot, this library's DSL.

+

We're going to improve on this NCSU ATM diagram that I +found:

+

+

Remember, at any time, you can take the code and put it into the +graph explorer for an opportunity to mess with the +code as you see fit.

+
+ + + +

0: Empty machine

+
+

We'll start with an empty machine.

+
EmptyWaiting 'Wait' -> EmptyWaiting;
+
+

+
+ + + +

1: Should be able to eject cards

+
+

We'll add the ability to physically eject the user's card and reset to the empty and waiting state. Right now it'll +dangle around un-used at the top, but later it'll become useful.

+

This is expressed as the path EjectCardAndReset -> EmptyWaiting;

+
EmptyWaiting 'Wait' -> EmptyWaiting;
EjectCardAndReset -> EmptyWaiting; +
+

+
+ + + +

2: Should be able to insert cards

+
+

We'll add the ability to physically insert a card, next. You know, the, uh, thing ATMs are pretty much for.

+

To get this, add the path leg EmptyWaiting 'InsertCard' -> HasCardNoAuth;

+
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;
EjectCardAndReset -> EmptyWaiting; +
+

Notice that the new state, HasCardNoAuth, has been rendered red. This is because it is terminal - there is +no exit from this node currently. (EmptyAndWaiting did not render that way because it had a transition to itself.) +That will change as we go back to adding more nodes. terminal nodes are usually either mistakes or the last single +state of a given FSM.

+

+
+ + + +

3: Should be able to cancel and recover the card

+
+

Next, we should have a cancel, because the ATM's 7 key is broken, and we need our card back. Cancel will +exit to the main menu, and return our card credential.

+

To that end, we add the path HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;

+
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;

HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;

EjectCardAndReset -> EmptyWaiting; +
+

+
+ + + +

4: Can give the wrong PIN

+
+

Next, let's give the ability to get the password ... wrong. 😂 Because we all know that one ATM that only has the +wrong-PIN path, so, apparently that's a product to someone.

+

When they get the PIN wrong, they're prompted to try again (or to cancel.)

+

We'll add the path HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;

+
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;

HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;

EjectCardAndReset -> EmptyWaiting; +
+

+
+ + + +

5: Can give the correct PIN

+
+

Next, let's give the ability to get the password right.

+

We'll add two paths. The first gets the password right: HasCardNoAuth 'RightPIN' -> MainMenu;

+

The second, from our new state MainMenu, gives people the ability to leave: MainMenu 'ExitReturnCard' -> EjectCardAndReset;

+
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;

HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
HasCardNoAuth 'RightPIN' -> MainMenu;

MainMenu 'ExitReturnCard' -> EjectCardAndReset;

EjectCardAndReset -> EmptyWaiting; +
+

+
+ + + +

6: Can check balance from main menu

+
+

Hooray, now we're getting somewhere.

+

Let's add the ability to check your balance. First pick that from the main menu, then pick which account to see the +balance of, then you're shown a screen with the information you requested; then go back to the main menu.

+

That's MainMenu 'CheckBalance' -> PickAccount -> DisplayBalance -> MainMenu;.

+
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;

HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
HasCardNoAuth 'RightPIN' -> MainMenu;

MainMenu 'ExitReturnCard' -> EjectCardAndReset;
MainMenu 'CheckBalance' -> PickAccount -> DisplayBalance -> MainMenu;

EjectCardAndReset -> EmptyWaiting; +
+

+
+ + + +

7: Can deposit money from main menu

+
+

Let's add something difficult. Their state machine just proceeds assuming everything is okay.

+

To desposit money:

+
    +
  1. Accept physical money
  2. +
  3. If accept failed (eg door jammed,) reject physical object, go to main menu
  4. +
  5. If accept succeeded, ask human expected value
  6. +
  7. Pick an account this should go into
  8. +
  9. Contact bank. Request to credit for theoretical physical money.
  10. +
  11. Three results: yes, no, offer-after-audit.
  12. +
  13. If no, reject physical object, go to main menu.
  14. +
  15. If yes, consume physical object, tell user consumed, go to main menu
  16. +
  17. If offer-after-audit, ask human what to do
  18. +
  19. if human-yes, consume physical object, tell user consumed, go to main menu
  20. +
  21. if human-no, reject physical object, go to main menu
  22. +
+

Writing this out in code is not only generally longer than the text form, but also error prone and hard to maintain.

+

... or there's the FSM DSL, which is usually as-brief-as the text, and frequently both briefer and more explicit.

+
    +
  • Rules 1-2: MainMenu 'AcceptDeposit' -> TentativeAcceptMoney 'AcceptFail' -> RejectPhysicalMoney -> MainMenu;
  • +
  • Rules 3-6: TentativeAcceptMoney 'AcceptSucceed' -> PickDepositAccount -> RequestValue 'TellBank' -> BankResponse;
  • +
  • Rule 7: BankResponse 'BankNo' -> RejectPhysicalMoney;
  • +
  • Rule 8: BankResponse 'BankYes' -> ConsumeMoney -> NotifyConsumed -> MainMenu;
  • +
  • Rules 9-10: BankResponse 'BankAudit' -> BankAuditOffer 'HumanAcceptAudit' -> ConsumeMoney;
  • +
  • Rule 11: BankAuditOffer 'HumanRejectAudit' -> RejectPhysicalMoney;
  • +
+

Or, as a block,

+
MainMenu 'AcceptDeposit' -> TentativeAcceptMoney;

TentativeAcceptMoney 'AcceptFail' -> RejectPhysicalMoney -> MainMenu;
TentativeAcceptMoney 'AcceptSucceed' -> PickDepositAccount -> RequestValue 'TellBank' -> BankResponse;

BankResponse 'BankNo' -> RejectPhysicalMoney;
BankResponse 'BankYes' -> ConsumeMoney -> NotifyConsumed -> MainMenu;
BankResponse 'BankAudit' -> BankAuditOffer 'HumanAcceptAudit' -> ConsumeMoney;

BankAuditOffer 'HumanRejectAudit' -> RejectPhysicalMoney; +
+

Which leaves us with the total code

+
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;

HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
HasCardNoAuth 'RightPIN' -> MainMenu;

MainMenu 'AcceptDeposit' -> TentativeAcceptMoney;
MainMenu 'ExitReturnCard' -> EjectCardAndReset;
MainMenu 'CheckBalance' -> PickCheckBalanceAccount -> DisplayBalance -> MainMenu;

TentativeAcceptMoney 'AcceptFail' -> RejectPhysicalMoney -> MainMenu;
TentativeAcceptMoney 'AcceptSucceed' -> PickDepositAccount -> RequestValue 'TellBank' -> BankResponse;

BankResponse 'BankNo' -> RejectPhysicalMoney;
BankResponse 'BankYes' -> ConsumeMoney -> NotifyConsumed -> MainMenu;
BankResponse 'BankAudit' -> BankAuditOffer 'HumanAcceptAudit' -> ConsumeMoney;

BankAuditOffer 'HumanRejectAudit' -> RejectPhysicalMoney;

EjectCardAndReset -> EmptyWaiting; +
+

+
+ + + +

8: Can withdraw money from main menu

+
+

Let's also be able to take money from the machine. After this, we'll move on, since our example is pretty squarely made +by now.

+
    +
  1. Pick a withdrawl account, or cancel to the main menu
  2. +
  3. Shown a balance, pick a withdrawl amount, or cancel to acct picker
  4. +
  5. Is the withdrawl account too high? If so go to 2
  6. +
  7. Does the machine actually have the money? If not go to 2
  8. +
  9. Otherwise confirm intent w/ human
  10. +
  11. Attempt to post the transaction.
  12. +
  13. If fail, display reason and go to 1
  14. +
  15. If succeed, dispense money and go to main menu
  16. +
+
    +
  • Rules 1-3: MainMenu -> PickWithdrawlAccount -> PickAmount -> AcctHasMoney? 'TooHighForAcct' -> PickWithdrawlAccount;
  • +
  • Rule 4: AcctHasMoney? -> MachineHasMoney? 'MachineLowOnCash' -> PickAmount;
  • +
  • Rule 5: MachineHasMoney? -> ConfirmWithdrawWithHuman 'MakeChanges' -> PickWithdrawlAmount;
  • +
  • Rule 6: ConfirmWithdrawWithHuman 'PostWithdrawl' -> BankWithdrawlResponse;
  • +
  • Rule 7: BankWithdrawlResponse 'WithdrawlFailure' -> WithdrawlFailureExplanation -> PickWithdrawlAccount;
  • +
  • Rule 8: BankWithdrawlResponse 'WithdrawlSuccess' -> DispenseMoney -> MainMenu;
  • +
+

Rule 1 canceller: PickWithdrawlAccount 'CancelWithdrawl' -> MainMenu; +Rule 2 canceller: PickWithdrawlAmount 'SwitchAccounts' -> PickWithdrawlAccount;

+

Or as a whole, we're adding

+
MainMenu -> PickWithdrawlAccount -> PickAmount -> AcctHasMoney? 'TooHighForAcct' -> PickWithdrawlAccount;
AcctHasMoney? -> MachineHasMoney? 'MachineLowOnCash' -> PickAmount;
MachineHasMoney? -> ConfirmWithdrawWithHuman 'MakeChanges' -> PickWithdrawlAmount;
ConfirmWithdrawWithHuman 'PostWithdrawl' -> BankWithdrawlResponse;
BankWithdrawlResponse 'WithdrawlFailure' -> WithdrawlFailureExplanation -> PickWithdrawlAccount;
BankWithdrawlResponse 'WithdrawlSuccess' -> DispenseMoney -> MainMenu;

PickWithdrawlAccount 'CancelWithdrawl' -> MainMenu;
PickWithdrawlAmount 'SwitchAccounts' -> PickWithdrawlAccount; +
+

Which leaves us with

+
EmptyWaiting 'Wait' -> EmptyWaiting 'InsertCard' -> HasCardNoAuth;

HasCardNoAuth 'CancelAuthReturnCard' -> EjectCardAndReset;
HasCardNoAuth 'WrongPIN' -> HasCardNoAuth;
HasCardNoAuth 'RightPIN' -> MainMenu;

MainMenu 'AcceptDeposit' -> TentativeAcceptMoney;
MainMenu 'ExitReturnCard' -> EjectCardAndReset;
MainMenu 'CheckBalance' -> PickCheckBalanceAccount -> DisplayBalance -> MainMenu;

TentativeAcceptMoney 'AcceptFail' -> RejectPhysicalMoney -> MainMenu;
TentativeAcceptMoney 'AcceptSucceed' -> PickDepositAccount -> RequestValue 'TellBank' -> BankResponse;

BankResponse 'BankNo' -> RejectPhysicalMoney;
BankResponse 'BankYes' -> ConsumeMoney -> NotifyConsumed -> MainMenu;
BankResponse 'BankAudit' -> BankAuditOffer 'HumanAcceptAudit' -> ConsumeMoney;

BankAuditOffer 'HumanRejectAudit' -> RejectPhysicalMoney;

MainMenu -> PickWithdrawlAccount -> PickAmount -> AcctHasMoney? 'TooHighForAcct' -> PickWithdrawlAccount;
AcctHasMoney? -> MachineHasMoney? 'MachineLowOnCash' -> PickAmount;
MachineHasMoney? -> ConfirmWithdrawWithHuman 'MakeChanges' -> PickWithdrawlAmount;
ConfirmWithdrawWithHuman 'PostWithdrawl' -> BankWithdrawlResponse;
BankWithdrawlResponse 'WithdrawlFailure' -> WithdrawlFailureExplanation -> PickWithdrawlAccount;
BankWithdrawlResponse 'WithdrawlSuccess' -> DispenseMoney -> MainMenu;

PickWithdrawlAccount 'CancelWithdrawl' -> MainMenu;
PickWithdrawlAmount 'SwitchAccounts' -> PickWithdrawlAccount;

EjectCardAndReset -> EmptyWaiting; +
+

+

As you can see, building up even very complex state machines is actually relatively straightforward, in a short +amount of time.

+



+ + +

Features

+
+ + +

DSL

+
+ + +

States

+
+ + +

Transitions

+
+ + +

Cycles

+
+ + +

Stripes

+
+ + +

Named Ordered Lists

+
+ + +

Atoms

+
+ + +

Strings

+
+ + +

Arrow types

+
+ + +

Unicode representations

+
+ + +

Node declarations

+
+ + +

All the styling bullshit

+
+ + +

Named edges

+
+ + +

URL callouts

+
+ + +

The 9 or whatever directives

+
+ + +

How to publish a machine

+
+ + +

Legal, main, and forced

+
+ + +

Validators

+
+ + +

State history

+
+ + +

Automatic visualization

+
+



+ + +

How to think in state machines

+
+



+ + +

Example Machines

+
+ + +

Door lock

+
+ + +

Traffic lights

+
+ + +

Basic three-state

+
+ + +

RYG, Off, Flash-red, Flash-yellow

+
+ + +

RYG, Off, Flash-red, Flash-yellow, Green-left, Yellow-left

+
+ + +

Heirarchal intersection

+
+ + +

ATM

+ + + +

HTTP

+ + + +

Better HTTP

+
+ + +

TCP

+ + + +

Coin-op vending machine (data)

+
+ + +

Video games

+
+ + +

Pac-man Ghost (sensors)

+
+ + +

Weather (probabilistics)

+
+ + +

Roguelike monster (interface satisfaction)

+
+ + +

Candy crush clone game flow (practical large use)

+
+ + +

Vegas locked 21 dealer behavior

+
+ + +

React SPA website (practical large use)

+
+ + +

BGP

+ + + +

LibGCrypt FIPS mode FSM

+ +



+ + +

How to debug

+
+



+ + +

How to publish

+
+

It's really quite simple.

+
    +
  1. Make a github repository.
  2. +
  3. Put your code in a file inside, with the extension .fsl
  4. +
  5. Make sure your code contains a machine_name
  6. +
+

Once done, your work should show up here.

+



+ + +

Notation Comparison

+
+ + +

Their notations, one by one

+
+ + +

Apples to Apples - Traffic Light

+
+



+ + +

Other state machines

+
+

There are a lot of state machine impls for JS, many quite a bit more mature than this one. Here are some options:

+
    +
  1. Finity 😮
  2. +
  3. Stately.js
  4. +
  5. machina.js
  6. +
  7. Pastafarian
  8. +
  9. Henderson
  10. +
  11. fsm-as-promised
  12. +
  13. state-machine
  14. +
  15. mood
  16. +
  17. FSM Workbench
  18. +
  19. SimpleStateMachine
  20. +
  21. shime/micro-machine
      +
    1. soveran/micromachine (ruby)
    2. +
    +
  22. +
  23. fabiospampinato/FSM
  24. +
  25. HQarroum/FSM
  26. +
  27. Finite-State-Automata
  28. +
  29. finite-state-machine
  30. +
  31. nfm
  32. +
+

And some similar stuff:

+
    +
  1. redux-machine
  2. +
  3. ember-fsm
  4. +
  5. State machine cat
  6. +
  7. Workty 😮
  8. +
  9. sam-simpler
  10. +
  11. event_chain
  12. +
  13. DRAKON
  14. +
  15. Yakindu Statechart Tools
  16. +
  17. GraphViz
      +
    1. Viz.js, which we use
    2. +
    +
  18. +
+




+ + +

Thanks

+
+

JSSM and FSL have had a lot of help.

+



+ + +

Internationalization

+
+ +

If I've overlooked you, please let me know.

+

If you'd like to help, it's straightforward.

+
    +
  1. Easy mode: open a PR with this file translated into your language
  2. +
  3. Extra mile: create a new repo containing this file translated
  4. +
+



+ + +

Code and Language

+
+

Forest Belton has provided guidance, bugfixes, parser and language commentary.

+

Jordan Harbrand suggested two interesting features and provided strong feedback on the initial tutorial draft.

+

The biggest thanks must go to Michael Morgan, who has debated significant sections of +the notation, invented several concepts and operators, helped with the parser, with system nomenclature, for having published +the first not-by-me FSL machine, for encouragement, and generally just for having been as interested as he has been.

+

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/docs/modules.html b/docs/docs/modules.html new file mode 100644 index 00000000..ab850104 --- /dev/null +++ b/docs/docs/modules.html @@ -0,0 +1 @@ +jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..30b9fef5 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + +

JSSM repo site

+ +

There isn't actually anything here. You're probably looking for:

+ + + +

+ +

Less commonly, you might be looking for

+ + + + + + \ No newline at end of file diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index 4218129b..00000000 --- a/docs/index.md +++ /dev/null @@ -1,37 +0,0 @@ -## Welcome to GitHub Pages - -You can use the [editor on GitHub](https://github.com/StoneCypher/jssm/edit/main/docs/index.md) to maintain and preview the content for your website in Markdown files. - -Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files. - -### Markdown - -Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for - -```markdown -Syntax highlighted code block - -# Header 1 -## Header 2 -### Header 3 - -- Bulleted -- List - -1. Numbered -2. List - -**Bold** and _Italic_ and `Code` text - -[Link](url) and ![Image](src) -``` - -For more details see [Basic writing and formatting syntax](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). - -### Jekyll Themes - -Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/StoneCypher/jssm/settings/pages). The name of this theme is saved in the Jekyll `_config.yml` configuration file. - -### Support or Contact - -Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out. diff --git a/jssm.d.ts b/jssm.d.ts index f24d4132..1d8ee563 100644 --- a/jssm.d.ts +++ b/jssm.d.ts @@ -1,6 +1,6 @@ declare type StateType = string; import { JssmGenericState, JssmGenericConfig, JssmTransition, JssmTransitionList, // JssmTransitionRule, -JssmMachineInternalState, JssmParseTree, JssmStateDeclaration, JssmArrow, JssmArrowDirection, JssmArrowKind, JssmLayout, FslDirection, FslTheme } from './jssm_types'; +JssmMachineInternalState, JssmParseTree, JssmStateDeclaration, JssmArrow, JssmArrowDirection, JssmArrowKind, JssmLayout, FslDirection, FslTheme, HookDescription } from './jssm_types'; import { seq, weighted_rand_select, weighted_sample_select, histograph, weighted_histo_key } from './jssm_util'; import { version } from './version'; declare function arrow_direction(arrow: JssmArrow): JssmArrowDirection; @@ -37,6 +37,8 @@ declare class Machine { _arrange_end_declaration: Array>; _theme: FslTheme; _flow: FslDirection; + _hooks: Map; + _named_hooks: Map; constructor({ start_states, complete, transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, state_declaration, fsl_version, dot_preamble, arrange_declaration, arrange_start_declaration, arrange_end_declaration, theme, flow, graph_layout }: JssmGenericConfig); _new_state(state_config: JssmGenericState): StateType; state(): StateType; @@ -86,6 +88,8 @@ declare class Machine { is_complete(): boolean; state_is_complete(whichState: StateType): boolean; has_completes(): boolean; + set_hook(HookDesc: HookDescription): void; + remove_hook(HookDesc: HookDescription): void; action(name: StateType, newData?: mDT): boolean; transition(newState: StateType, newData?: mDT): boolean; force_transition(newState: StateType, newData?: mDT): boolean; diff --git a/jssm_types.d.ts b/jssm_types.d.ts index bb21625c..333a599f 100644 --- a/jssm_types.d.ts +++ b/jssm_types.d.ts @@ -148,4 +148,28 @@ declare type JssmCompileSeStart = { }; declare type JssmParseTree = Array>; declare type JssmParseFunctionType = (string: any) => JssmParseTree; -export { JssmColor, JssmTransition, JssmTransitions, JssmTransitionList, JssmTransitionRule, JssmArrow, JssmArrowKind, JssmArrowDirection, JssmGenericConfig, JssmGenericState, JssmGenericMachine, JssmParseTree, JssmCompileSe, JssmCompileSeStart, JssmCompileRule, JssmPermitted, JssmPermittedOpt, JssmResult, JssmStateDeclaration, JssmStateDeclarationRule, JssmLayout, JssmParseFunctionType, JssmMachineInternalState, FslDirection, FslTheme }; +declare type BasicHookDescription = { + kind: 'hook'; + from: string; + to: string; + handler: Function; +}; +declare type HookDescriptionWithAction = { + kind: 'named'; + from: string; + to: string; + action: string; + handler: Function; +}; +declare type EntryHook = { + kind: 'entry'; + to: string; + handler: Function; +}; +declare type ExitHook = { + kind: 'exit'; + from: string; + handler: Function; +}; +declare type HookDescription = BasicHookDescription | HookDescriptionWithAction | EntryHook | ExitHook; +export { JssmColor, JssmTransition, JssmTransitions, JssmTransitionList, JssmTransitionRule, JssmArrow, JssmArrowKind, JssmArrowDirection, JssmGenericConfig, JssmGenericState, JssmGenericMachine, JssmParseTree, JssmCompileSe, JssmCompileSeStart, JssmCompileRule, JssmPermitted, JssmPermittedOpt, JssmResult, JssmStateDeclaration, JssmStateDeclarationRule, JssmLayout, JssmParseFunctionType, JssmMachineInternalState, FslDirection, FslTheme, HookDescription }; diff --git a/package.json b/package.json index 47c70e1d..de29d0af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jssm", - "version": "5.44.0", + "version": "5.45.0", "engines": { "node": ">=10.0.0" }, diff --git a/src/ts/jssm.ts b/src/ts/jssm.ts index becce783..b84b605e 100644 --- a/src/ts/jssm.ts +++ b/src/ts/jssm.ts @@ -19,7 +19,8 @@ import { JssmCompileSe, JssmCompileSeStart, JssmCompileRule, JssmArrow, JssmArrowDirection, JssmArrowKind, JssmLayout, - FslDirection, FslTheme + FslDirection, FslTheme, + HookDescription } from './jssm_types'; @@ -215,6 +216,18 @@ function makeTransition( +function hook_name(from: string, to: string): string { + return JSON.stringify([from, to]); +} + +function named_hook_name(from: string, to: string, action: string): string { + return JSON.stringify([from, to, action]); +} + + + + + function wrap_parse(input: string, options?: Object) { return parse(input, options || {}); } @@ -475,11 +488,14 @@ class Machine { _theme : FslTheme; _flow : FslDirection; + _hooks : Map; + _named_hooks : Map; + // whargarbl this badly needs to be broken up, monolith master constructor({ start_states, - complete = [], + complete = [], transitions, machine_author, machine_comment, @@ -530,6 +546,9 @@ class Machine { this._flow = flow; this._graph_layout = graph_layout; + this._hooks = new Map(); + this._named_hooks = new Map(); + if (state_declaration) { state_declaration.map( (state_decl: JssmStateDeclaration) => { @@ -975,12 +994,53 @@ class Machine { + // basic toolable hook call. convenience wrappers will follow, like + // hook(from, to, handler) and exit_hook(from, handler) and etc + set_hook(HookDesc: HookDescription) { + + switch (HookDesc.kind) { + + case 'hook': + this._hooks.set(JSON.stringify([HookDesc.from, HookDesc.to]), HookDesc.handler); + break; + + case 'named': + this._named_hooks.set(JSON.stringify([HookDesc.from, HookDesc.to, HookDesc.action]), HookDesc.handler); + break; + + case 'entry': + console.log('TODO: Should add entry hook here'); + throw 'TODO: Should add entry hook here'; + + case 'exit': + console.log('TODO: Should add exit hook here'); + throw 'TODO: Should add exit hook here'; + + default: + console.log(`Unknown hook type ${(HookDesc as any).kind}, should be impossible`); + throw new RangeError(`Unknown hook type ${(HookDesc as any).kind}, should be impossible`); + + } + } + + remove_hook(HookDesc: HookDescription) { + throw 'TODO: Should remove hook here'; + } + + + action(name: StateType, newData?: mDT): boolean { // todo whargarbl implement hooks // todo whargarbl implement data stuff // todo major incomplete whargarbl comeback if (this.valid_action(name, newData)) { - const edge: JssmTransition = this.current_action_edge_for(name); + const edge : JssmTransition = this.current_action_edge_for(name), + nhk : string = JSON.stringify([this._state, edge.to, name]); // named hook key + + let hook_permits : boolean | undefined = undefined; + + if (this._named_hooks.has + this._state = edge.to; return true; } else { @@ -1017,7 +1077,9 @@ class Machine { current_action_for(action: StateType): number { const action_base: Map = this._actions.get(action); - return action_base? action_base.get(this.state()): undefined; + return action_base + ? action_base.get(this.state()) + : undefined; } current_action_edge_for(action: StateType): JssmTransition { @@ -1081,7 +1143,8 @@ function sm(template_strings: TemplateStringsArray, ... remainder /* , argu // string notation, as designed, it's not really worth the hassle /* eslint-disable prefer-rest-params */ - (acc, val, idx): string => `${acc}${remainder[idx-1]}${val}` // arguments[0] is never loaded, so args doesn't need to be gated + (acc, val, idx): string => + `${acc}${remainder[idx-1]}${val}` // arguments[0] is never loaded, so args doesn't need to be gated /* eslint-enable prefer-rest-params */ ))); diff --git a/src/ts/jssm_types.ts b/src/ts/jssm_types.ts index 11b6009a..dbb09d98 100644 --- a/src/ts/jssm_types.ts +++ b/src/ts/jssm_types.ts @@ -305,6 +305,43 @@ type JssmParseFunctionType = +type BasicHookDescription = { + kind : 'hook' + from : string, + to : string, + handler : Function +}; + +type HookDescriptionWithAction = { + kind : 'named', + from : string, + to : string, + action : string, + handler : Function +}; + +type EntryHook = { + kind : 'entry', + to : string, + handler : Function +}; + +type ExitHook = { + kind : 'exit', + from : string, + handler : Function +}; + +type HookDescription + = BasicHookDescription + | HookDescriptionWithAction + | EntryHook + | ExitHook; + + + + + export { JssmColor, @@ -341,6 +378,8 @@ export { JssmMachineInternalState, FslDirection, - FslTheme + FslTheme, + + HookDescription }; diff --git a/src/ts/version.ts b/src/ts/version.ts index 9d0181d6..43d9a163 100644 --- a/src/ts/version.ts +++ b/src/ts/version.ts @@ -1,3 +1,3 @@ -const version: string = "5.44.0"; +const version: string = "5.45.0"; export { version }; From f1534eeae47b10c7847762f560de25cfee5813e9 Mon Sep 17 00:00:00 2001 From: John Haugeland Date: Tue, 3 May 2022 12:01:52 -0700 Subject: [PATCH 2/5] Trivial implementation on just .action; gated off for efficiency fixes StoneCypher/fsl#655 --- benchmark/results/general.chart.html | 14 +++++----- benchmark/results/general.json | 12 ++++----- dist/es6/jssm.d.ts | 1 + dist/es6/jssm.js | 38 ++++++++++++++++++++++++---- dist/jssm.es5.cjs.js | 2 +- dist/jssm.es5.cjs.nonmin.js | 38 ++++++++++++++++++++++++---- dist/jssm.es5.iife.js | 2 +- dist/jssm.es5.iife.nonmin.js | 38 ++++++++++++++++++++++++---- docs/docs/assets/search.js | 2 +- docs/docs/classes/Machine.html | 2 +- docs/docs/modules.html | 2 +- jssm.d.ts | 1 + src/ts/jssm.ts | 34 +++++++++++++++++++------ src/ts/tests/hooks.spec.ts | 20 ++++++++++++--- 14 files changed, 162 insertions(+), 44 deletions(-) diff --git a/benchmark/results/general.chart.html b/benchmark/results/general.chart.html index d20e0f47..5ffa456b 100644 --- a/benchmark/results/general.chart.html +++ b/benchmark/results/general.chart.html @@ -28,7 +28,7 @@
- +
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • remove_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +Machine | jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_has_hooks: boolean
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • remove_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/docs/modules.html b/docs/docs/modules.html index ab850104..9e8bc27f 100644 --- a/docs/docs/modules.html +++ b/docs/docs/modules.html @@ -1 +1 @@ -jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/jssm.d.ts b/jssm.d.ts index 1d8ee563..de516dc2 100644 --- a/jssm.d.ts +++ b/jssm.d.ts @@ -37,6 +37,7 @@ declare class Machine { _arrange_end_declaration: Array>; _theme: FslTheme; _flow: FslDirection; + _has_hooks: boolean; _hooks: Map; _named_hooks: Map; constructor({ start_states, complete, transitions, machine_author, machine_comment, machine_contributor, machine_definition, machine_language, machine_license, machine_name, machine_version, state_declaration, fsl_version, dot_preamble, arrange_declaration, arrange_start_declaration, arrange_end_declaration, theme, flow, graph_layout }: JssmGenericConfig); diff --git a/src/ts/jssm.ts b/src/ts/jssm.ts index b84b605e..f6daead5 100644 --- a/src/ts/jssm.ts +++ b/src/ts/jssm.ts @@ -488,6 +488,7 @@ class Machine { _theme : FslTheme; _flow : FslDirection; + _has_hooks : boolean; _hooks : Map; _named_hooks : Map; @@ -546,6 +547,7 @@ class Machine { this._flow = flow; this._graph_layout = graph_layout; + this._has_hooks = false; this._hooks = new Map(); this._named_hooks = new Map(); @@ -1001,11 +1003,11 @@ class Machine { switch (HookDesc.kind) { case 'hook': - this._hooks.set(JSON.stringify([HookDesc.from, HookDesc.to]), HookDesc.handler); + this._hooks.set(hook_name(HookDesc.from, HookDesc.to), HookDesc.handler); break; case 'named': - this._named_hooks.set(JSON.stringify([HookDesc.from, HookDesc.to, HookDesc.action]), HookDesc.handler); + this._named_hooks.set(named_hook_name(HookDesc.from, HookDesc.to, HookDesc.action), HookDesc.handler); break; case 'entry': @@ -1034,15 +1036,31 @@ class Machine { // todo whargarbl implement data stuff // todo major incomplete whargarbl comeback if (this.valid_action(name, newData)) { - const edge : JssmTransition = this.current_action_edge_for(name), - nhk : string = JSON.stringify([this._state, edge.to, name]); // named hook key - let hook_permits : boolean | undefined = undefined; + const edge : JssmTransition = this.current_action_edge_for(name); - if (this._named_hooks.has + if (this._has_hooks) { + + let hook_permits : boolean | undefined = undefined; + + const nhn : string = named_hook_name(this._state, edge.to, name), + maybe_hook : Function | undefined = this._named_hooks.get(nhn); + + if (maybe_hook === undefined) { hook_permits = true; } + else { hook_permits = maybe_hook('TODO FIXME'); } + + if (hook_permits) { + this._state = edge.to; + return true; + } else { + return false; + } + + } else { + this._state = edge.to; + return true; + } - this._state = edge.to; - return true; } else { return false; } diff --git a/src/ts/tests/hooks.spec.ts b/src/ts/tests/hooks.spec.ts index 4c3ebb89..f990233f 100644 --- a/src/ts/tests/hooks.spec.ts +++ b/src/ts/tests/hooks.spec.ts @@ -5,9 +5,7 @@ import { sm } from '../jssm'; -describe('Hooks', () => { - - +describe('Hooks open and closed in grammar', () => { test.todo('Hooks open doesn\'t throw' /*, () => { @@ -23,6 +21,22 @@ describe('Hooks', () => { } */); +}); + + + + + +describe('Basic hooks on API callpoint', () => { + + test('Setting a hook doesn\'t throw', () => { + + expect( () => { + const _foo = sm`a -> b;`; + _foo.set_hook({ from: 'a', to: 'b', handler: () => console.log('hi'), kind: 'hook' }) + }) + .not.toThrow(); + } ); }); From f7e0dd94d368fdbd918b55311e5b53bdf830a2cd Mon Sep 17 00:00:00 2001 From: John Haugeland Date: Tue, 3 May 2022 12:20:21 -0700 Subject: [PATCH 3/5] Add setup support for this._has_hooks, fixes StoneCypher/fsl#657; add performance differentiation tests for hooks, fixes StoneCypher/fsl#656 --- benchmark/results/general.chart.html | 16 ++++----- benchmark/results/general.json | 28 +++++++++++----- dist/es6/jssm.js | 2 ++ dist/jssm.es5.cjs.js | 2 +- dist/jssm.es5.cjs.nonmin.js | 2 ++ dist/jssm.es5.iife.js | 2 +- dist/jssm.es5.iife.nonmin.js | 2 ++ docs/docs/classes/Machine.html | 2 +- docs/docs/modules.html | 2 +- src/buildjs/benchmark.js | 49 ++++++++++++++++++++++++---- src/ts/jssm.ts | 2 ++ 11 files changed, 83 insertions(+), 26 deletions(-) diff --git a/benchmark/results/general.chart.html b/benchmark/results/general.chart.html index 5ffa456b..f56dfcf7 100644 --- a/benchmark/results/general.chart.html +++ b/benchmark/results/general.chart.html @@ -28,7 +28,7 @@
- +
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_has_hooks: boolean
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • remove_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +Machine | jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_has_hooks: boolean
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • remove_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/docs/modules.html b/docs/docs/modules.html index 9e8bc27f..19bd1f00 100644 --- a/docs/docs/modules.html +++ b/docs/docs/modules.html @@ -1 +1 @@ -jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/src/buildjs/benchmark.js b/src/buildjs/benchmark.js index da6451d2..1e17866a 100644 --- a/src/buildjs/benchmark.js +++ b/src/buildjs/benchmark.js @@ -7,9 +7,9 @@ const b = require('benny'), -function TransitionCycleFourPointTrafficLight500Times() { +const Tl4 = sm`red => green => yellow => red; [red yellow green] ~> off -> red;`; - const Tl4 = sm`red => green => yellow => red; [red yellow green] ~> off -> red;`; +function TransitionCycleFourPointTrafficLight500Times() { for (let i=0; i<500; ++i) { Tl4.transition('green'); @@ -23,9 +23,26 @@ function TransitionCycleFourPointTrafficLight500Times() { -function ActionCycleFourPointTrafficLight500Times() { +const Tl4WH = sm`red => green => yellow => red; [red yellow green] ~> off -> red;`; +Tl4WH.set_hook({ from: 'red', to: 'green', handler: () => true, kind: 'hook' }); - const Tl4WA = sm`red 'next' => green 'next' => yellow 'next' => red; [red yellow green] ~> off -> red;`; +function TransitionCycleFourPointTrafficLightWithHooks500Times() { + + for (let i=0; i<500; ++i) { + Tl4WH.transition('green'); + Tl4WH.transition('yellow'); + Tl4WH.transition('red'); + } + +} + + + + + +const Tl4WA = sm`red 'next' => green 'next' => yellow 'next' => red; [red yellow green] ~> off -> red;`; + +function ActionCycleFourPointTrafficLight500Times() { for (let i=0; i<500; ++i) { Tl4WA.action('next'); // to green @@ -39,10 +56,30 @@ function ActionCycleFourPointTrafficLight500Times() { + +const Tl4WAWH = sm`red 'next' => green 'next' => yellow 'next' => red; [red yellow green] ~> off -> red;`; +Tl4WAWH.set_hook({ from: 'red', to: 'green', handler: () => true, kind: 'hook' }); + +function ActionCycleFourPointTrafficLightWithHooks500Times() { + + for (let i=0; i<500; ++i) { + Tl4WAWH.action('next'); // to green + Tl4WAWH.action('next'); // to yellow + Tl4WAWH.action('next'); // to red + } + +} + + + + + b.suite('General performance suite', - b.add('Blind cycle a traffic light 500 times by transition', TransitionCycleFourPointTrafficLight500Times ), - b.add('Blind cycle a traffic light 500 times by action', ActionCycleFourPointTrafficLight500Times ), + b.add('Blind cycle a traffic light 500 times by transition', TransitionCycleFourPointTrafficLight500Times ), + b.add('Blind cycle a hooked traffic light 500 times by transition', TransitionCycleFourPointTrafficLightWithHooks500Times ), + b.add('Blind cycle a traffic light 500 times by action', ActionCycleFourPointTrafficLight500Times ), + b.add('Blind cycle a hooked traffic light 500 times by action', ActionCycleFourPointTrafficLightWithHooks500Times ), b.cycle(), b.complete(), diff --git a/src/ts/jssm.ts b/src/ts/jssm.ts index f6daead5..e687baea 100644 --- a/src/ts/jssm.ts +++ b/src/ts/jssm.ts @@ -1004,10 +1004,12 @@ class Machine { case 'hook': this._hooks.set(hook_name(HookDesc.from, HookDesc.to), HookDesc.handler); + this._has_hooks = true; break; case 'named': this._named_hooks.set(named_hook_name(HookDesc.from, HookDesc.to, HookDesc.action), HookDesc.handler); + this._has_hooks = true; break; case 'entry': From 58f4df20b9ca9d24da525a49c231b7cf7e1f048e Mon Sep 17 00:00:00 2001 From: John Haugeland Date: Tue, 3 May 2022 16:15:27 -0700 Subject: [PATCH 4/5] Basic working external api hooks, fixes StoneCypher/fsl#661, and api action hooks, fixes StoneCypher/fsl#662 --- benchmark/results/general.chart.html | 14 +++--- benchmark/results/general.json | 24 +++++----- dist/es6/jssm.js | 56 +++++++++++++++++----- dist/es6/jssm_util.d.ts | 4 +- dist/es6/jssm_util.js | 4 +- dist/jssm.es5.cjs.js | 2 +- dist/jssm.es5.cjs.nonmin.js | 56 +++++++++++++++++----- dist/jssm.es5.iife.js | 2 +- dist/jssm.es5.iife.nonmin.js | 56 +++++++++++++++++----- docs/docs/classes/Machine.html | 2 +- docs/docs/modules.html | 2 +- jssm_util.d.ts | 4 +- src/ts/jssm.ts | 72 +++++++++++++++++++++------- src/ts/jssm_util.ts | 19 +++++++- src/ts/tests/hooks.spec.ts | 63 +++++++++++++++++++++++- 15 files changed, 300 insertions(+), 80 deletions(-) diff --git a/benchmark/results/general.chart.html b/benchmark/results/general.chart.html index f56dfcf7..82966527 100644 --- a/benchmark/results/general.chart.html +++ b/benchmark/results/general.chart.html @@ -28,7 +28,7 @@
- +
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_has_hooks: boolean
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • remove_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +Machine | jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_has_hooks: boolean
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • remove_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/docs/modules.html b/docs/docs/modules.html index 19bd1f00..8f9af50f 100644 --- a/docs/docs/modules.html +++ b/docs/docs/modules.html @@ -1 +1 @@ -jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/jssm_util.d.ts b/jssm_util.d.ts index 301318bd..67392167 100644 --- a/jssm_util.d.ts +++ b/jssm_util.d.ts @@ -5,4 +5,6 @@ declare const seq: Function; declare const histograph: Function; declare const weighted_sample_select: Function; declare const weighted_histo_key: Function; -export { seq, arr_uniq_p, histograph, weighted_histo_key, weighted_rand_select, weighted_sample_select, array_box_if_string }; +declare const hook_name: (from: string, to: string) => string; +declare const named_hook_name: (from: string, to: string, action: string) => string; +export { seq, arr_uniq_p, histograph, weighted_histo_key, weighted_rand_select, weighted_sample_select, array_box_if_string, hook_name, named_hook_name }; diff --git a/src/ts/jssm.ts b/src/ts/jssm.ts index e687baea..1c1533a0 100644 --- a/src/ts/jssm.ts +++ b/src/ts/jssm.ts @@ -30,7 +30,7 @@ import { import { seq, weighted_rand_select, weighted_sample_select, histograph, - weighted_histo_key, array_box_if_string + weighted_histo_key, array_box_if_string, hook_name, named_hook_name } from './jssm_util'; @@ -216,18 +216,6 @@ function makeTransition( -function hook_name(from: string, to: string): string { - return JSON.stringify([from, to]); -} - -function named_hook_name(from: string, to: string, action: string): string { - return JSON.stringify([from, to, action]); -} - - - - - function wrap_parse(input: string, options?: Object) { return parse(input, options || {}); } @@ -1049,7 +1037,7 @@ class Machine { maybe_hook : Function | undefined = this._named_hooks.get(nhn); if (maybe_hook === undefined) { hook_permits = true; } - else { hook_permits = maybe_hook('TODO FIXME'); } + else { hook_permits = maybe_hook( { from: this._state, to: edge.to, action: name } ); } if (hook_permits) { this._state = edge.to; @@ -1073,11 +1061,35 @@ class Machine { // todo whargarbl implement data stuff // todo major incomplete whargarbl comeback if (this.valid_transition(newState, newData)) { - this._state = newState; - return true; + + if (this._has_hooks) { + + let hook_permits : boolean | undefined = undefined; + + const hn : string = hook_name(this._state, newState), + maybe_hook : Function | undefined = this._hooks.get(hn); + + if (maybe_hook === undefined) { hook_permits = true; } + else { hook_permits = maybe_hook( { from: this._state, to: newState } ); } + + if (hook_permits) { + this._state = newState; + return true; + } else { + return false; + } + + } else { + + this._state = newState; + return true; + + } + } else { return false; } + } // can leave machine in inconsistent state. generally do not use @@ -1086,11 +1098,35 @@ class Machine { // todo whargarbl implement data stuff // todo major incomplete whargarbl comeback if (this.valid_force_transition(newState, newData)) { - this._state = newState; - return true; + + if (this._has_hooks) { + + let hook_permits : boolean | undefined = undefined; + + const hn : string = hook_name(this._state, newState), + maybe_hook : Function | undefined = this._named_hooks.get(hn); + + if (maybe_hook === undefined) { hook_permits = true; } + else { hook_permits = maybe_hook({ from: this._state, to: newState }); } + + if (hook_permits) { + this._state = newState; + return true; + } else { + return false; + } + + } else { + + this._state = newState; + return true; + + } + } else { return false; } + } diff --git a/src/ts/jssm_util.ts b/src/ts/jssm_util.ts index 9777fac6..3a1611ce 100644 --- a/src/ts/jssm_util.ts +++ b/src/ts/jssm_util.ts @@ -90,11 +90,28 @@ const weighted_histo_key: Function = (n: number, opts: Array, prob_prop: st +const hook_name = (from: string, to: string): string => + + JSON.stringify([from, to]); + + + + + +const named_hook_name = (from: string, to: string, action: string): string => + + JSON.stringify([from, to, action]); + + + + + export { seq, arr_uniq_p, histograph, weighted_histo_key, weighted_rand_select, weighted_sample_select, - array_box_if_string + array_box_if_string, + hook_name, named_hook_name }; diff --git a/src/ts/tests/hooks.spec.ts b/src/ts/tests/hooks.spec.ts index f990233f..50885457 100644 --- a/src/ts/tests/hooks.spec.ts +++ b/src/ts/tests/hooks.spec.ts @@ -29,7 +29,8 @@ describe('Hooks open and closed in grammar', () => { describe('Basic hooks on API callpoint', () => { - test('Setting a hook doesn\'t throw', () => { + + test('Setting a regular hook doesn\'t throw', () => { expect( () => { const _foo = sm`a -> b;`; @@ -39,4 +40,64 @@ describe('Basic hooks on API callpoint', () => { } ); + + test('Setting a named hook doesn\'t throw', () => { + + expect( () => { + const _foo = sm`a 'foo' -> b;`; + _foo.set_hook({ from: 'a', to: 'b', handler: () => console.log('hi'), kind: 'named', action: 'foo' }) + }) + .not.toThrow(); + + } ); + +}); + + + + + +describe('Basic hooks on API callpoint', () => { + + test('Basic hooks call their handler', () => { + + const handler = jest.fn(x => true), + uncalled = jest.fn(x => true); + + expect( () => { + const _foo = sm`a -> b -> c;`; + _foo.set_hook({ from: 'a', to: 'b', handler, kind: 'hook' }); + _foo.set_hook({ from: 'b', to: 'a', handler: uncalled, kind: 'hook' }); + _foo.set_hook({ from: 'b', to: 'c', handler: uncalled, kind: 'hook' }); + _foo.transition('b'); + }) + .not.toThrow(); + + // should hook from first, but not from second + expect(handler.mock.calls.length).toBe(1); + expect(uncalled.mock.calls.length).toBe(0); + + } ); + + test('Named hooks call their handler', () => { + + const handler = jest.fn(x => true), + uncalled = jest.fn(x => true); + + expect( () => { + const _foo = sm`a 'next' -> b 'next' -> c;`; + _foo.set_hook({ from: 'a', to: 'b', handler, kind: 'named', action: 'next' }); + _foo.set_hook({ from: 'a', to: 'b', handler: uncalled, kind: 'named', action: 'borg' }); + _foo.set_hook({ from: 'b', to: 'a', handler: uncalled, kind: 'named', action: 'next' }); + _foo.action('next'); + _foo.action('next'); + }) + .not.toThrow(); + + // should hook from first, but not from second + expect(handler.mock.calls.length).toBe(1); + expect(uncalled.mock.calls.length).toBe(0); + + } ); + }); From 30412045f8f1444594a965f18a5140048a11312e Mon Sep 17 00:00:00 2001 From: John Haugeland Date: Tue, 3 May 2022 17:40:22 -0700 Subject: [PATCH 5/5] Working external API hooks with rejection, fixes StoneCypher/fsl#701, fixes StoneCypher/fsl#660 --- benchmark/results/general.chart.html | 14 ++-- benchmark/results/general.json | 24 +++--- dist/es6/jssm.d.ts | 1 - dist/es6/jssm.js | 28 +++---- dist/jssm.es5.cjs.js | 2 +- dist/jssm.es5.cjs.nonmin.js | 28 +++---- dist/jssm.es5.iife.js | 2 +- dist/jssm.es5.iife.nonmin.js | 28 +++---- docs/docs/assets/search.js | 2 +- docs/docs/classes/Machine.html | 2 +- docs/docs/modules.html | 2 +- jssm.d.ts | 1 - src/ts/jssm.ts | 32 ++++---- src/ts/tests/hooks.spec.ts | 106 +++++++++++++++++++++++++++ 14 files changed, 188 insertions(+), 84 deletions(-) diff --git a/benchmark/results/general.chart.html b/benchmark/results/general.chart.html index 82966527..ea779cf1 100644 --- a/benchmark/results/general.chart.html +++ b/benchmark/results/general.chart.html @@ -28,7 +28,7 @@
- +
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_has_hooks: boolean
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • remove_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +Machine | jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

Class Machine<mDT>

Type parameters

  • mDT

Hierarchy

  • Machine

Index

Constructors

  • new Machine<mDT>(__namedParameters: JssmGenericConfig<mDT>): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • __namedParameters: JssmGenericConfig<mDT>

    Returns Machine<mDT>

Properties

_actions: Map<string, Map<string, number>>
_arrange_declaration: string[][]
_arrange_end_declaration: string[][]
_arrange_start_declaration: string[][]
_dot_preamble: string
_edge_map: Map<string, Map<string, number>>
_edges: JssmTransition<mDT>[]
_flow: FslDirection
_fsl_version?: string
_graph_layout: JssmLayout
_has_hooks: boolean
_hooks: Map<string, Function>
_machine_author?: string[]
_machine_comment?: string
_machine_contributor?: string[]
_machine_definition?: string
_machine_language?: string
_machine_license?: string
_machine_name?: string
_machine_version?: string
_named_hooks: Map<string, Function>
_named_transitions: Map<string, number>
_raw_state_declaration?: Object[]
_reverse_action_targets: Map<string, Map<string, number>>
_reverse_actions: Map<string, Map<string, number>>
_state: string
_state_declarations: Map<string, JssmStateDeclaration>
_states: Map<string, JssmGenericState>
_theme: FslTheme

Methods

  • _new_state(state_config: JssmGenericState): string
  • Parameters

    • state_config: JssmGenericState

    Returns string

  • action(name: string, newData?: mDT): boolean
  • Parameters

    • name: string
    • Optional newData: mDT

    Returns boolean

  • actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • current_action_edge_for(action: string): JssmTransition<mDT>
  • Parameters

    • action: string

    Returns JssmTransition<mDT>

  • current_action_for(action: string): number
  • Parameters

    • action: string

    Returns number

  • dot_preamble(): string
  • flow(): FslDirection
  • force_transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • fsl_version(): string
  • get_transition_by_state_names(from: string, to: string): number
  • Parameters

    • from: string
    • to: string

    Returns number

  • graph_layout(): string
  • has_completes(): boolean
  • has_state(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • has_terminals(): boolean
  • has_unenterables(): boolean
  • is_complete(): boolean
  • is_final(): boolean
  • is_terminal(): boolean
  • is_unenterable(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • list_actions(): string[]
  • list_edges(): JssmTransition<mDT>[]
  • list_entrances(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exit_actions(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_exits(whichState?: string): string[]
  • Parameters

    • whichState: string = ...

    Returns string[]

  • list_named_transitions(): Map<string, number>
  • list_states_having_action(whichState: string): string[]
  • Parameters

    • whichState: string

    Returns string[]

  • list_transitions(whichState?: string): JssmTransitionList
  • Parameters

    • whichState: string = ...

    Returns JssmTransitionList

  • lookup_transition_for(from: string, to: string): JssmTransition<mDT>
  • Parameters

    • from: string
    • to: string

    Returns JssmTransition<mDT>

  • machine_author(): string[]
  • machine_comment(): string
  • machine_contributor(): string[]
  • machine_definition(): string
  • machine_language(): string
  • machine_license(): string
  • machine_name(): string
  • machine_state(): JssmMachineInternalState<mDT>
  • Returns JssmMachineInternalState<mDT>

  • machine_version(): string
  • probabilistic_histo_walk(n: number): Map<string, number>
  • Parameters

    • n: number

    Returns Map<string, number>

  • probabilistic_transition(): boolean
  • probabilistic_walk(n: number): string[]
  • Parameters

    • n: number

    Returns string[]

  • probable_action_exits(whichState?: string): any[]
  • Parameters

    • whichState: string = ...

    Returns any[]

  • probable_exits_for(whichState: string): JssmTransition<mDT>[]
  • Parameters

    • whichState: string

    Returns JssmTransition<mDT>[]

  • raw_state_declarations(): Object[]
  • set_hook(HookDesc: HookDescription): void
  • Parameters

    • HookDesc: HookDescription

    Returns void

  • sm(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • state(): string
  • state_declaration(which: string): JssmStateDeclaration
  • Parameters

    • which: string

    Returns JssmStateDeclaration

  • state_declarations(): Map<string, JssmStateDeclaration>
  • Returns Map<string, JssmStateDeclaration>

  • state_for(whichState: string): JssmGenericState
  • Parameters

    • whichState: string

    Returns JssmGenericState

  • state_is_complete(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_final(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • state_is_terminal(whichState: string): boolean
  • Parameters

    • whichState: string

    Returns boolean

  • states(): string[]
  • theme(): FslTheme
  • transition(newState: string, newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional newData: mDT

    Returns boolean

  • valid_action(action: string, _newData?: mDT): boolean
  • Parameters

    • action: string
    • Optional _newData: mDT

    Returns boolean

  • valid_force_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

  • valid_transition(newState: string, _newData?: mDT): boolean
  • Parameters

    • newState: string
    • Optional _newData: mDT

    Returns boolean

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/docs/modules.html b/docs/docs/modules.html index 8f9af50f..3346b6af 100644 --- a/docs/docs/modules.html +++ b/docs/docs/modules.html @@ -1 +1 @@ -jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file +jssm
Options
All
  • Public
  • Public/Protected
  • All
Menu

jssm

Index

Variables

histograph: Function = ...
seq: Function = ...
version: string = "5.45.0"
weighted_histo_key: Function = ...
weighted_rand_select: Function = ...
weighted_sample_select: Function = ...

Functions

  • arrow_direction(arrow: JssmArrow): JssmArrowDirection
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowDirection

  • arrow_left_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • arrow_right_kind(arrow: JssmArrow): JssmArrowKind
  • Parameters

    • arrow: JssmArrow

    Returns JssmArrowKind

  • compile<mDT>(tree: JssmParseTree): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • tree: JssmParseTree

    Returns JssmGenericConfig<mDT>

  • make<mDT>(plan: string): JssmGenericConfig<mDT>
  • Type parameters

    • mDT

    Parameters

    • plan: string

    Returns JssmGenericConfig<mDT>

  • parse(input: string, options?: Object): any
  • Parameters

    • input: string
    • Optional options: Object

    Returns any

  • sm<mDT>(template_strings: TemplateStringsArray, ...remainder: any[]): Machine<mDT>
  • Type parameters

    • mDT

    Parameters

    • template_strings: TemplateStringsArray
    • Rest ...remainder: any[]

    Returns Machine<mDT>

  • transfer_state_properties(state_decl: JssmStateDeclaration): JssmStateDeclaration
  • Parameters

    • state_decl: JssmStateDeclaration

    Returns JssmStateDeclaration

Legend

  • Property
  • Method

Settings

Theme

Generated using TypeDoc

\ No newline at end of file diff --git a/jssm.d.ts b/jssm.d.ts index de516dc2..458377c5 100644 --- a/jssm.d.ts +++ b/jssm.d.ts @@ -90,7 +90,6 @@ declare class Machine { state_is_complete(whichState: StateType): boolean; has_completes(): boolean; set_hook(HookDesc: HookDescription): void; - remove_hook(HookDesc: HookDescription): void; action(name: StateType, newData?: mDT): boolean; transition(newState: StateType, newData?: mDT): boolean; force_transition(newState: StateType, newData?: mDT): boolean; diff --git a/src/ts/jssm.ts b/src/ts/jssm.ts index 1c1533a0..2a937bef 100644 --- a/src/ts/jssm.ts +++ b/src/ts/jssm.ts @@ -1000,13 +1000,13 @@ class Machine { this._has_hooks = true; break; - case 'entry': - console.log('TODO: Should add entry hook here'); - throw 'TODO: Should add entry hook here'; + // case 'entry': + // console.log('TODO: Should add entry hook here'); + // throw 'TODO: Should add entry hook here'; - case 'exit': - console.log('TODO: Should add exit hook here'); - throw 'TODO: Should add exit hook here'; + // case 'exit': + // console.log('TODO: Should add exit hook here'); + // throw 'TODO: Should add exit hook here'; default: console.log(`Unknown hook type ${(HookDesc as any).kind}, should be impossible`); @@ -1015,9 +1015,9 @@ class Machine { } } - remove_hook(HookDesc: HookDescription) { - throw 'TODO: Should remove hook here'; - } + // remove_hook(HookDesc: HookDescription) { + // throw 'TODO: Should remove hook here'; + // } @@ -1033,13 +1033,13 @@ class Machine { let hook_permits : boolean | undefined = undefined; - const nhn : string = named_hook_name(this._state, edge.to, name), - maybe_hook : Function | undefined = this._named_hooks.get(nhn); + const nhn : string = named_hook_name(this._state, edge.to, name), + maybe_hook = this._named_hooks.get(nhn); if (maybe_hook === undefined) { hook_permits = true; } else { hook_permits = maybe_hook( { from: this._state, to: edge.to, action: name } ); } - if (hook_permits) { + if (hook_permits !== false) { this._state = edge.to; return true; } else { @@ -1072,7 +1072,7 @@ class Machine { if (maybe_hook === undefined) { hook_permits = true; } else { hook_permits = maybe_hook( { from: this._state, to: newState } ); } - if (hook_permits) { + if (hook_permits !== false) { this._state = newState; return true; } else { @@ -1104,12 +1104,12 @@ class Machine { let hook_permits : boolean | undefined = undefined; const hn : string = hook_name(this._state, newState), - maybe_hook : Function | undefined = this._named_hooks.get(hn); + maybe_hook : Function | undefined = this._hooks.get(hn); if (maybe_hook === undefined) { hook_permits = true; } - else { hook_permits = maybe_hook({ from: this._state, to: newState }); } + else { hook_permits = maybe_hook({ from: this._state, to: newState, forced: true }); } - if (hook_permits) { + if (hook_permits !== false) { this._state = newState; return true; } else { diff --git a/src/ts/tests/hooks.spec.ts b/src/ts/tests/hooks.spec.ts index 50885457..64d507b5 100644 --- a/src/ts/tests/hooks.spec.ts +++ b/src/ts/tests/hooks.spec.ts @@ -51,6 +51,92 @@ describe('Basic hooks on API callpoint', () => { } ); + + test('Setting a kind of hook that doesn\'t exist throws', () => { + + expect( () => { + const _foo = sm`a 'foo' -> b;`; + _foo.set_hook({ from: 'a', to: 'b', handler: () => console.log('hi'), kind: 'Smaug the Merciless', action: 'foo' } as any) + }) + .toThrow(); + + } ); + +}); + + + + + +test('Basic hook rejection works', () => { + + const foo = sm`a => b;`; + + foo.set_hook({ from: 'a', to: 'b', kind: 'hook', handler: () => false }); + expect(foo.transition('b')).toBe(false); + expect(foo.state()).toBe('a'); + + foo.set_hook({ from: 'a', to: 'b', kind: 'hook', handler: () => true }); + expect(foo.transition('b')).toBe(true); + expect(foo.state()).toBe('b'); + +}); + + + + + +test('Basic hook rejection works on forced edges', () => { + + const foo = sm`a ~> b ~> c;`; + + foo.set_hook({ from: 'a', to: 'b', kind: 'hook', handler: () => false }); + expect(foo.force_transition('b')).toBe(false); + expect(foo.state()).toBe('a'); + + foo.set_hook({ from: 'a', to: 'b', kind: 'hook', handler: () => true }); + expect(foo.force_transition('b')).toBe(true); + expect(foo.state()).toBe('b'); + + // line completion for when a hook lookup finds nothing + expect(foo.force_transition('c')).toBe(true); + expect(foo.state()).toBe('c'); + +}); + + + + + +test('Named hook rejection works', () => { + + const foo = sm`a 'foo' => b;`; + + foo.set_hook({ from: 'a', to: 'b', action: 'foo', kind: 'named', handler: () => false }); + expect(foo.action('foo')).toBe(false); + expect(foo.state()).toBe('a'); + + foo.set_hook({ from: 'a', to: 'b', action: 'foo', kind: 'named', handler: () => true }); + expect(foo.action('foo')).toBe(true); + expect(foo.state()).toBe('b'); + +}); + + + + + +test('Named hook rejection doesn\'t block transitions', () => { + + const foo = sm`a 'foo' => b;`; + + foo.set_hook({ from: 'a', to: 'b', action: 'foo', kind: 'named', handler: () => false }); + expect(foo.action('foo')).toBe(false); + expect(foo.state()).toBe('a'); + + expect(foo.transition('b')).toBe(true); + expect(foo.state()).toBe('b'); + }); @@ -100,4 +186,24 @@ describe('Basic hooks on API callpoint', () => { } ); + test('Forced hooks call their handler', () => { + + const handler = jest.fn(x => true), + uncalled = jest.fn(x => true); + + expect( () => { + const _foo = sm`a ~> b ~> c;`; + _foo.set_hook({ from: 'a', to: 'b', handler, kind: 'hook' }); + _foo.set_hook({ from: 'b', to: 'a', handler: uncalled, kind: 'hook' }); + _foo.set_hook({ from: 'b', to: 'c', handler: uncalled, kind: 'hook' }); + _foo.force_transition('b'); + }) + .not.toThrow(); + + // should hook from first, but not from second + expect(handler.mock.calls.length).toBe(1); + expect(uncalled.mock.calls.length).toBe(0); + + } ); + });