|
|
|
@ -1,12 +1,13 @@
|
|
|
|
|
export interface Machine_T<S,E> {
|
|
|
|
|
type EventShape = [name:string, payload:any];
|
|
|
|
|
export interface Machine_T<S,E extends EventShape> {
|
|
|
|
|
states: Array<State_T<S,E>>
|
|
|
|
|
}
|
|
|
|
|
export interface State_T<S,E> {
|
|
|
|
|
export interface State_T<S,E extends EventShape> {
|
|
|
|
|
name: S;
|
|
|
|
|
ons: Array<On_T<S,E>>;
|
|
|
|
|
}
|
|
|
|
|
export interface On_T<S,E> {
|
|
|
|
|
eventName: E;
|
|
|
|
|
export interface On_T<S,E extends EventShape> {
|
|
|
|
|
eventName: E[0];
|
|
|
|
|
reactions: Array<Do_T | Goto_T<S>>;
|
|
|
|
|
};
|
|
|
|
|
export interface Do_T {
|
|
|
|
@ -19,10 +20,69 @@ export interface Goto_T<S> {
|
|
|
|
|
targetStateName: S;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const Machine = function<S,E>(...states:Array<State_T<S,E>>) : Machine_T<S,E> { return {states}; };
|
|
|
|
|
export const State = function<S,E>(name:S, ...ons:Array<On_T<S,E>>) : State_T<S,E>{ return {name, ons}; };
|
|
|
|
|
export const On = function<S,E>(eventName:E, ...reactions:Array<Do_T | Goto_T<S>>) : On_T<S,E>{ return {eventName, reactions}; };
|
|
|
|
|
export const Machine = function<S,E extends EventShape>(...states:Array<State_T<S,E>>) : Machine_T<S,E> { return {states}; };
|
|
|
|
|
export const State = function<S,E extends EventShape>(name:S, ...ons:Array<On_T<S,E>>) : State_T<S,E>{ return {name, ons}; };
|
|
|
|
|
export const On = function<S,E extends EventShape>(eventName:E[0], ...reactions:Array<Do_T | Goto_T<S>>) : On_T<S,E>{ return {eventName, reactions}; };
|
|
|
|
|
export const Do = function(fn:DoFn_T) : Do_T{ return {type:'Do', fn}; };
|
|
|
|
|
export const Goto = function<S>(targetStateName:S) : Goto_T<S> { return {type:'Goto', targetStateName} };
|
|
|
|
|
|
|
|
|
|
interface Tick_T {
|
|
|
|
|
doFunctions: Array<DoFn_T>
|
|
|
|
|
};
|
|
|
|
|
const Tick = function(doFunctions : Array<DoFn_T>) : Tick_T{ return {doFunctions}; };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export interface Interpreter_T<S,E extends EventShape,C> {
|
|
|
|
|
machine: Machine_T<S,E>;
|
|
|
|
|
state: S;
|
|
|
|
|
context: C | {};
|
|
|
|
|
}
|
|
|
|
|
export function interpret<S,E extends EventShape,C>(machine:Machine_T<S,E>, options?:{state?:S, context?:C | {}}) : Interpreter_T<S,E,C>{
|
|
|
|
|
let {state, context} = options || {};
|
|
|
|
|
if(typeof state === 'undefined'){ state = machine.states[0].name; }
|
|
|
|
|
if(typeof context === 'undefined'){ context = {}; }
|
|
|
|
|
return {machine, state, context};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Helper function for `send()`
|
|
|
|
|
*/
|
|
|
|
|
function getState<S,E extends EventShape,C>(interpreter : Interpreter_T<S,E,C>) : State_T<S,E>{
|
|
|
|
|
return interpreter.machine.states.find((state)=>state.name===interpreter.state) as unknown as State_T<S,E>;
|
|
|
|
|
}
|
|
|
|
|
/** Helper function for `send()`
|
|
|
|
|
*/
|
|
|
|
|
function getOns<S,E extends EventShape>(state : State_T<S,E>, event:E) : Array<On_T<S,E>>{
|
|
|
|
|
return state.ons.filter((on)=>on.eventName===event[0]);
|
|
|
|
|
}
|
|
|
|
|
/** Inject an Event into the Interpreter's "tick queue".
|
|
|
|
|
*
|
|
|
|
|
* An event can be signify something "new" happening, such that its reactions should run on the next Tick;
|
|
|
|
|
* or it can signify a milestone "within" the current Tick, such that a Tick can be thought of as having
|
|
|
|
|
* "sub-Ticks".
|
|
|
|
|
*
|
|
|
|
|
* This distinction is significant for proper ordering of reaction execution, and also for determining
|
|
|
|
|
* whether to run a reaction at all. If an Event is received, and is specified to be applied on a past
|
|
|
|
|
* Tick, it is discarded.
|
|
|
|
|
*/
|
|
|
|
|
export function send<S,E extends EventShape,C>(interpreter : Interpreter_T<S,E,C>, event:E){
|
|
|
|
|
const state = getState(interpreter);
|
|
|
|
|
const ons = getOns(state, event);
|
|
|
|
|
const tick = Tick(ons
|
|
|
|
|
.map((on)=>on.reactions)
|
|
|
|
|
.flat()
|
|
|
|
|
.map((reaction)=>{
|
|
|
|
|
if(reaction.type === 'Do'){
|
|
|
|
|
return reaction.fn;
|
|
|
|
|
}
|
|
|
|
|
else if(reaction.type === 'Goto'){
|
|
|
|
|
return (ctx,e,self)=>{};
|
|
|
|
|
}
|
|
|
|
|
else{
|
|
|
|
|
return (ctx,e,self)=>{};
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const Spawn = function(){};
|
|
|
|
|
export const Unspawn = function(){};
|