# \[ann\] rsec - parsec for ruby1.9

**URL:** <https://www.ruby-forum.com/t/ann-rsec-parsec-for-ruby1-9/183471>\
**Category:** Ruby\
**Created:** [February 15, 2010, 9:17am UTC](https://www.ruby-forum.com/t/ann-rsec-parsec-for-ruby1-9/183471 "2010-02-15T09:17:07Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![night\_stalker](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/f0a364/32.png) [@night\_stalker](https://www.ruby-forum.com/u/night_stalker)\
**Post date:** [February 15, 2010, 9:17am UTC](https://www.ruby-forum.com/t/ann-rsec-parsec-for-ruby1-9/183471/1 "2010-02-15T09:17:07Z")

</div>

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](http://gemcutter.org)

source code (about 500 LOCs):

> **[GitHub - luikore/rsec: Parser / Regexp Combinator For Ruby](https://github.com/luikore/rsec)**
>
> Parser / Regexp Combinator For Ruby. Contribute to luikore/rsec development by creating an account on GitHub.

the terrible document:  
[http://wiki.github.com/luikore/rsec/](http://wiki.github.com/luikore/rsec/)

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)’

---

<div class="post-metadata">

**Author:** ![night\_stalker](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/f0a364/32.png) [@night\_stalker](https://www.ruby-forum.com/u/night_stalker)\
**Post date:** [February 16, 2010, 10:31am UTC](https://www.ruby-forum.com/t/ann-rsec-parsec-for-ruby1-9/183471/2 "2010-02-16T10:31:26Z")

</div>

# 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]
