论坛首页 Ruby版

rparsec

浏览 3433 次
锁定老贴子 主题:rparsec
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
时间:2006-10-05
正在ruby上写parsec,发现语法比java好到不能行。虽然还没有全部完成。
大家看看下面计算器的例子。(为什么不用eval?这个parsec的目的是general purpose的parser framework,要支持自己做dsl的,不是仅仅ruby parser。calculator只是一个测试。)

支持+,-,*,/,括号等

require 'src/functors'
require 'src/parsers'

class CalculatorTestCase < RUNIT::TestCase
  include Functors
  include Parsers
  def operator(op, val)
    str(op) >> value(val)
  end
  
  def calculator
    int = integer.map(&To_i)
    ops = OperatorTable.new.
      prefix(operator('-', Neg), 60).
      infixl(operator('+', Plus), 20).
      infixl(operator('-', Minus), 20).
      infixl(operator('*', Mul), 40).
      infixl(operator('/', Div), 40)
    expr = nil
    term = int | char('(') >> lazy{expr} << char(')')
    delim = whitespace.many_
    expr = delim >> Expressions.build(term, ops, delim)
  end
  def testCalculator
    assert_equal(5, calculator.parse('1 + 2 * (5 + -3)'))
  end
end
   
时间:2006-10-06
这个就看糊涂了,ajoo上仙啥时候给俺们讲解一下Haskell天书之parsec篇?
   
0 请登录后投票
时间:2006-10-06
稍微解释一下。

rparsec的基本操作都是在Parser上面。

char(?a)是一个Parser,它只parse一个单一的'a'字符,并且返回这个字符。

str('abc')是一个Parser,它parse一个'abc'字符串,并且返回这个字符串。

value(1)是一个Parser,它不管输入,直接返回1。

eof是一个Parser,它要求输入当前必须是空。

parser1 | parser2 是一个parser,它在parser1失败之后跑parser2。

parser1 >> parser2 是一个parser,它在parser1成功后,接着跑parser2。

parser1 << parser2 是一个parser。他也是顺序跑parser1和parser2,但是保留parser1的计算结果。

parser.many是一个parser,它相当于bnf里面的kleen star。就是说重复0..n次。把结果存在一个数祖里面。

parser.many_和many类似。不过它只保留最后一次的结果。

parser.map{...} 是一个parser,它先执行parser,产生的结果用一个block变换。


OperatorTable用来声明操作符的结合率和优先级。

Expression.build用来根据操作符的结合率和优先级,以及原子表达式,产生一个能够parse完整表达式的parser。

差不多了八?

scip的第二章有关于closure property的描述,这个parsec系统就有closure property:操作的参数是Parser,结果也是Parser。
   
0 请登录后投票
时间:2006-10-09
发布了:

http://jparsec.codehaus.org/Ruby+Parsec
   
0 请登录后投票
时间:2006-10-09
我比较喜欢这样的写法^_^:
class OperatorTable
  def OperatorTable.new
    o = allocate
    yield o
    o
  end

  ...
end

ops = OperatorTable.new do |o|
  o.prefix(operator('-', Neg), 60)
  o.infixl(operator('+', Plus), 20)
  o.infixl(operator('-', Minus), 20)
  o.infixl(operator('*', Mul), 40)
  o.infixl(operator('/', Div), 40)
end
   
0 请登录后投票
时间:2006-10-09
好主意!改好了。多谢。

不过,这样重复写"o.",真的更好么?
   
0 请登录后投票
时间:2006-10-09
还有,这样做和:
ops = OperatorTable.new
ops.prefix(operator('-', Neg), 60)
ops.infixl(operator('+', Plus), 20)
ops.infixl(operator('-', Minus), 20)
ops.infixl(operator('*', Mul), 40)
ops.infixl(operator('/', Div), 40)

相比有什么好处?
   
0 请登录后投票
时间:2006-10-10
一般block方式封装的话是为了在block之前之后插点什么逻辑进去,比如exception处理啦。。。否则就无所谓,对象方式也很直观。原来的串串烧(decorator)模式串的东西小点写在一行还好,串好几个大蹄膀分成多行就难看了点。还有种省点括号更函数化点的风格:

# '<<' => prefix, '<|' => infixl
ops = OperatorTable.new
ops << operator('-', Neg), 60
ops <| operator('+', Plus), 20
ops <| operator('-', Minus), 20
ops <| operator('*', Mul), 40
ops <| operator('/', Div), 40
   
0 请登录后投票
时间:2006-10-10
<|是一个合法运算符么?


有一点觉得不爽.象SomeClass.new {|this|...}
这种用法,为什么需要我自己写def new才行?为什么ruby不直接支持这个idiom?
   
0 请登录后投票
时间:2006-10-10
失败orz, yy代码的结果,自挂东南枝去。。。
改用<=巴

不支持大概是因为这个idiom还不够通用,也不明显节省代码。
   
0 请登录后投票
论坛首页 Ruby版

跳转论坛:
JavaEye推荐