Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
September 15, 2022 02:07 pm GMT

smplie finite state machine for EcmaScript

Mach: flexible FSM in ECMAScript

Hi there,

I just developped a simple Finite State Machine in JS. My initial goal was to integrate it within a SvelteKit component to provide UI state management.

Design and usage

this is a single ECMAScript file; simply import it in yout code, and call the FSM () constructor with its parameters:

    import { FSM } from './FSM.js'    const fsm = FSM ({        debug: true,        jumps: ({ state }) => console.log ({ state }),        success: ({ state }) => console.log ('success'),        failure: ({ state }) => console.log ('failure')    })

Then you can simply define states and transition arrows:

    fsm.state ({ name: 'INIT', init: true })    fsm.state ({ name: 'TERM', term: true })    fsm.state ({ name: 'OFF' })    fsm.state ({ name: 'ON' })    fsm.arrow ({ from: 'INIT', accept: 'activate', to: 'OFF'})    fsm.arrow ({ from: 'OFF', accept: 'switch', to: 'ON'})    fsm.arrow ({ from: 'ON', accept: 'switch', to: 'OFF'})    fsm.arrow ({ from: 'ON', accept: "kick", to: 'TERM'})

Of course with this way of doing things, you can freely hydrate states and transition arrows from an external data store such a database; or modularize you FSM design according to logial tests !

After all, pack the machine to run it, and read some entries:

    fsm.pack ({ state: 'INIT '})    fsm.accept ({ word: 'activate'})    fsm.accept ({ word: 'on'})    fsm.accept ({ word: 'kick'})    // console.log ('success')    

Implementation details

all entries (STATES and reanition arrows) are stored in the same array structure, as duck blocks. The items list is doubled by a lookup map which holds pairs (name) => [array-of-indexes] for all name inside the machine (UPPERCASED for STATES and lowercased for arrows). This shorts the time used to locate states/words blocks .

Further steps

adding support for asynchronous transduction operations.

    /// Here's the complete code for the Mach FSM ...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 ('uiddefaliasmore info...')        texts.push ('--------'.repeat(5))        entries.map ((item) => {            if (item.def === 'STATE') {                texts.push ([                    item.uid, 'STATE', item.alias + '',                     (item.init ? '*init*' : '') +  (item.term ? "*term*" : "")                ].join(''))            }            if (item.def === 'ARROW') {                texts.push ([                    item.uid, 'ARROW', item.alias + '',                     item.from + ' --{' + item.accept +  '}-> ' +  item.to                ].join(''))            }        })        return texts.join('
') } return { state, arrow, pack, read, table }}

Original Link: https://dev.to/hefeust/smplie-finite-state-machine-for-ecmascript-2a5d

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To