专栏首页原创分享用js实现一个状态机

用js实现一个状态机

var utils = {
    getType: function(content) {
        return Object.prototype.toString.call(content).match(/\[object (.*)\]/)[1];
    },
    isType: function(content, type) {
        return this.getType(content).toLowerCase() === type.toLowerCase();
    }

};
function FSMTable(arg) {
    if (this instanceof FSMTable) {
        this.table = [];
        if (utils.isType(arg, 'Array')) {
            for(var i = 0 ;i < arg.length; i++) {
                this.table.push(new FSMItem(arg[i]));
            }
        }
        return this;
    }
    return new FSMTable(arg);
}

FSMTable.prototype = {
    append: function(arg) {
        this.table.push(arg);
    },
    getLenght: function() {
        return this.table.length;
    },
    getItem: function(index) {
        if (index >= this.getLenght()) {
            return null;
        }
        return this.table[index];
    },
    setItem: function(state, arg) {
        var len = this.getLenght();
        for (var i = 0; i < len; i++) {
            if (this.table[i]['state'] === state) {
                for (var k in arg) {
                    if (arg.hasOwnProperty(k) && k !== 'state') {
                        this.table[i][k] = arg[k];
                    }
                }
                break;
            }
        }
    },
    delItem: function(state) {
        var len = this.getLenght();
        for (var i = 0; i < len; i++) {
            if (this.table[i]['state'] === state) {
                delete this.table[i];
                break;
            }
        }
    }
}
function FSMItem(arg) {
    this.state = arg.state;
    this.eventHandlers = arg.eventHandlers;
}




function Iteraor(content, cfg) {
    this.content = content;
    this.length = 0;
    this.count = 0;
    this.position = 0;
    this.direction = (cfg && cfg.direction) || 0;
    this.init();
}
Iteraor.prototype = {
    next: function() {
        if (!this.hasNext()) {
            return null;
        }
        this.count++;
        if (this.direction === 0) {
            return this.content[this.position++];
        } else {
            return this.content[this.position--];
        }

    },  
    rewind: function(index) {
        if (index  < this.length && index >= 0) {
            this.position = index;
            this.count = this.direction === 0 ? index : this.length - index - 1;
        }
    },
    hasNext: function() {
        return this.count < this.length;
    },
    init: function() {

        var type = utils.getType(this.content);
        switch(type) {
            case 'String': 
                            this.content = this.content.split('');
                            break;
            case 'Array': 
                            this.content = this.content.concat();
                            break;
            case 'Object': 
                        var arr = [];
                        var keys = Object.keys(this.content);
                        for (var i = 0; i< keys.length; i++) {
                            arr.push(this.content[keys[i]]);
                        }
                        this.content = arr;
                        break;
        }

        this.length = this.content.length;
        this.position = this.direction === 0 ? 0 : this.length - 1;
    }
}

function FSM(table,initState,endState,content,cfg) {
    this.iterator = new Iteraor(content, cfg);
    this.table = table;
    this.currentState = initState;
    this.endState = endState;
    this.result = [];
    this.currentResult = '';
    this.hooks = (cfg && cfg.hooks) || null;
}
FSM.prototype = {
    getCurrentState: function() {
        return this.currentState;
    },
    setCurrentState: function(state) {
        return this.currentState = state;
    },
    getCurrentResult: function() {
        return this.currentResult;
    },
    setCurrentResult: function(val, mode) {
        mode = mode || 'a';
        switch(mode) {
            case 'a' : this.currentResult += val; break;
            case 'w' : this.currentResult = val; break;
        }
    },
    getResult: function() {
        return this.result;
    },
    setResult: function(val, mode) {
        mode = mode || 'a';
        switch(mode) {
            case 'a' : this.result.push(val); break;
            case 'w' : this.result = val; break;
        }

    },
    setHook: function(hookName, hookValue) {
        this.hooks[hookName] = hookValue;
    },
    stateHandler: function() {
        var content = this.getContent();
        var event = this.getEvent(content);
        var tableLength = this.table.getLenght();
        for (var index = 0; index < tableLength; index++) {
            var item = this.table.getItem(index);
            if (item.state === this.currentState && item.eventHandlers[event] && utils.isType(item.eventHandlers[event], 'function')) {
                if (item.eventHandlers[event].call(this,content) === false) {
                    return false;
                } 
                break;
            }
        }
        return true;
    },
    getEvent: function(content) {
        return this.hooks.eventGetter.call(this,content);
    },
    getContent: function() {
        var content = null;
        if (this.iterator.hasNext()) {
            content = this.iterator.next();
        } 
        return content;
    },
    run: function() {
        var flag;
        do {
            flag = this.stateHandler();
        } while (!!flag);
    }
}


function charHandler(char) {
    this.setCurrentResult(char,'a');
    this.setCurrentState(STATE.INWORD);
}
    var STATE = {
        START:0,
        INWORD: 1,
        SPACE: 2,
        END:3
    }
    var EVENT = {
        GETACHAR: 1,
        GETASPACE:2,
        END: 3
    };
    var table = new FSMTable([{
        state:STATE.START,
        eventHandlers: {[EVENT.GETACHAR]: charHandler, [EVENT.GETASPACE]: function() {

        }}
    },{
        state:STATE.INWORD,
        eventHandlers: {[EVENT.GETACHAR]: charHandler, [EVENT.GETASPACE]: function(char) {
            this.setCurrentState(STATE.SPACE);
            this.setResult(this.getCurrentResult(),'a');
            this.setCurrentResult('','w');
        }}
    },{
        state:STATE.SPACE,
        eventHandlers: {[EVENT.GETACHAR]: charHandler, [EVENT.GETASPACE]: function(char) {

        }}
    },{
        state:STATE.END,
        eventHandlers: {[EVENT.END]: function() {
            if (this.getCurrentResult() !== '') {
                this.setResult(this.getCurrentResult(),'a');//.split('').reverse().join('')
            }

            return false;
        }}
    }]);
    var hooks = {
        eventGetter:function(content) {
            var event = null;
            if (content === null) {
                event = EVENT.END;
                this.setCurrentState(this.endState);
            }else if(/^\s$/.test(content)) {
                event = EVENT.GETASPACE;
            }else {
                event = EVENT.GETACHAR;
            }
                return event;
        }
    };

    data = 'sas s as a fc s f vf v fsv s';
    var fsm = new FSM(table,STATE.START,STATE.END,data, {direction: 0, hooks: hooks});
    fsm.run();
    console.log(fsm.result)
    /* nodejs 
    var fs = require('fs');
    var str = fs.readFile('fsm6.js',(err,data)=> {
        data = data.toString()
        var fsm = new FSM(table,STATE.START,STATE.END,data, {direction: 0, hooks: hooks});
        fsm.run();
        console.log(fsm.result)
    })
    */

本文分享自微信公众号 - 编程杂技(theanarkh)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • nodejs 14.0.0源码分析之FixedQueue

    theanarkh
  • nodejs可读流源码分析

    可读流是对数据消费的抽象,nodejs中可读流有两种工作模式:流式和暂停式,流式就是有数据的时候就会触发回调,并且把数据传给回调,暂停式就是需要用户自己手动执行...

    theanarkh
  • nodejs-ipc的设计与实现

    对于有继承关系的进程,nodejs本身为我们提供了进程间通信的方式,但是对于没有继承关系的进程,比如兄弟进程,想要通信最简单的方式就是通过主进程...

    theanarkh
  • bug 回忆录(四)

    @author Ken @time 2020-09-27 21:30:59 @description 转载请备注出处,谢谢

    公众号---人生代码
  • 聊聊rocketmq的HAClient

    rocketmq-all-4.6.0-source-release/store/src/main/java/org/apache/rocketmq/store/...

    codecraft
  • Flutter 入门指北之基础部件

    原文:https://www.jianshu.com/p/8ddb16902ce6

    陈宇明
  • HTML5 Canvas炫酷的火焰风暴动画

    越陌度阡
  • .glb格式的模型怎么在three.js中展示

    3D软件中导出的格式一般有.obj 和.glb ,下面是blender 2.8.2 生成模型并在three.js中展示的流程

    tianyawhl
  • 云终端系列(一)—— 实时音视频Web端接入体验(Vue基础音视频通话篇)

    这个系列呢,主要给各位观众老爷看看目前有较大趋势的SaaS应用的SDK在各种主流Web终端的使用姿势和异常分析,如果想要纯粹了解开发的或者云原生,云开发的可以去...

    楚歌
  • 分享一个关于this对象的编程小技巧,如何使用箭头函数避免this对象混淆?

    以微信小程序举例。小程序的主要语言是js,使用小程序也方便说明我们接下来要讲的问题。

    程序员LIYI

扫码关注云+社区

领取腾讯云代金券