动态语言大行其道 ,不过还有不少公司在用着java 。 很大部分人也会java 。
所以用java 来开发自己的一个动态语言 不错 。反正总是要东西打发内心的无聊, 治疗枯燥的工作对人心理的创伤。
搞这么大个题目 ,招来不少人 。高手也会来吧? 本人编译原理原来上学就没学好 ,这也算是初学乍练 ,您看得高兴捧个人场 ,看得不高兴 多多指教 ,以免我误导了群众。
首先 我必须承认我这话说大了。 我目前只是写到了 词法分析器 和 一个表达式计算。距离动态语言 还差的远。 我也很怀疑我有时间真的写完这些 。今天有空就写点 。 本文的宗旨 就是简单。主要是复杂的东东 我也说不出来 。
编译原理 上过这课的都知道 很无聊。 工作两年基本忘掉 残留下来的概念看到了热泪盈眶的 想不起来。 咱就再回忆一遍吧。
第一个 DFA 确定状态机。
先不说这是啥。 看一个例子, 为了说明状态机的作用和能力 得绕点路先撇开编译原理 抛开概念看看怎么一步步发现这个DFA。 如很解析xml html?
这是我在做一个手机浏览器的时候遇到的问题,虽然有很多库,但是手机上没有或者有不适合,于是自己搞一个 。这里就不争论 “轮子” 的问题了。
<p> xxxxx </p>
这是典型的一段xml 。 注意看, 主要分两部分 ‘<’ 和 ‘>’ 括住的部分,和之外的东西 也就是 ‘>’ 和 ‘<’ 括住的部分。 其实就是把tag 和 内容 区分开。分出如下:
p xxxxx /p 3块
于是写代码如下
state = 初始状态
for char in htmlstr{
if 初始状态{
if(char == '<'){
state = tag状态
}
}else if tag状态{
if(char == '>'){
state = 内容状态
}else{
tagstr += char;
}
}else if 内容状态{
if(char == '<'){
state = tag状态
}else{
contentstr += char;
}
}
呵呵 第一步 把tag 和 内容分开了 。 有同学问 < > 不会乱出现么? 规则上规定xml 中< >必须是tag 的 遇到和要转义 就是用 < 这种东东代表一下。 但是也会遇到不转义的写错的 这个只要加上写判断 容错即可 这里不做详述。
tag 里边还有东东啊
<img src="url" alt="xxxx">
同理可以分状态 处理之。 找到tag名字img之前叫做状态1 ,然后遇到空格 变为状态2, 然后遇到=号变为状态3 ..... 当然这里的逻辑比上边复杂一些 ,其实也就是又臭又长些 。
上边说的就是一个 原始的用 if else 构成的状态机。 状态机 就是根据输入 改变到不同的状态 然后做不同的处理 。简单的通俗的说 状态机 == 装逼的switch
这里
http://code.google.com/p/tagparser/
有源码 python 和 java 的 。写的都比较乱 凑合着看吧 ,不大 也就是200行左右解析html xml 之流。
python 版本的还有一个抓取tianya 论坛的一个例子 不知道现在还能用吗 很长时间了
今天就到这吧 。
明天继续....
接昨天
if else 或者 switch 构成的 状态机 非常简陋。 状态越来越多的时候变得乱七八糟 。 所以要分解包装一下。
状态机行为就是 :
----输入---->switch----改变状态--->
在游戏编程 Ai设计中经常用到 ,不过我们这里只主要是谈编译 ,所以输入就变成了 一个字符 输出为一个状态。 一直说“状态” 这个东西 那就把它搞成一个类吧
- class State{
- StateType type = StateType.BEGIN;
- public State nextState(char i){
- return null;
- }
nextState 就是主要部分了 输入为一个字符串 输出也就是返回值 为一个State。 看看如何用这个State来识别一个 整型数字 例如 1000 100 99 .....
建立一个IntState 然后重载 State:
- class IntState extends State{
- public State nextState(char c){
- if(c >= '0' && c <= '9'){
- return this;
- }else{
- return null;
- }
- }
- }
这个比较简单 可以生成一个 IntSate intState = new IntState() 看看它如何工作
- String prostr = "192321 2312 3243";
- String buff = "";
- for(int i=0 ; i<prostr.length() ; i++){
- char c = prostr.charAt(i);
- State st = intStat.nextSate(c);
- buff += c;
- if(st == null){
- print buff;
- }
其结果就是把字符串中的整型数字给 挑出来了。 这看起来很容易不是么? 等等如果它没有split 强大费这种力气干么? 实际上程序文件包含的字符串比这复杂的多,有int float string 变量 关键字 各种符号.... ....................木有关系 统统没有关系 每一个建立一个state即可
如 floatState stringState 看看如何编写 ,这里只写出nextState 函数 它们都是继承自State类
- floatSate:
- public State nextState(char i){
- if(isDigit(i)){
- return floatSt;
- }else if(i == '.' || isAlpha(i)){ // 浮点数状态下 输入“.” 和字符 返回deny状态 说明词法错误,这里也可以做成抛异常
- return deny;
- }else{
- return null; //如遇到空格 或者其他的字符 则说明词法完成 顺利通过
- }
- spaceState 识别空白的字符
- public State nextState(char i){
- if(isSpace(i)){
- return spaceSt;
- }else{
- return null;
- }
常用的函数
- public static boolean isAlpha(char i){
- return (i>='a' && i<='z') || (i>='A' && i<='Z');
- }
- public static boolean isDigit(char i){
- return (i>='0' && i<='9') ;
- }
- public static boolean isSpace(char i){
- return (i==' ' || i=='\r' || i=='\n' || i=='\t') ;
- }
识别 单个的符号 更简单了 直接用State 生成一个不用继承,然后把它放入到一个字典里 在扫描程序字符串的时候可以快速查到。如下
- map.put("(", new State(StateType.LPARENT));
这里还需要一个开始的状态 ,编译开始时 从一个字符开始扫描 。第一个字符有可能是 整型数字 也可能是字母 也可能 变量。 可以如下编写
- public State nextState(char i){
- if(isAlpha(i) || i == '_'){ //如第一个字符遇见字母 ,那么接下来就返回 identState ,它用来识别变量和关键字这类的。
- return identSt;
- }else if(isDigit(i)){ //如是数字 看开头是0打头的 有可能是0x 也可是8位的 ,如果不是0 那就是整型的。当然整型半截可能遇见“.”
- if(i == '0'){ //在整型状态下遇见"." 就返回floatState 当作float识别。 这里用了子状态 。其实可以多生成些状态不要子
- intSt.subtype = 8160; //状态
- }else{
- intSt.subtype = 10;
- }
- return intSt;
- }else if(i == '\"'){ // 引号出现说明后边是 字符串
- return strSt;
- }else if(isSpace(i)){
- return spaceSt;
- }else { // 如果是其他符号 看看是不是单个的 {} []: ,. 之类的
- return smap.get(String.valueOf(i));
- }
- }
一旦一个状态识别完 就会返回null 说明一个词义被确认 而且这个词是整型 还是浮点型 还是字符串 ... 都是最后的那个状态类型。 这时候可以根据类型 把字符串转化为java 的类型了。
其中遇到的一些 小问题 。如 第一个字符是 0 ,就有几种可能 十六进制0x7873 浮点数0.423 八进制0632,如何确定 用intSate 还是用floatState ?
这里我程序里是假定是整型先 如遇到“.” 状态机返回floatSate这样就跳到了浮点型识别, 在区分 八进制还是十六进制的时候 用了子状态 。其实这么做不好。最好引入中间状态 如叫做 zeroState .在这个状态里就可以区分到底是用那个状态机去识别。 因为再往后一个字符就可以确定是什么类型 如果是‘x’就是十六位整型。
这种情况很常见,就是一个字符出现不能确定状态该怎么走 ,方法就是引入中间状态。 最后实现了 每个状态输入一个字符 就可以确定下一个状态,这也是为什么叫做有限确定状态机 DFA 。一个输入可能对应多个状态的叫做非确定状态机 NFA 。很恶心的书上把简单的概念都写得抽象的吓人,真不知道为啥?为了严谨? 写书给人看的 搞的跟给机器写书一样 。 隐隐约约记得书上像些咒语一样 写着如何把NFA 转为DFA ..... 压根我看不明白。
状态机的应用还很多 。其实正则就是状态机实现的 。有的是用NFA 有的是用DFA规则。 表现的区别 如匹配 (aaa|bbb|cdd) 。
- 字符串 : abcd
- DFA 过程: 先看和 aaa 匹配么再和bbb匹配 在和ccc匹配
- NFA过程: 先看abcd的第一个字符 和 aaa bbb ccc 那个匹配 。然后在看第二个字符b ...
- 有点类似一个深度优先 一个是广度优先。
今天到这里 待续
大家过年好,春节快乐。
给出词法分析的代码