zoukankan      html  css  js  c++  java
  • 状态机——Javascript词法扫描示例

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

    str="123"abc";

      我们需要得到的结果序列应该是:
        标识符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

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

  • 相关阅读:
    wppay免登录付费查看隐藏内容/付费资源下载
    个人网站html5雪花飘落代码JS特效下载
    HTML5 audio 如何实现播放多个MP3音频
    网站html代码解析
    vue-webpack模板升级到webpack4
    npm安装cnpm
    单个div充满屏幕的CSS方法
    vue监听滚动事件-元素固定位置显示
    HTML5中判断横屏竖屏
    The META for Mobile terminal
  • 原文地址:https://www.cnblogs.com/Random/p/3343074.html
Copyright © 2011-2022 走看看