parser generator書いてみた(3)

度重なる仕様変更でこんな感じに書くようになった.

Lexer = ParserGenerator::Lexer.new(/[ \t\n\r\v]+/) {
  token(:operator, /[+-]/) {|ma| ma[0].to_sym }
  token(:operator2, /[*\/]/) {|ma| ma[0].to_sym }
  token(:integer, /[0-9]+/) {|ma| ma[0].to_i }
  token(:identifier, /[a-z]+/) {|ma| ma[0].to_sym }
}

Parser = ParserGenerator::Parser.new(Lexer) {
  rule(:expr, seq(:term, repeat(:operator, :term))) {|term, ary|
    ary.inject(term) {|r, (op, x)| r.__send__(op.content, x) }
  }
  rule(:term, seq(:factor, repeat(:operator2, :factor))) {|factor, ary|
    ary.inject(factor) {|r, (op, x)| r.__send__(op.content, x) }
  }
  rule(:factor, seq(opt(:operator), select(:integer, :identifier))) {|sign, token|
    case token.type
    when :integer
      token.content
    when :identifier
      10
    end * if sign && sign.content == :-
            -1
          else
            1
          end
  }
}

parser = Parser.new

p parser.parse("x / 2 + -4 * 5", :expr) # => -15

いきなりparserオブジェクトを生成するのではなく,parserクラスを生成するようにした.lexerも同じく.parserにlexerクラスを渡すのはどちらのタイミングでもいい.parserクラス生成時に渡したlexerクラスは,そのparserクラスのデフォルトのlexerになる.