|
|
@ -42,16 +42,33 @@ export interface Interpreter_T {
|
|
|
|
state: string;
|
|
|
|
state: string;
|
|
|
|
context: any;
|
|
|
|
context: any;
|
|
|
|
eventQueue:Array<Event_T>;
|
|
|
|
eventQueue:Array<Event_T>;
|
|
|
|
|
|
|
|
subscriptions: Record<string, SubscriptionCallbackFunction_T>;
|
|
|
|
isTransitioning: boolean;
|
|
|
|
isTransitioning: boolean;
|
|
|
|
subscriptions: Record<string, SubscriptionCallbackFunction_T>
|
|
|
|
isPaused: boolean;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export function interpret(machine:Machine_T, options:{state?:string, context:any}) : Interpreter_T{
|
|
|
|
|
|
|
|
let {state, context} = options;
|
|
|
|
/**
|
|
|
|
if(typeof state === 'undefined'){ state = machine.states[0].name; }
|
|
|
|
* Description placeholder
|
|
|
|
const interpreter = {machine, state, context, eventQueue:[], isTransitioning:false, subscriptions: {}}
|
|
|
|
*
|
|
|
|
|
|
|
|
* @export
|
|
|
|
|
|
|
|
* @param {Machine_T} machine
|
|
|
|
|
|
|
|
* @param {InitialContextFunction_T} initialContextFunction - in the form of a function rather than a direct value, so as to facilitate co-initialization of peer interpreters. Otherwise, the "parent" interpreter will start, but without a reference to a running child interpreter which it might expect to exist.
|
|
|
|
|
|
|
|
* @param {?string} [initialStateName]
|
|
|
|
|
|
|
|
* @returns {Interpreter_T}
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
export function Interpreter(machine:Machine_T, initialContext:any, initialStateName?:string) : Interpreter_T{
|
|
|
|
|
|
|
|
if(typeof initialStateName === 'undefined'){ initialStateName = machine.states[0].name; }
|
|
|
|
|
|
|
|
const interpreter = {machine, state: initialStateName, context:initialContext, eventQueue:[], isTransitioning:false, subscriptions: {}, isPaused: true}
|
|
|
|
send(interpreter, ['entry', null] );
|
|
|
|
send(interpreter, ['entry', null] );
|
|
|
|
return interpreter;
|
|
|
|
return interpreter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function start(interpreter:Interpreter_T){
|
|
|
|
|
|
|
|
interpreter.isPaused = false;
|
|
|
|
|
|
|
|
processEvents(interpreter);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export function pause(interpreter:Interpreter_T){
|
|
|
|
|
|
|
|
interpreter.isPaused = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Helper function for `send()`
|
|
|
|
/** Helper function for `send()`
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -76,16 +93,19 @@ function getMatchingEventReactionCouplings(state : State_T, event:Event_T) : Arr
|
|
|
|
export function send(interpreter : Interpreter_T, event:Event_T){
|
|
|
|
export function send(interpreter : Interpreter_T, event:Event_T){
|
|
|
|
interpreter.eventQueue.push(event);
|
|
|
|
interpreter.eventQueue.push(event);
|
|
|
|
if(interpreter.isTransitioning === false){
|
|
|
|
if(interpreter.isTransitioning === false){
|
|
|
|
interpreter.isTransitioning = true;
|
|
|
|
processEvents(interpreter);
|
|
|
|
while(interpreter.eventQueue.length > 0){
|
|
|
|
|
|
|
|
processNextEvent(interpreter);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
interpreter.isTransitioning = false;
|
|
|
|
|
|
|
|
// only run subscriptions here, once the machine's state has settled:
|
|
|
|
|
|
|
|
Object.values(interpreter.subscriptions).forEach((subscriptionCallbackFunction)=>{ subscriptionCallbackFunction(interpreter); });
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export const enqueue = send;
|
|
|
|
export const enqueue = send;
|
|
|
|
|
|
|
|
function processEvents(interpreter:Interpreter_T){
|
|
|
|
|
|
|
|
interpreter.isTransitioning = true;
|
|
|
|
|
|
|
|
while(interpreter.eventQueue.length > 0 && interpreter.isPaused===false){
|
|
|
|
|
|
|
|
processNextEvent(interpreter);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
interpreter.isTransitioning = false;
|
|
|
|
|
|
|
|
// only run subscriptions here, once the machine's state has settled:
|
|
|
|
|
|
|
|
Object.values(interpreter.subscriptions).forEach((subscriptionCallbackFunction)=>{ subscriptionCallbackFunction(interpreter); });
|
|
|
|
|
|
|
|
}
|
|
|
|
function processNextEvent(interpreter:Interpreter_T){
|
|
|
|
function processNextEvent(interpreter:Interpreter_T){
|
|
|
|
const nextEvent = interpreter.eventQueue.shift();
|
|
|
|
const nextEvent = interpreter.eventQueue.shift();
|
|
|
|
if(typeof nextEvent !== 'undefined'){
|
|
|
|
if(typeof nextEvent !== 'undefined'){
|
|
|
@ -135,7 +155,7 @@ let subscriptionId : number = 0;
|
|
|
|
export function subscribe(interpreter:Interpreter_T, callback:SubscriptionCallbackFunction_T){
|
|
|
|
export function subscribe(interpreter:Interpreter_T, callback:SubscriptionCallbackFunction_T){
|
|
|
|
subscriptionId++;
|
|
|
|
subscriptionId++;
|
|
|
|
interpreter.subscriptions[subscriptionId.toString()] = callback;
|
|
|
|
interpreter.subscriptions[subscriptionId.toString()] = callback;
|
|
|
|
return subscriptionId;
|
|
|
|
return subscriptionId.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export function unsubscribe(interpreter:Interpreter_T, subscriptionId:string){
|
|
|
|
export function unsubscribe(interpreter:Interpreter_T, subscriptionId:string){
|
|
|
|
delete interpreter.subscriptions[subscriptionId.toString()];
|
|
|
|
delete interpreter.subscriptions[subscriptionId.toString()];
|
|
|
|