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