状态机——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)继续输入下一个字符,在状态d下处理,符合js字符串字面量规则,记录当前字符;
9)继续输入下一个字符,在状态d下处理,符合js字符串字面量规则,记录当前字符;
10)继续输入下一个字符,在状态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));
这就是状态机的运作方式,不过要写全各种状态这种事真特么不是人干的~~
状态机——Javascript词法扫描示例的更多相关文章
- JavaScript 词法 All In One
JavaScript 词法 All In One JavaScript 词法 这部分描述了JavaScript 的词法(lexical grammar). ECMAScript 源码文本会被从左到右扫 ...
- XAMARIN ANDROID 二维码扫描示例
现在二维码的应用越来越普及,二维码扫描也成为手机应用程序的必备功能了.本文将基于 Xamarin.Android 平台使用 ZXing.Net.Mobile 做一个简单的 Android 条码扫描示 ...
- Javascript调用ActiveX示例
Javascript调用ActiveX示例 写一个ActiveX控件比如叫做MyNameSpace.SecreteInfo,安装在客户机器上,这样可以通过c++获取到机器的几乎任何信息. 在网 ...
- 第二章:Javascript词法结构
编程语言的词法结构是一套基础性的规则,用来描述你如何编写这门语言.作为语法的基础,它规定了变量名是怎么样的,如何写注释,以及语句之间是如何区分的.本节用很短的篇幅来介绍javascript的词法结构. ...
- 网易JS面试题与Javascript词法作用域说明
调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量).函数参数及Arguments对象都在函数内的作用域中--这意味着它们隐藏了作用域链更上层的任何同名的属性. 2010年9月14日, ...
- JavaScript 词法句法
JavaScript 中的几个重要概念 JavaScript 遵循 ECMA-262 规范,目前其最新版是 ECMAScript 2018,而获得所有主流浏览器完全支持的则是 ECMAScript 5 ...
- 【译】Spring 4 自动装配、自动检测、组件扫描示例
前言 译文链接:http://websystique.com/spring/spring-auto-detection-autowire-component-scanning-example-with ...
- 将百度坐标转换的javascript api官方示例改写成传统的回调函数形式
改写前: 百度地图中坐标转换的JavaScript API示例官方示例如下: var points = [new BMap.Point(116.3786889372559,39.90762965106 ...
- javascript 词法结构小结
作为一名前端程序员,自然学习了一些框架,但是学的越多越发现自己基础的不足,于是想系统的学习一下js基础,然后把它记录下来. 如其他编程语言一样,词法结构是一门语言的基础,它规定了诸如如何给变量起名字. ...
随机推荐
- Eclipse如何安装JD-Eclipse反编译插件
一.Eclipse在线安装JD-Eclipse反编译插件 1.在eclipse的help—>Install New Software...中添加新软件开发,添加它的源: name:jd-ecli ...
- 常用awk命令(转)
awk 用法:awk ' pattern {action} ' 变量名 含义 ARGC 命令行变元个数 ARGV 命令行变元数组 FILENAME 当前输入文件名 FNR 当前文件中的记录号 FS 输 ...
- Windows8下PhoneGap 4 + Android Studio 1.0 + VS2013配置指南
1.准备工作 安装JDK1.6+,设置环境变量 JAVA_HOME C:\Program Files\Java\jdk1.5.0_07 CLASSPATH .;%JAVA_HOME%\lib Path ...
- 3D俄罗斯方块设计
发布在博客:http://blog.sina.com.cn/s/blog_d38e811c0101cpis.html
- 对于大学4年的反思(续),记我的ThoughtWorks面试
之前我写了一篇对于大学四年的反思,时隔一个月,为什么我这么快就要来写这篇续章呢?主要有两个原因,第一是感谢静子姐姐,记得知乎上有个回答里面说过人生需要有贵人的帮助,遇到贵人是一件很幸运的事情.我想,静 ...
- 【温故而知新-Javascript】使用 Document 对象
Document 对象时通往DOM功能的入口,它向你提供了当前文档的信息,以及一组可供探索.导航.搜索或操作结构与内容的功能. 我们通过全局变量document访问Document对象,它是浏览器为我 ...
- 【ASP.NET 类库】当你懒得用 Json+Ajax 时,可以试试 AjaxPro
一般我们在页面调用Ajax时,需要通过 js 调用ajax ,接着后台处理得到的数据,然后返回数据,ajax 得到返回的数据进行处理. 然而有没有可以像调用方法一样来进行Ajax操作呢? AjaxPr ...
- hdu-5904 LCIS(水题)
题目链接: LCIS Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total ...
- ckplayer播放器去掉右边的开关灯分享插件
在上一篇文章中介绍了如何使用ckplayer播放器,但是有的人不需要CK播放器右边的开关灯分享插件,那么就需要把该插件给去掉,方法也很简单. 第一步:先打开ckplayer.js里找到下面三行删除掉 ...
- 【mybatis】1、入门CURD基本操作(环境搭建)
#1.基本环境 环境 版本 jdk 1.7.0_10 ide eclipse-jee-luna-SR2-win32-x86_64 maven 3.3.3 mybatis 3.2.7 mysql 5.1 ...