[ann] rsec - parsec for ruby1.9

Hi dear everyone,

I just made a small parser combinator library for ruby1.9 (sadly it's
1.9 only).
It's simple(buggy) and fast(ha, it beats treetop on arithmetics!).
Please have a look if you are interested in.

install:
gem install rsec -s http://gemcutter.org

source code (about 500 LOCs):

the terrible document:

Yours, sincerely, and many best regards.

Helloworld: a simple arithmetic calculator

···

#---------------------------------------------------------

require 'rubygems'
require 'rsec'
include Rsec::Helpers

def arithmetic
  calculate = proc do |(p, *ps)|
    ps.each_slice(2).inject(p) do |left, (op, right)|
      left.send op.strip, right
    end
  end

  num = /[+-]?[1-9]\d*(\.\d+)?/.r.map &:to_f
  bra = '('.r.skip
  ket = ')'.r.skip
  paren = (bra << lazy{@expr} << ket).map &:first
  factor = paren | num
  term = factor.join(/\s*[\*\/]\s*/).map &calculate
  @expr = term.join(/\s*[\+\-]\s*/).map &calculate
end

puts arithmetic.parse '1 + 2.3 / 4 - 5 + 6 * 7.8 / (9 +10)'
--
Posted via http://www.ruby-forum.com/.

# Just added another example: a Scheme interpreter.

require "rsec"

class Scheme
  include Rsec::Helpers

  def initialize
    bra = /\(\s*/.r.skip
    ket = /\s*\)/.r.skip
    boolean = /\#[tf]/. r.map {|n| ValueNode[n=='#t'] }
    integer = /0|[1-9]\d*/.r.map {|n| ValueNode[n.to_i] }
    id = /[^\s\(\)\[\]]+/.r.on {|n|
                def n.eval bind, *xs
                  bind[self]
                end
              }
    atom = boolean | integer | id
    list = nil # declare for lazy
    cell = atom | lazy{list}
    list = ( bra < cell.join_(spacee) < ket ).map {|(n)|
ListNode[*n] }
    @parser = (spacee < cell.join_(spacee) < spacee).map {|(n)|
ListNode[*n] }
  end

  def run source
    @ctx = Rsec::ParseContext.new source, 'scm'
    res = @parser._parse @ctx
    if !res or !@ctx.eos?
      raise Rsec::ParseError['syntax error', @ctx]
    end
    res.eval Runtime.new
  end

  ValueNode = Struct.new :val
  class ValueNode
    def eval *xs
      val
    end
    def pretty_print q
      q.text "<#{val}>"
    end
  end

  class ListNode < Array
    def eval bind
      head, *tail = self
      case head
      when String
        pr = bind[head]
        pr.is_a?(Proc) ? pr[bind, tail] : pr
      when ListNode
        map{|n| n.eval bind }.last
      end
    end
  end

  class Bind < Hash
    def initialize parent = {}
      @parent = parent
    end

    def [] key
      super(key) || @parent[key]
    end

    # define a function
    def define id, &p
      self[id] = proc do |bind, xs|
        p[* xs.map{|x| x.eval bind }]
      end
    end
  end

  class Runtime < Bind
    def initialize
      super()

      self['define'] = proc do |bind, (param, body)|
        case param
        # (define (name plist[0]) body)
        when ListNode
          func, *xs = param
          self[func] = self['lambda'][bind, [xs, body]]
        # (define param body)
        when String
          self[param] = body.eval bind
        end
      end

      # declare:
      # (lambda (xs[0] xs[1]) body)
      self['lambda'] = proc do |bind_def, (xs, body)|
        xs = [xs] if xs.is_a?(String)
        new_bind = Bind.new bind_def
        # calling:
        # (some vs[0] vs[1])
        proc do |bind_call, vs|
          vs = vs.map{|v| v.eval bind_call}
          new_bind.merge! Hash[xs.zip vs]
          body.eval new_bind
        end
      end

      # lazy (short cut)
      self['if'] = proc do |bind, (p, left, right)|
        p.eval(bind) ? left.eval(bind) : right.eval(bind)
      end

      # misc
      %w|+ - * / ** % > <|.each do |s|
        define s, &s.to_sym
      end
      define '=', &:==
      define 'display' do |x|
        puts x
      end
    end
  end
end

s = Scheme.new
s.run File.read ARGV[0]

···

--
Posted via http://www.ruby-forum.com/.