class RDParser
Subject: ***Re: [SOLUTION] Dice Roller (#61)* From: *Dennis Ranke *<mail exoticorn.de> Date: Mon, 9 Jan 2006 06:53:10 +0900 References: 174521 </cgi-bin/scat.rb/ruby/ruby-talk/174521> 174811 </cgi-bin/scat.rb/ruby/ruby-talk/174811> In-reply-to: 174811 </cgi-bin/scat.rb/ruby/ruby-talk/174811>
Hi,
here is my second solution. Quite a bit longer, but a lot nicer. For this I implemented a simple recursive descent parser class that allows the tokens and the grammar to be defined in a very clean ruby syntax. I think I’d really like to see a production quality parser(generator) using something like this grammar format.
Constants
- LexToken
Attributes
pos[RW]
rules[R]
Public Class Methods
new(&block)
click to toggle source
# File lib/asciitracker/rdparser.rb, line 22 def initialize(&block) @lex_tokens = [] @rules = {} @start = nil instance_eval(&block) end
Public Instance Methods
expect(tok)
click to toggle source
# File lib/asciitracker/rdparser.rb, line 64 def expect(tok) t = next_token if @pos - 1 > @max_pos @max_pos = @pos - 1 @expected = [] end return t if tok === t @expected << tok if @max_pos == @pos - 1 && !@expected.include?(tok) return nil end
next_token()
click to toggle source
# File lib/asciitracker/rdparser.rb, line 59 def next_token @pos += 1 return @tokens[@pos - 1] end
parse(string)
click to toggle source
# File lib/asciitracker/rdparser.rb, line 29 def parse(string) @tokens = [] until string.empty? raise "unable to lex '#{string}" unless @lex_tokens.any? do |tok| #puts "match(#{string}) <- (#{tok})" match = tok.pattern.match(string) if match s_tok = match.to_s puts "(#{s_tok})" unless /^\s+$/.match(s_tok) #puts "<<< #{s_tok} | #{tok.pattern} >>>" @tokens << tok.block.call(s_tok) if tok.block string = match.post_match #puts "<<<#{s_tok}|||#{match.post_match}>>>" true else false end end end @pos = 0 @max_pos = 0 @expected = [] result = @start.parse if @pos != @tokens.size raise "Parse error. expected: '#{@expected.join(', ')}', found '#{@tokens[@max_pos]}'" end return result end
Private Instance Methods
match(*pattern, &block)
click to toggle source
# File lib/asciitracker/rdparser.rb, line 95 def match(*pattern, &block) @current_rule.add_match(pattern, block) end
rule(name) { || ... }
click to toggle source
# File lib/asciitracker/rdparser.rb, line 88 def rule(name) @current_rule = Rule.new(name, self) @rules[name] = @current_rule yield @current_rule = nil end
start(name, &block)
click to toggle source
# File lib/asciitracker/rdparser.rb, line 83 def start(name, &block) rule(name, &block) @start = @rules[name] end
token(pattern, &block)
click to toggle source
# File lib/asciitracker/rdparser.rb, line 79 def token(pattern, &block) @lex_tokens << LexToken.new(Regexp.new('\\A' + pattern.source), block) end