Loading...

    AD: 猛买 | 快递查询 | Jobsdigg | 很棒的男装店

用Racc编写解释器

可任意转载,但必须在醒目位置以超链接形式标明文章原始出处和作者信息
原文地址:http://www.blogkid.net/archives/2698.html

在Twitter上看到盛大举办的校园牛人大赛,其中有“云计算脚本比赛”。看了下题目,开始悔恨大学里没好好学编译原理。但又手痒,就想用Racc试试。做题过程居然很顺利,从学习Racc到做完前三道题花了两三个小时,于是写一点有关Racc的示例,算是学习笔记。

以“云计算脚本比赛”第一题为例,要求执行如下脚本后,得到“Hello, 老赵”。

SET name = "老赵"
RETURN "Hello, " + name

简单来说,用Racc写成解释器,可以把上面的脚本,转化为Ruby代码并执行。

安装Racc:
gem install racc

创建一个level_1.y文件,定义一个JeffParser类。

class JeffParser
token T_SET T_RETURN T_STRING T_VAR
rule
  lines:
    | lines T_SET T_VAR '=' T_STRING {@local_vars[val[2]] = val[4] }
    | lines T_RETURN exp {result = val[2]}
  exp: exp '+' exp {result = val[0] + val[2] }
    | T_STRING
    | T_VAR {result = @local_vars[val[0]]}
end

先定义几个token。因为语法比较简单,只有 SET/RETURN/变量名/字符串四种。

在rule部分,格式是 token: token token … {action} 。中括号中的action 部分就是Ruby代码了,调试时可以把一些需要的变量打出来。val变量是由左边token的值构成的数组,0表示第一个token的值,依此类推。

之后是词法分析的部分。做的事儿是用正则表达式,把上面定义的4种token识别出来,并放到 @tokens 数组中。

---- header
require 'strscan'
---- inner
 def parse(str)
   @local_vars = {};
   @tokens = []
   s = StringScanner.new(str)
   until s.eos?
     case
     when s.scan(/SET/)
       @tokens << [:T_SET, s[0]]
     when s.scan(/RETURN/)
       @tokens << [:T_RETURN, s[0]]
     when s.scan(/".+"/)
       @tokens << [:T_STRING, eval(s[0])]
     when s.scan(/[a-z0-9]+/)
       @tokens << [:T_VAR, s[0]]
     when s.skip(/[ \t\r\n]/)
     else
       @tokens << [s.getch, nil]
     end
   end
   do_parse()
 end

 def next_token
   @tokens.shift
 end

保存后运行 `racc level_1.y ` 将生成一个名为level_1.tab.rb的文件,这就是我们想要的解释器了。

简单测试(假设脚本存放在level_1.file文件中):

ruby -r level_1.tab.rb -e \
  'print JeffParser.new.parse(open("level_1.file").read())'

输出:

Hello, 老赵

当然,题目还只做了一半。题目要求:需要用http访问并得到结果。可以在前面挂一个Webrick来做到,就和Racc没关系了。

后面几道题,也可以如法泡制。

–广告时间–

赵姐夫(@jeffz_cn)说这次的题目是他经手的,后悔没把blog的链接放上去,于是我帮他打个广告,欢迎大家猛点此处

3 Responses to “用Racc编写解释器”


  1. Ruby的Parser Generator,建议试试Treetop,支持PEG,用起来很容易。 http://treetop.rubyforge.org/

  2. @liancheng
    多谢兄弟

  3. 如果修改成,需要从浏览器中访问,就更有趣了。可以做个SOCKET程序监视某个端口,然后解析Http Request Message,调用你的那个解析器,返回Response Message.

Leave a Reply