惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
A
About on SuperTechFans
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 叶小钗
博客园 - 聂微东
博客园 - Franky
Apple Machine Learning Research
Apple Machine Learning Research
罗磊的独立博客
量子位
博客园 - 三生石上(FineUI控件)
Recent Announcements
Recent Announcements
The GitHub Blog
The GitHub Blog
B
Blog RSS Feed
T
The Blog of Author Tim Ferriss
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
Last Week in AI
Last Week in AI
宝玉的分享
宝玉的分享
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Stack Overflow Blog
Stack Overflow Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

博客园 - 随机

10周年整 年中review Javascript 模块化开发上线解决方案 AMDJS编译工具 第三方组件接入方案(演示文稿图片) 代码打包预处理工具 manifest资源提取工具 也来山寨一版Flappy Bird (js版) 2014 todo list 2013年终总结 #1分钟删# 模块合并小工具 移动端页面前端提速的技术梳理 [整理] ES5 词法约定文档树状图 php并发请求 [转]给明年依然年轻的我们:欲望、外界、标签、天才、时间、人生目标、现实、后悔、和经历 javascript基础组件设计资料 2013 草莓音乐节妹子抓拍 [组图] 键盘按键事件的fireEvent 错觉
状态机——Javascript词法扫描示例
随机 · 2013-09-27 · via 博客园 - 随机

  所谓的状态机实质其实很很简单,其存在的目的也是把大量复杂的处理分散,使处理变得简单化一些。状态机只有一个当前状态,并且在当前状态下根据输入进行处理,然后再决定是否改变当前状态,然后再处理下一个输入,如此往复直到所有输入结束。
  所以,相同的输入在不同的当前状态下的处理是不一样的,以字符串的处理为例,我们来看看怎么处理下面这条语句:

  我们需要得到的结果序列应该是:
    标识符str,标点符号=,字面量"123\"abc",标点符号;

  首先我们会建立起几种处理的状态(这里只是针对这个列子,实际开发的状态比这多得多T_T):
    a.一般状态处理;
    b.标识符状态处理;
    c.标点符号状态处理;
    d.双引号字符串字面量状态处理;
    e.双引号字符串字面量遇到\符号时的状态处理;

  建立完成状态处理方法后,我们将语句作为字符串输入流,一个个字符地进行输入处理:
    1)输入s,首先进入状态a进行一般处理,判断出该字符符合js标识符规则,记录当前字符,将当前状态转换为状态b;
    2)继续输入下一个字符t,进入状态b进行字符处理,字符t符合js标识符规则,记录当前字符,并且当前状态还是状态b,不发生改变;
    3)继续输入下一个字符r,进入状态b进行字符处理,字符r符合js标识符规则,记录当前字符,并且当前状态还是状态b,不发生改变;
    4)继续输入下一个字符=,进入状态b进行字符处理,字符=不符合当前状态需要的js标识符规则,于是保存之前记录的字符集,并标记为id类型,即["id","str"]。再将当前状态转换为状态a;
    5)在当前状态a下继续输入刚才未处理的字符=,判断出其符合js标点符号规则,记录当前字符,并将当前状态转换为状态c;
    6)继续输入下一个字符",进入状态c进行标点符号处理,判读出字符"并不符合标点符号规则,于是保存记录的字符集,并标记为标点符号类型["pun","="]。再将当前状态转换为状态a;
    7)在当前状态a下继续输入刚才未处理的字符",判断出其符合js字符串字面量规则,记录当前字符,并将当前状态转换为状态d;
    8)继续输入下一个字符1,在状态d下处理,符合js字符串字面量规则,记录当前字符;
    9)继续输入下一个字符2,在状态d下处理,符合js字符串字面量规则,记录当前字符;
    10)继续输入下一个字符3,在状态d下处理,符合js字符串字面量规则,记录当前字符;
    11)继续输入下一个字符\,在状态d下处理,\字符在状态d里会触发状态转换,记录当前字符,将当前状态转换为状态e;
    12)继续输入下一个字符",在状态e下处理,判断符合当前的处理规则,记录当前字符",将状态转换为状态d;
    13)继续输入下一个字符a,在状态d下处理,符合js字符串字面量规则,记录当前字符;
    14)继续输入下一个字符b,在状态d下处理,符合js字符串字面量规则,记录当前字符;
    15)继续输入下一个字符c,在状态d下处理,符合js字符串字面量规则,记录当前字符;
    16) 继续输入下一个字符",在状态d下处理,状态d接收到"时就可以判断出当前状态结束了,于是保存当前的记录的字符集,并标记为字符串字面量类型["str","\"123\\\"abc\""],再将当前状态转换为状态a;
    17)继续输入下一个字符;,在状态a下处理,判断出其符合js标点符号规则,记录当前字符,将状态转换为状态c;
    18)现在所有字符都扫描完了,我们可以人为加一个终止符,当再读到最后的终止符时,判断出不符合标点符号规则,保存字符集,标记为标点符号类型["pun",";"];
    19)处理结束。

  于是我们就得到了我们需要的词法序列:
    [["id","str"], ["pun","="], ["str","\"123\\\"abc\""], ["pun",";""]]

  简化版的代码看起来大概就是这个样子:  

   var Reader= function(str){
        var index=0;
        var stream=str;
            
        stream +=" ";
        
        var me={
            get char(){
                return stream[index];
            },
            
            get length(){
                return stream.length;
            },
            
            get stream(){
                return stream;
            },
            
            get pchar(){
                return stream[index-1];
            },
            
            get nchar(){
                return stream[index+1];
            },
            
            get eof(){
                return index === stream.length;
            },
            
            next : function(){
                index++;
            },
            
            prev : function(){
                index--;
            }
            
        };
        
        return me;
    };
  
  
    var statement="str=\"123\\\"abc\";";
    var reader=Reader(statement);
    var l=reader.length;
    var i;
    var newState;
    var state;
    var tokenList=[];
    var word="";
    
    var punctuatorList=["{", "}", "(", ")", "[", "]", ".", ";", ",", "<", ">", "<=",
                    ">=", "==", "!=", "===", "!==", "+", "-", "*", "%", "++", "--",
                    "<<", ">>", ">>>", "&", "|", "^", "!", "~", "&&", "||", "?", ":",
                    "=", "+=", "-=", "*=", "%=", "<<=", ">>=", ">>>=", "&=", "|=", "^="];
                    
    function checkUnicodeLetter(c){
        return c.match(/[a-z]/i); //囧oz
    }
    
    function checkUnicodeNumber(c){
        return (c.charCodeAt() >= "\u0030".charCodeAt() && c.charCodeAt() <= "\u0039".charCodeAt())
            || (c.charCodeAt() >= "\u1D7CE".charCodeAt() && c.charCodeAt() <= "\u1D7FF".charCodeAt());
            
    }
    
    function emitToken(type){
        tokenList.push([type, word]);   
        word="";
    }
    
    
    
    function dataState(c){
        if(punctuatorList.indexOf(c) > -1){
            word=c;
            return punctuatorState;
            
        }else if(checkUnicodeLetter(c) || c==="_" || c==="$" || c==="\\"){
            word=c;
            return identifierState;
            
        }else if(c==="\""){
            word=c;
            return doubleStringLiteralState;
        }
    }
    
    function punctuatorState(c){
        if(punctuatorList.indexOf(word+c) === -1){
            emitToken("pun");
            reader.prev();
            return dataState;
        }else{
            word += c;
        }
    }
    
    function identifierState(c){
        if(checkUnicodeLetter(c) || checkUnicodeNumber(c)){
            word += c;
            
        }else{
            
            emitToken("id");
            reader.prev();
            return dataState;
        }
    }
    
    function doubleStringLiteralState(c){
        if(c==="\\"){
            word += c;
            return doubleStringLiteralEscapeSequenceState;
            
        }else if(c==="\""){
            word += c;
            emitToken("str");
            return dataState;
        }else{
            word += c;
        }
    }
    function doubleStringLiteralEscapeSequenceState(c){
        word+=c;
        return doubleStringLiteralState;
    }
    
    
    state=dataState;
    while(!reader.eof){
        newState=state(reader.char);
        newState && (state=newState);
        reader.next();
    }
    
    alert(JSON.stringify(tokenList));

View Code

  这就是状态机的运作方式,不过要写全各种状态这种事真特么不是人干的~~