sync send works

This commit is contained in:
Brian Sakal
2023-05-12 00:41:47 -04:00
parent 0495f1d87a
commit e328533629
6 changed files with 77 additions and 45 deletions
+47 -31
View File
@@ -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
View File
@@ -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:{}});