Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

Jparsec is now ported to Ruby, with a way lighter syntax than Java.

Calculator demo

The following is the ruby version of calculator:

Code Block
require 'rparsec'
include RParsec
class Calculator
  include Parsers
  include Functors
  def parser
    ops = OperatorTable.new.
      infixl(char(?+) >> Plus, 20).
      infixl(char(?-) >> Minus, 20).
      infixl(char(?*) >> Mul, 40).
      infixl(char(?/) >> Div, 40).
      prefix(char(?-) >> Neg, 60)
    expr = nil
    term = integer.map(&To_i) | char('(') >> lazy{expr} << char (')')
    delim = whitespace.many_
    expr = delim >> Expressions.build(term, ops, delim)
  end
end

Calculator.new.parser.parse '1+2*(3-1)' # => 5

As simple as that!

S-expression demo

The above example utilizes the pre-built Expressions class to help building expression parser. Another example is for a simpler s-expression parser (in lisp syntax, "- (+ 1 (* 2 2)) (1)" sort of thing). As s-expression is way simpler to parse than expressions with infix operators, we will build the parser without using Expressions class:

Code Block
include RParsec
class SExpressionTestCase < RUNIT::TestCase
  include Parsers
  include Functors
  def delim
    whitespace.many_
  end
  def ignore parser
    parser << delim
  end
  def lparen
    ignore(char('('))
  end
  def rparen
    ignore(char(')'))
  end
  def parser
    expr = nil
    lazy_expr = lazy{expr}
    term = number.map(&To_f) | lparen >> lazy_expr << rparen
    binop = char('+') >> Plus | char('-') >> Minus | char('*') >> Mul | char('/') >> Div
    binop = ignore binop
    term = ignore term
    binary = sequence(binop, lazy_expr, lazy_expr) do |op, e1, e2|
      op.call(e1, e2)
    end
    expr = delim >> (term | binary)
  end
  def test1
    assert_equal(4, parser.parse('- + 1 (* 2 2.0) (1)'))
  end
end

The code is pretty self-explanatory. The only thing to note is that we had to manually skip whitespaces using the "ignore" function. (Whitespace skipping can be more elegant if we do a 2-phase parsing, where lexer takes care of them before grammar rule kicks in. Such use is demonstrated in detail in the test code.)

More examples

Take a look at the SQL Parser for a more sophisticated parser, where more traditional lex/parse approach is used.

Installing rparsec

Code Block
gem install rparsec

rparsec can be downloaded at http://rubyforge.org/frs/?group_id=2326

Rdoc is available online.


Children Display
depth4

Created by benyu benyu
On Sun Oct 08 14:53:56 CDT 2006
Using TimTam