export const FSM = ({ jumping, success, failure, debug } = {}) => { // holds STATE and ARROW items const entries = [{ uid: 0, def: 'ROOT' }] // aliases lookups table const lookups = new Map () const runtime = { GUID: 0, state: null, trace: [], data: null } // state definition const state = ({ name, init, term, payload }) => { const def = 'STATE' const alias = ('' + name).toUpperCase () const uid = ++runtime.GUID const uids = lookups.get(alias) || [] const len = uids.length const item = { uid, def, alias, init, term, payload } if (len === 0) { entries.push (item) lookups.set (alias, [uid]) } else { throw new Error ('duplicate entry ' + uis + ' ' + alias) } if (debug) { if (len === 0) { console.log ('creating new state', { alias, uid }) } else { console.log ('replacing item', { alias, uid }) } } } // arrow definition const arrow = ({ from, accept, to, ops }) => { const def = 'ARROW' const alias = ('' + accept).toLowerCase () const uid = ++runtime.GUID const uids = lookups.get(alias) || [] const len = uids.length const item = { uid, def, alias, from, accept, to, ops } entries.push (item) uids.push (uid) lookups.set (alias, uids) // console.log ('craating arrow', { item }) } // ready to run !!! const pack = ({ state, data }) => { const alias = ('' + state).toUpperCase () runtime.state = alias runtime.data = data if (debug) { console.log('Finite State Machine packing', alias) } } // read entry word const read = ({ word, data }) => { const alias = ('' + word).toLowerCase () const uids = lookups.get (alias) || [] let accepted = false console.log ('read', { [alias]: uids.join(', ') }) uids.map ((uid) => entries [uid]) .map ((item) => { console.log ('MAP', { item }) return item }) .filter ((item) => accepted === false) .filter ((item) => item.def === 'ARROW') .filter ((item) => item.from === runtime.state) .filter ((item) => item.accept === alias) .map ((item) => { const suids = lookups.get (item.to) || [] const final = entries [suids[0]] runtime.state = item.to runtime.trace.push (alias) accepted = true jumping && jumping.call && jumping.call (null, { trace: runtime.trace, result: runtime.data, state: runtime.state, word: alias }) item.ops && item.ios.call && item.ops.call (null, { data, trace: runtime.trace, result: runtime.data, state: runtime.state, word: alias }) if (final.term) { success && success.call (null, { data, trace: runtime.trace, result: runtime.data, state: runtime.state, word: alias }) } return true }) if (accepted === false) { failure && failure.call (null, { data, trace: runtime.trace, result: runtime.data, state: runtime.state, word: alias }) } } // return debug table as string const table = () => { const texts = [] texts.push ('~~~ Finistamach: lightweight Finite State Machine ~~~') texts.push ('') texts.push ('uid\tdef\talias\t\tmore info...') texts.push ('--------'.repeat(5)) entries.map ((item) => { if (item.def === 'STATE') { texts.push ([ item.uid, 'STATE', item.alias + '\t', (item.init ? '*init*' : '') + (item.term ? "*term*" : "") ].join('\t')) } if (item.def === 'ARROW') { texts.push ([ item.uid, 'ARROW', item.alias + '\t', item.from + ' --{' + item.accept + '}-> ' + item.to ].join('\t')) } }) return texts.join('\n') } return { state, arrow, pack, read, table } }