sync send works
This commit is contained in:
+47
-31
@@ -1,4 +1,4 @@
|
||||
type Event_T = [name:string, payload:any];
|
||||
type Event_T = [name:string, payload:any] | ['entry', any];
|
||||
export interface Machine_T<S,E extends Event_T,C> {
|
||||
states: Array<State_T<S,E,C>>
|
||||
}
|
||||
@@ -8,13 +8,13 @@ export interface State_T<S,E extends Event_T,C> {
|
||||
}
|
||||
export interface On_T<S,E extends Event_T,C> {
|
||||
eventName: E[0];
|
||||
reactions: Array<Do_T<E,C> | Goto_T<S>>;
|
||||
reactions: Array<Do_T<S,E,C> | Goto_T<S>>;
|
||||
};
|
||||
export interface Do_T<E extends Event_T, C> {
|
||||
export interface Do_T<S,E extends Event_T, C> {
|
||||
type: 'Do';
|
||||
fn: DoFn_T<E,C>;
|
||||
fn: DoFn_T<S,E,C>;
|
||||
};
|
||||
export type DoFn_T<E extends Event_T, C> = (ctx:C,e:E,self)=>any;
|
||||
export type DoFn_T<S,E extends Event_T, C> = (ctx:C,e:E,self:Interpreter_T<S,E,C>)=>any;
|
||||
export interface Goto_T<S> {
|
||||
type: 'Goto';
|
||||
targetStateName: S;
|
||||
@@ -22,26 +22,30 @@ export interface Goto_T<S> {
|
||||
|
||||
export const Machine = function<S,E extends Event_T,C>(...states:Array<State_T<S,E,C>>) : Machine_T<S,E,C> { return {states}; };
|
||||
export const State = function<S,E extends Event_T,C>(name:S, ...ons:Array<On_T<S,E,C>>) : State_T<S,E,C>{ return {name, ons}; };
|
||||
export const On = function<S,E extends Event_T,C>(eventName:E[0], ...reactions:Array<Do_T<E,C> | Goto_T<S>>) : On_T<S,E,C>{ return {eventName, reactions}; };
|
||||
export const Do = function<E extends Event_T, C>(fn:DoFn_T<E,C>) : Do_T<E,C>{ return {type:'Do', fn}; };
|
||||
export const On = function<S,E extends Event_T,C>(eventName:E[0], ...reactions:Array<Do_T<S,E,C> | Goto_T<S>>) : On_T<S,E,C>{ return {eventName, reactions}; };
|
||||
export const Do = function<S,E extends Event_T, C>(fn:DoFn_T<S,E,C>) : Do_T<S,E,C>{ return {type:'Do', fn}; };
|
||||
export const Goto = function<S>(targetStateName:S) : Goto_T<S> { return {type:'Goto', targetStateName} };
|
||||
|
||||
interface Tick_T<E extends Event_T, C> {
|
||||
doFunctions: Array<DoFn_T<E,C>>
|
||||
interface Tick_T<S,E extends Event_T, C> {
|
||||
doFunctions: Array<DoFn_T<S,E,C>>
|
||||
};
|
||||
const Tick = function<E extends Event_T,C>(doFunctions : Array<DoFn_T<E,C>>) : Tick_T<E,C>{ return {doFunctions}; };
|
||||
const Tick = function<S,E extends Event_T,C>(doFunctions : Array<DoFn_T<S,E,C>>) : Tick_T<S,E,C>{ return {doFunctions}; };
|
||||
|
||||
|
||||
export interface Interpreter_T<S,E extends Event_T,C> {
|
||||
machine: Machine_T<S,E,C>;
|
||||
state: S;
|
||||
context: C | {};
|
||||
context: C;
|
||||
tickQueues:Array<TickQueue_T<S,E,C>>
|
||||
}
|
||||
export function interpret<S,E extends Event_T,C>(machine:Machine_T<S,E,C>, options?:{state?:S, context?:C | {}}) : Interpreter_T<S,E,C>{
|
||||
let {state, context} = options || {};
|
||||
type TickQueue_T<S,E extends Event_T,C> = Array<Tick_T<S,E,C>>;
|
||||
export function interpret<S,E extends Event_T,C>(machine:Machine_T<S,E,C>, 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};
|
||||
const interpreter = {machine, state, context, tickQueues:[]}
|
||||
//@ts-ignore
|
||||
send(interpreter, ['entry', null] );
|
||||
return interpreter;
|
||||
}
|
||||
|
||||
/** Helper function for `send()`
|
||||
@@ -54,6 +58,9 @@ function getState<S,E extends Event_T,C>(interpreter : Interpreter_T<S,E,C>) : S
|
||||
function getOns<S,E extends Event_T,C>(state : State_T<S,E,C>, event:E) : Array<On_T<S,E,C>>{
|
||||
return state.ons.filter((on)=>on.eventName===event[0]);
|
||||
}
|
||||
/** Helper function for `send()`
|
||||
*/
|
||||
function noop(){}
|
||||
/** 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;
|
||||
@@ -66,22 +73,31 @@ function getOns<S,E extends Event_T,C>(state : State_T<S,E,C>, event:E) : Array<
|
||||
*/
|
||||
export function send<S,E extends Event_T,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:C,e:E,self)=>{};
|
||||
}
|
||||
else{
|
||||
return (ctx:C,e:E,self)=>{};
|
||||
}
|
||||
})
|
||||
);
|
||||
const onsTree = getOns(state, event);
|
||||
const reactions = onsTree
|
||||
.map((on)=>on.reactions)
|
||||
.flat();
|
||||
const indexOfFirstGoto = reactions.findIndex((reaction)=>reaction.type==='Goto');
|
||||
const indexOfFinalReaction = indexOfFirstGoto === -1 ? reactions.length-1 : indexOfFirstGoto;
|
||||
const reactionsUntilFirstGoto = reactions.slice(0, indexOfFinalReaction+1);
|
||||
const functionsToRunInTick = reactionsUntilFirstGoto
|
||||
.map((reaction)=>{
|
||||
if(reaction.type === 'Do'){
|
||||
return reaction.fn;
|
||||
}
|
||||
else if(reaction.type === 'Goto'){
|
||||
return (ctx:C,e:E,self:Interpreter_T<S,E,C>)=>{
|
||||
self.state = reaction.targetStateName;
|
||||
//@ts-ignore
|
||||
send(self, ['entry', e] );
|
||||
};
|
||||
}
|
||||
else{
|
||||
return noop;
|
||||
}
|
||||
});
|
||||
const tick = Tick(functionsToRunInTick);
|
||||
tick.doFunctions.forEach((fn)=>{ fn(interpreter.context, event, interpreter); });
|
||||
}
|
||||
|
||||
export const Spawn = function(){};
|
||||
|
||||
+19
-13
@@ -1,35 +1,41 @@
|
||||
import { Machine, State, On, Do, Goto, Spawn, Unspawn } from '../index';
|
||||
import { Machine, State, On, Do, Goto, Spawn, Unspawn, interpret, Interpreter_T, send } from '../index';
|
||||
|
||||
const beginTimer = (ctx:C, e:E)=>{};
|
||||
const beginTimer = (ctx:C, e:E, self:Interpreter_T<S,E,C>)=>{ setTimeout(()=>{ send(self, ['timer-finished',null]); }, 800); };
|
||||
const log = (ctx:C, e:E, self:Interpreter_T<S,E,C>)=>{ console.log(self.state); };
|
||||
|
||||
type S = 'green' | 'yellow' | 'red';
|
||||
type E = ['entry',null] | ['timer-finished',null];
|
||||
type C = null;
|
||||
type C = {};
|
||||
|
||||
const machine =
|
||||
Machine<S,E,C>(
|
||||
State('green',
|
||||
On('entry',
|
||||
Do(beginTimer)
|
||||
On<S,E,C>('entry',
|
||||
Do(beginTimer),
|
||||
Do(log)
|
||||
),
|
||||
On('timer-finished',
|
||||
On<S,E,C>('timer-finished',
|
||||
Goto('yellow')
|
||||
)
|
||||
),
|
||||
State('yellow',
|
||||
On('entry',
|
||||
Do(beginTimer)
|
||||
On<S,E,C>('entry',
|
||||
Do(beginTimer),
|
||||
Do(log)
|
||||
),
|
||||
On('timer-finished',
|
||||
On<S,E,C>('timer-finished',
|
||||
Goto('red')
|
||||
)
|
||||
),
|
||||
State('red',
|
||||
On('entry',
|
||||
Do(beginTimer)
|
||||
On<S,E,C>('entry',
|
||||
Do(beginTimer),
|
||||
Do(log)
|
||||
),
|
||||
On('timer-finished',
|
||||
On<S,E,C>('timer-finished',
|
||||
Goto('green')
|
||||
)
|
||||
),
|
||||
);
|
||||
);
|
||||
|
||||
const actor = interpret(machine, {context:{}});
|
||||
Reference in New Issue
Block a user