remove typescript generics
they were getting in the way; it can be developed after the lib is more maturemain
parent
e328533629
commit
26383090ba
@ -1 +1,112 @@
|
||||
(()=>{var x=function(...t){return{states:t}},i=function(t,...e){return{name:t,ons:e}},o=function(t,...e){return{eventName:t,reactions:e}},s=function(t){return{type:"Do",fn:t}},S=function(t){return{type:"Goto",targetStateName:t}},l=function(t){return{doFunctions:t}};function u(t,e){let{state:n,context:C}=e;typeof n>"u"&&(n=t.states[0].name);let E={machine:t,state:n,context:C,tickQueues:[]};return c(E,["entry",null]),E}function m(t){return t.machine.states.find(e=>e.name===t.state)}function v(t,e){return t.ons.filter(n=>n.eventName===e[0])}function h(){}function c(t,e){let n=m(t),E=v(n,e).map(r=>r.reactions).flat(),_=E.findIndex(r=>r.type==="Goto"),f=_===-1?E.length-1:_,y=E.slice(0,f+1).map(r=>r.type==="Do"?r.fn:r.type==="Goto"?(k,d,p)=>{p.state=r.targetStateName,c(p,["entry",d])}:h);l(y).doFunctions.forEach(r=>{r(t.context,e,t)})}var a=(t,e,n)=>{setTimeout(()=>{c(n,["timer-finished",null])},800)},T=(t,e,n)=>{console.log(n.state)},D=x(i("green",o("entry",s(a),s(T)),o("timer-finished",S("yellow"))),i("yellow",o("entry",s(a),s(T)),o("timer-finished",S("red"))),i("red",o("entry",s(a),s(T)),o("timer-finished",S("green")))),M=u(D,{context:{}});})();
|
||||
(() => {
|
||||
// src/index.ts
|
||||
var Machine = function(...states) {
|
||||
return { states };
|
||||
};
|
||||
var State = function(name, ...ons) {
|
||||
return { name, ons };
|
||||
};
|
||||
var On = function(eventName, ...reactions) {
|
||||
return { eventName, reactions };
|
||||
};
|
||||
var Do = function(fn) {
|
||||
return { type: "Do", fn };
|
||||
};
|
||||
var Goto = function(targetStateName) {
|
||||
return { type: "Goto", targetStateName };
|
||||
};
|
||||
var Tick = function(doFunctions) {
|
||||
return { doFunctions };
|
||||
};
|
||||
function interpret(machine2, options) {
|
||||
let { state, context } = options;
|
||||
if (typeof state === "undefined") {
|
||||
state = machine2.states[0].name;
|
||||
}
|
||||
const interpreter = { machine: machine2, state, context, tickQueues: [] };
|
||||
console.log(interpreter);
|
||||
send(interpreter, ["entry", null]);
|
||||
return interpreter;
|
||||
}
|
||||
function getState(interpreter) {
|
||||
return interpreter.machine.states.find((state) => state.name === interpreter.state);
|
||||
}
|
||||
function getOns(state, event) {
|
||||
return state.ons.filter((on) => on.eventName === event[0]);
|
||||
}
|
||||
function noop() {
|
||||
}
|
||||
function send(interpreter, event) {
|
||||
const state = getState(interpreter);
|
||||
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, e, self) => {
|
||||
self.state = reaction.targetStateName;
|
||||
send(self, ["entry", e]);
|
||||
};
|
||||
} else {
|
||||
return noop;
|
||||
}
|
||||
});
|
||||
const tick = Tick(functionsToRunInTick);
|
||||
tick.doFunctions.forEach((fn) => {
|
||||
fn(interpreter.context, event, interpreter);
|
||||
});
|
||||
}
|
||||
|
||||
// src/tests/00-basic.ts
|
||||
var beginTimer = (ctx, e, self) => {
|
||||
setTimeout(() => {
|
||||
send(self, ["timer-finished", null]);
|
||||
}, 800);
|
||||
};
|
||||
var log = (ctx, e, self) => {
|
||||
console.log(self.state);
|
||||
};
|
||||
var machine = Machine(
|
||||
State(
|
||||
"green",
|
||||
On(
|
||||
"entry",
|
||||
Do(beginTimer),
|
||||
Do(log)
|
||||
),
|
||||
On(
|
||||
"timer-finished",
|
||||
Goto("yellow")
|
||||
)
|
||||
),
|
||||
State(
|
||||
"yellow",
|
||||
On(
|
||||
"entry",
|
||||
Do(beginTimer),
|
||||
Do(log)
|
||||
),
|
||||
On(
|
||||
"timer-finished",
|
||||
Goto("red")
|
||||
)
|
||||
),
|
||||
State(
|
||||
"red",
|
||||
On(
|
||||
"entry",
|
||||
Do(beginTimer),
|
||||
Do(log)
|
||||
),
|
||||
On(
|
||||
"timer-finished",
|
||||
Goto("green")
|
||||
)
|
||||
)
|
||||
);
|
||||
var actor = interpret(machine, { context: {} });
|
||||
})();
|
||||
|
@ -0,0 +1,152 @@
|
||||
(() => {
|
||||
// src/index.ts
|
||||
var Machine = function(...states) {
|
||||
return { states };
|
||||
};
|
||||
var State = function(name, ...ons) {
|
||||
return { name, ons };
|
||||
};
|
||||
var On = function(eventName, ...reactions) {
|
||||
return { eventName, reactions };
|
||||
};
|
||||
var Do = function(fn) {
|
||||
return { type: "Do", fn };
|
||||
};
|
||||
var Goto = function(targetStateName) {
|
||||
return { type: "Goto", targetStateName };
|
||||
};
|
||||
var Tick = function(doFunctions) {
|
||||
return { doFunctions };
|
||||
};
|
||||
function interpret(machine, options) {
|
||||
let { state, context } = options;
|
||||
if (typeof state === "undefined") {
|
||||
state = machine.states[0].name;
|
||||
}
|
||||
const interpreter = { machine, state, context, tickQueues: [] };
|
||||
console.log(interpreter);
|
||||
send(interpreter, ["entry", null]);
|
||||
return interpreter;
|
||||
}
|
||||
function getState(interpreter) {
|
||||
return interpreter.machine.states.find((state) => state.name === interpreter.state);
|
||||
}
|
||||
function getOns(state, event) {
|
||||
return state.ons.filter((on) => on.eventName === event[0]);
|
||||
}
|
||||
function noop() {
|
||||
}
|
||||
function send(interpreter, event) {
|
||||
const state = getState(interpreter);
|
||||
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, e, self) => {
|
||||
self.state = reaction.targetStateName;
|
||||
send(self, ["entry", e]);
|
||||
};
|
||||
} else {
|
||||
return noop;
|
||||
}
|
||||
});
|
||||
const tick = Tick(functionsToRunInTick);
|
||||
tick.doFunctions.forEach((fn) => {
|
||||
fn(interpreter.context, event, interpreter);
|
||||
});
|
||||
}
|
||||
|
||||
// src/tests/01-ping-pong.ts
|
||||
var wait = (ms) => new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(1);
|
||||
}, ms);
|
||||
});
|
||||
var makeRequest = (ctx, e, self) => {
|
||||
send(ctx.serverActor, ["received-request", self]);
|
||||
};
|
||||
var sendResponse = (ctx, e, self) => {
|
||||
send(ctx.clientActor, ["received-response", self]);
|
||||
};
|
||||
var startTimer = async (ctx, e, self) => {
|
||||
await wait(500);
|
||||
send(self, ["timer-finished", null]);
|
||||
};
|
||||
var log = (ctx, e, self) => {
|
||||
console.log(self.state);
|
||||
};
|
||||
var client = Machine(
|
||||
State(
|
||||
"idle",
|
||||
On(
|
||||
"entry",
|
||||
Do(log)
|
||||
),
|
||||
On(
|
||||
"server-created",
|
||||
Do((_ctx, [_eventName, serverActor2], self) => {
|
||||
self.context.serverActor = serverActor2;
|
||||
}),
|
||||
Goto("making-request")
|
||||
)
|
||||
),
|
||||
State(
|
||||
"making-request",
|
||||
On(
|
||||
"entry",
|
||||
Do(log),
|
||||
Do(makeRequest),
|
||||
Goto("awaiting-response")
|
||||
)
|
||||
),
|
||||
State(
|
||||
"awaiting-response",
|
||||
On(
|
||||
"entry",
|
||||
Do(log)
|
||||
),
|
||||
On(
|
||||
"received-response",
|
||||
Do(log),
|
||||
Goto("making-request")
|
||||
)
|
||||
)
|
||||
);
|
||||
var server = Machine(
|
||||
State(
|
||||
"awaiting-request",
|
||||
On(
|
||||
"entry",
|
||||
Do(log)
|
||||
),
|
||||
On(
|
||||
"received-request",
|
||||
Do((_ctx, [_eventName, clientActor2], self) => {
|
||||
self.context.clientActor = clientActor2;
|
||||
}),
|
||||
Goto("sending-response")
|
||||
)
|
||||
),
|
||||
State(
|
||||
"sending-response",
|
||||
On(
|
||||
"entry",
|
||||
Do(log),
|
||||
Do(startTimer)
|
||||
),
|
||||
On(
|
||||
"timer-finished",
|
||||
Do(sendResponse),
|
||||
Goto("awaiting-request")
|
||||
)
|
||||
)
|
||||
);
|
||||
var clientActor = interpret(client, { context: {} });
|
||||
var serverActor = interpret(server, { context: {} });
|
||||
send(clientActor, ["server-created", serverActor]);
|
||||
})();
|
@ -0,0 +1,84 @@
|
||||
import { Machine, State, On, Do, Goto, Spawn, Unspawn, interpret, Interpreter_T, send, Event_T } from '../index';
|
||||
|
||||
const wait = (ms:number)=>new Promise((resolve)=>{ setTimeout(()=>{ resolve(1); }, ms); });
|
||||
const makeRequest = (ctx,e,self)=>{ send(ctx.serverActor, ['received-request',self]); };
|
||||
const sendResponse = (ctx,e,self)=>{ send(ctx.clientActor, ['received-response',self]); };
|
||||
const startTimer = async (ctx,e,self)=>{ await wait(500); send(self, ['timer-finished',null]); }
|
||||
const log = (ctx, e, self:Interpreter_T)=>{ console.log(self.state); };
|
||||
|
||||
type Sc =
|
||||
| 'idle'
|
||||
| 'making-request'
|
||||
| 'awaiting-response';
|
||||
type Ec =
|
||||
| ['entry',null]
|
||||
| ['received-response',Interpreter_T]
|
||||
| ['server-created', Interpreter_T];
|
||||
type Cc =
|
||||
| {serverActor?:Interpreter_T};
|
||||
|
||||
const client =
|
||||
Machine(
|
||||
State('idle',
|
||||
On('entry',
|
||||
Do(log),
|
||||
),
|
||||
On('server-created',
|
||||
|
||||
Do((_ctx,[_eventName,serverActor],self)=>{ self.context.serverActor=serverActor; }),
|
||||
Goto('making-request')
|
||||
)
|
||||
),
|
||||
State('making-request',
|
||||
On('entry',
|
||||
Do(log),
|
||||
Do(makeRequest),
|
||||
Goto('awaiting-response')
|
||||
),
|
||||
),
|
||||
State('awaiting-response',
|
||||
On('entry',
|
||||
Do(log),
|
||||
),
|
||||
On('received-response',
|
||||
Do(log),
|
||||
Goto('making-request')
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
type Ss =
|
||||
| 'awaiting-request'
|
||||
| 'sending-response';
|
||||
type Es =
|
||||
| ['entry',null]
|
||||
| ['received-request',Interpreter_T]
|
||||
| ['timer-finished', null];
|
||||
type Cs = {clientActor?:Interpreter_T};
|
||||
|
||||
const server =
|
||||
Machine(
|
||||
State('awaiting-request',
|
||||
On('entry',
|
||||
Do(log),
|
||||
),
|
||||
On('received-request',
|
||||
Do((_ctx,[_eventName,clientActor],self)=>{ self.context.clientActor=clientActor; }),
|
||||
Goto('sending-response')
|
||||
),
|
||||
),
|
||||
State('sending-response',
|
||||
On('entry',
|
||||
Do(log),
|
||||
Do(startTimer)
|
||||
),
|
||||
On('timer-finished',
|
||||
Do(sendResponse),
|
||||
Goto('awaiting-request')
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
const clientActor = interpret(client, {context:{}});
|
||||
const serverActor = interpret(server, {context:{}});
|
||||
send(clientActor, ['server-created', serverActor]);
|
Loading…
Reference in New Issue