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行になってしまった.まぁ,これが動くからには大方のバグは取れたんじゃないかと思いたい.