parser generator書いてみた(4)

構文規則(の左辺)を文字列リテラルでも書けるようにした.

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, "term (operator term)*") {|term, ary|
    ary.inject(term) {|r, (op, x)| r.__send__(op.content, x) }
  }
  rule(:term, "factor (operator2 factor)*") {|factor, ary|
    ary.inject(factor) {|r, (op, x)| r.__send__(op.content, x) }
  }
  rule(:factor, "operator? (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は自分自身で書かれている.と言っても,こちらでは文字列での構文指定はできないけど.

SyntaxRuleLexer = ParserGenerator::Lexer.new(/[ \t\n\r\v]+/) {
  token(:left_paren, /\(/) {|ma| ma[0].to_sym }
  token(:right_paren, /\)/) {|ma| ma[0].to_sym }
  token(:operator, /[*?]/) {|ma| ma[0].to_sym }
  token(:operator2, /[|]/) {|ma| ma[0].to_sym }
  token(:identifier, /[-_<>a-zA-Z0-9]+/) {|ma| ma[0].to_sym }
}

SyntaxRuleParser = ParserGenerator::Parser.new(SyntaxRuleLexer) {
  rule(:rule, seq(:expr, repeat(:operator2, :expr))) {|expr, exprs|
    if exprs.empty?
      expr
    else
      select(expr, *exprs.map {|e| e[1] })
    end
  }

  rule(:expr, seq(:term, repeat(:term))) {|expr, exprs|
    if exprs.empty?
      expr
    else
      seq(expr, *exprs)
    end
  }

  rule(:term, seq(:factor, opt(:operator))) {|factor, op|
    if op
      case op.content
      when :*
        repeat(factor)
      else
        opt(factor)
      end
    else
      factor
    end
  }

  rule(:factor, select(:identifier, seq(:left_paren, :rule, :right_paren))) {|x|
    case x
    when ParserGenerator::Lexer::Token
      x.content
    when Array
      x[1]
    end
  }
}

あんまりいろんな構文でテストしてなかったもんで,ぼろぼろとバグが出てきて大変だった.メソッド呼び出しが深くてデバッグしにくいし.勢いだけで書くとこうなる.最初は200行ちょいだったソースも今や500行になってしまった.まぁ,これが動くからには大方のバグは取れたんじゃないかと思いたい.