ruby language parser in ruby

C

Caleb Clausen

A minor problem with the redparse gem is that it gives most of the files
root-only permissions. I just did 'sudo gem install redparse'

Yes, that is indeed most unfortunate and due to be fixed in the next
release, which is coming Real Soon Now. I'm not sure how those
permissions got all weird (again); it's not something I usually give a
lot of thought or attention to, I'm afraid. (Frankly, I wish rubygems
would have warned me when I created the gem that some of the files
were not world-readable.)
Looking at this code - I don't think I would dare hack it. I think
ruby_parser is more what I was looking for.

Would you care to elaborate on that? What didn't you like and/or find
hard to understand? What could I do better? Actually, redparse is a
fairly normal LALR-based parser, the code divides internally into
definitions, rules, and actions. I did invent my own LR language, tho,
being as I'm so unhappy with yacc and friends.

I'm interested to hear, to tell the truth, what sorts of things you
want to change in the existing ruby grammar; I'm very much like
playing around with extending the language in various ways. I have a
variety of ideas I want to pursue myself, but I'm always interested to
hear what new features other people might want.
 
B

Brian Candler

Caleb said:
Yes, that is indeed most unfortunate and due to be fixed in the next
release, which is coming Real Soon Now. I'm not sure how those
permissions got all weird (again); it's not something I usually give a
lot of thought or attention to, I'm afraid. (Frankly, I wish rubygems
would have warned me when I created the gem that some of the files
were not world-readable.)


Would you care to elaborate on that? What didn't you like and/or find
hard to understand?

Well, all of it really.

I presume that lib/redparse.rb is the "real" parser (I also found
lib/redparse/babyparser.rb and babynodes.rb)

It's a monolithic file, and it was hard even to see where the grammar
began. I believe it's here:

[
-[UNOP, Expr, lower_op]>>UnOpNode,
-[DEFOP, ParenedNode]>>UnOpNode,
-[Op(/^(?:unary|lhs|rhs)\*$/), ValueNode, lower_op]>>UnaryStarNode,
... etc

and an example of a larger rule is like this:

-[NumberToken&-{:negative=>true}, Op('**').la]>>
stack_monkey("fix_neg_exp",2,Op("-@",true)){|stack|
#neg_op.unary=true
num=stack[-2]
op=OperatorToken.new("-@",num.offset)
# op.startline=num.startline
stack[-2,0]=op
num.ident.sub!(/\A-/,'')
num.offset+=1
},

I have no idea how to (a) understand, or (b) modify that. I can see
there is quite a lot of Ruby operator abuse going on, but without
defined semantics.

At least with racc, it's extremely well documented, in the sense that I
have a printout of the yacc manual to refer to.

So no doubt RedParse is a fine ruby parser, and generates a fine object
tree as its output. But it's not so good for me as a starting point for
building languages which inherit some of ruby flavour, but are
significantly different.
 
C

Caleb Clausen

Well, all of it really.

I presume that lib/redparse.rb is the "real" parser (I also found
lib/redparse/babyparser.rb and babynodes.rb)

Yes, that's right. babyparser.rb is an example of a minimal (3 rule)
parser using this system, and can make a good place to start trying to
understand the full-blown parser.
It's a monolithic file, and it was hard even to see where the grammar
began. I believe it's here:

Unfortunately, there's a lot of experimental stuff which shouldn't be
in there, related to my attempt to write a proper parser compiler.
[
-[UNOP, Expr, lower_op]>>UnOpNode,
-[DEFOP, ParenedNode]>>UnOpNode,
-[Op(/^(?:unary|lhs|rhs)\*$/), ValueNode, lower_op]>>UnaryStarNode,
... etc

These are indeed the rules. The general form is of a rule is
-[ patterns to search for on the top of parse stack ] >>
NodeTypeTheyGetReplacedWith,


These 3 rules deal respectively with (most) unary operators, the
defined? operator, and unary star operators. (What's found to the
right of the >> is generally a pretty good clue as to what a
particular rule is doing. (But not always in the case of stack
monkeys...)) UNOP, DEFOP, Expr, lower_op and the like are defined
above in the definitions section.

Altho the action to take on finding a pattern on the parse stack is
not always to reduce the matched portion of the stack into a Node. For
instance, here:
and an example of a larger rule is like this:

-[NumberToken&-{:negative=>true}, Op('**').la]>>
stack_monkey("fix_neg_exp",2,Op("-@",true)){|stack|
#neg_op.unary=true
num=stack[-2]
op=OperatorToken.new("-@",num.offset)
# op.startline=num.startline
stack[-2,0]=op
num.ident.sub!(/\A-/,'')
num.offset+=1
},

That's not really what I'd call a larger rule, merely (alas) a longer
one... This is an example of one of many relatively unimportant rules
with which the parser must unfortunately be littered. This particular
example fixes up the precedence of expressions like -2**10. '-2' is
normally lexed as one single numeric token, as is normal in most
languages. In this one special case, however, the -@ must actually be
made lower precedence than **. The implementation of this fixup can't
be neatly shoehorned into a Node constructor, however, so special
imperative code (a 'stack monkey') had to be written to fiddle with
the parse stack directly.
I have no idea how to (a) understand, or (b) modify that. I can see
there is quite a lot of Ruby operator abuse going on, but without
defined semantics.

I would call that a 'DSL'. Most of the special operators and other
unusual syntax are defined in my pattern matching language, Reg, which
is a different project. Reg is moderately well documented, but that's
in a whole other directory.
At least with racc, it's extremely well documented, in the sense that I
have a printout of the yacc manual to refer to.

I just couldn't ever get yacc to do what I wanted it to do,
personally. Lots of other people have had more luck....

I would like some day (if I ever have time) to split out the parser
construction tool aspects or redparse from the actual ruby parser
itself, and package and document the parser compiler/interpreter
better. For now, altho I have made an effort to make the interface to
RedParse fairly clear and well described, the internals I simply
didn't even try to explain....
So no doubt RedParse is a fine ruby parser, and generates a fine object
tree as its output. But it's not so good for me as a starting point for
building languages which inherit some of ruby flavour, but are
significantly different.

I can explain more IF you're interested, but it does seem like you
know where you want to go right now.

I'd still like to hear more about this language(s) you're trying to
make, if you want to tell.
 
B

Brian Candler

Caleb said:
This particular
example fixes up the precedence of expressions like -2**10. '-2' is
normally lexed as one single numeric token, as is normal in most
languages. In this one special case, however, the -@ must actually be
made lower precedence than **

Ugh. No doubt that sort of thing will bite me.
I'd still like to hear more about this language(s) you're trying to
make, if you want to tell.

I'm going to try to implement a 'ruby flavoured erlang', a front-end
which spits out regular erlang which is compiled in the normal way, to
see what such a language might look like.
 
B

Brian Candler

Ryan said:
I think your best bet at this time is ruby_parser.

Question for you: as far as I can make out, the handling of embeeded
linebreaks such as

a = b +
c

is handled via state kept in the lexer. Can I ask why you did it this
way? I am thinking it ought to be possible to do this in the grammar,
e.g.

expr: expr '+' opt_nl expr
| expr '-' opt_nl expr

opt_nl:
| nl

but if you've found out the hard way that it isn't, it could save me
following a dead end.

Also: can you summarise how you're handling expressions nested within
string literals, e.g. "abc #{foo} def" ?

Thanks,

Brian.
 
R

Ryan Davis

=20
Question for you: as far as I can make out, the handling of embeeded=20=
linebreaks such as
=20
a =3D b +
c
=20
is handled via state kept in the lexer. Can I ask why you did it this=20=

My default answer to nearly any of these types of questions is going to =
be "because that's how MRI does it".

In this case, yes, the lexer and parser are keeping a shared variable =
called lex_state that knows whether it is in the middle of an expression =
(and many other states) so here the trailing '+' keeps the expression =
open.

lex_state needs to die. It basically only exists because the language is =
a tangled mess and it was designed with LR parsing in mind (AFAICT, =
lex_state is a symptom of not really knowing where you are contextually, =
because you're parsing bottom up). I'd like to not have lex_state and =
many of the complications that come with it. I'm not sure if it'll still =
be ruby at that point, but I'm giving it a go to see.
I am thinking it ought to be possible to do this in the grammar,=20
e.g.
=20
expr: expr '+' opt_nl expr
| expr '-' opt_nl expr
=20
opt_nl:
| nl

you're going to have those EVERYWHERE... but yeah, it should be =
possible. Write good tests from the beginning.
Also: can you summarise how you're handling expressions nested within=20=
string literals, e.g. "abc #{foo} def" ?


There is no way to summarize that. It is horrible and I hate it, but I'm =
not in a position to make it work better with the current architecture.=
 

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

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top