Ruby Parser Combinator

S

Scott Weeks

Hello all,

this is my first post on the ruby-talk list. I've been using ruby for a
while though :)

Anyway, just horsing around I tried my hand at writing a ruby parser
combinator. It (along with an explanation) is at

http://twelvestone.com/forum_thread/view/29249

(the explanation and link to the gem and source tar are most of the way
down the page (the post with the catapult in it)).

Anyway I'm still a bit of a noob when it comes to fp and parsing stuffs
so if anyone wants to correct me I'd be grateful for the help :)

Cheers,
Scott
 
S

Scott Weeks

Minor correction your example calculator has a prefix syntax, not an
infix syntax. Otherwise I likes dem der combinators. and cor lets you
do scanner forking, which is always fun. Neat.


ha! thanks for the correction, I have a tendency to do stuff like that.
I'll update the examples/code :)

Cheers
 
E

Eric Mahurin

Hello all,

this is my first post on the ruby-talk list. I've been using ruby for a
while though :)

Anyway, just horsing around I tried my hand at writing a ruby parser
combinator. It (along with an explanation) is at

http://twelvestone.com/forum_thread/view/29249

(the explanation and link to the gem and source tar are most of the way
down the page (the post with the catapult in it)).

Anyway I'm still a bit of a noob when it comes to fp and parsing stuffs
so if anyone wants to correct me I'd be grateful for the help :)

Cheers,
Scott

Take a look here:

http://rubyforge.org/projects/grammar/

I think this is the same concept that you are talking about. I just
took it to the next level.

Sorry I haven't been working on this for a while. I just haven't had
time with a new job where I'm working too much. I have lots of
updates, but haven't had time to get back to it.
 
S

Scott Weeks


Nice! It's a bit different since it seems to be a classic style parser
library and not quite in the same vein but still very cool. I
especially like how you overrode | and + to make expressing production
rules more natural

...

Hey Christian, I couldn't find the code for your parser combinator in
that blog entry, could you post a link? I'd love to look at your
implementation because it seems like you've done it in a more elegant
way (at least from the examples you gave).

Cheers,
Scott
 
S

Scott Weeks

Cool! Thanks for that, I love reading other people's code :)

I'm really more interested in this as an exercise to get a better
understanding of functional programming techniques by seeing how they
could/can be implemented in Ruby. I love this sort of stuff.
 
E

Eric Mahurin

Nice! It's a bit different since it seems to be a classic style parser
library and not quite in the same vein but still very cool. I
especially like how you overrode | and + to make expressing production
rules more natural

I think what I did is closer to what your blog talks about than
classic parsers. Basically, you start with leaf "Grammar" objects and
build complex Grammar productions by combining Grammars.

If want to see the very basics of this technique, here is a stripped
down version of it with a calculator example (self contained and
executable):

class SimpleGrammar
def initialize(grammar)
@grammar =3D grammar
end
def scan(cursor,buffer)
@grammar.scan(cursor,buffer)
end
def |(other); Code.new { |cursor,buffer|
scan(cursor,buffer) || other.scan(cursor,buffer)
} end
def +(other); Code.new { |cursor,buffer|
scan(cursor,buffer) && other.scan(cursor,buffer)
} end
def filter(buf0,&block); Code.new { |cursor,buffer|
buf =3D buf0.clone
scan(cursor,buf) && buffer.concat(block[buf])
} end
def discard; Code.new { |cursor,buffer|
scan(cursor,[])
} end
class Code < SimpleGrammar
def initialize(&block)
@block =3D block
end
def scan(cursor,buffer)
@block[cursor,buffer]
end
end
class Recurse < SimpleGrammar
def initialize(&block)
@grammar =3D block[self]
end
def scan(cursor,buffer)
@grammar.scan(cursor,buffer)
end
end
class Element < SimpleGrammar
def initialize(pattern)
@pattern =3D pattern
end
def scan(cursor,buffer)
c =3D cursor.read1after
if @pattern=3D=3D=3Dc
buffer << c
cursor.skip1next
true
end
end
end
# Make methods for our classes that call new for us
constants.each { |klass|
eval("
def #{klass}(*args,&block)
#{klass}.new(*args,&block)
end
def self.#{klass}(*args,&block)
#{klass}.new(*args,&block)
end
")
}
NULL =3D Code.new { true }
end

class IO
# implement just the methods we need to look like a cursor
def read1after;c=3Dgetc;ungetc(c);c;end
def skip1next;getc&&true;end
end

class Expression < SimpleGrammar::Recurse
def initialize; super() { |expr|
digit =3D Element(?0..?9)
int =3D Recurse { |int| digit+(int|NULL) }
number =3D
(int + (
Element(?.)+int |
NULL
)).filter("") { |n| [n.to_f] }
primary =3D Recurse { |primary|
number |
Element(?-).discard + primary + Code { |_,b| b[-1]=3D-b[-1] } |
Element(?().discard + expr + Element(?)).discard
}
product =3D Recurse { |product|
primary + (
Element(?*).discard + product + Code { |_,b|
b[-2]*=3Db[-1];b.pop } |
Element(?/).discard + product + Code { |_,b|
b[-2]/=3Db[-1];b.pop } |
NULL
)
}
sum =3D Recurse { |sum|
product + (
Element(?+).discard + sum + Code { |_,b| b[-2]+=3Db[-1];b.p=
op } |
Element(?-).discard + sum + Code { |_,b| b[-2]-=3Db[-1];b.p=
op } |
NULL
)
}
} end
end

Expression.new.scan(STDIN,buf=3D[]) && p(buf[0])


In the grammar package on rubyforge, the API is very similar to above,
but I added a bunch of stuff to improve performance, usability, and
features. Notice in the above, I'm not really tied to using a
character stream (IO). You could be parsing a token stream or
whatever. You just need to implement a subset of the Cursor (another
package of mine) API. This way you can build lexers, parsers,
preprocessors, etc in the same way.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top