Scalaでパーサーを作ってみる〜1:構文定義

Scalaの勉強をはじめました。
で、なんかパーサーコンビネータというのがあるらしく、パーサーが簡単に作れるらしく、じゃあパーサー作ってみるのがScalaの勉強にいいんじゃないかということで、簡単なパーサーを作ってみることにします。


とりあえず構文定義してみます。
今回つくるのは、整数の計算をするパーサーってことにします。整数と、+/-、*、あと()くらいが使えるものにします。割り算を入れないのは、そこは単純に処理を付け加えれば実現できるから。
式の優先順位と、同じ優先度に複数の演算子があればいいってことにします。あと、単項演算子も使いません。めんどうだから。
実装方針は「ソースコードが増えないこと」です。


JavaTokenParsersを使うと、Javaリテラル表記がそのまま使えるパーサーが作れるらしいんですけど、構文を全部自分で定義したい病気なので、その一階層上のRegexParsersを使います。


実行して「12-5*3+7*(2+5)+0」をパースするとこんな感じになって、構文の解釈ができてる感じですね。

[1.17] parsed: ( (12~List())~List( (-~(5~List( (*~3)))), 
(+~(7~List( (*~( ( (~( (2~List())~List( (+~(5~List())))))~)))))),
 (+~(0~List()))))

というか、Lispに変換されたw

package parser
import util.parsing.combinator._

object MiniParserMain {

  def main(args: Array[String]): Unit = {
    val parser = new MiniParser
    println(parser.parse("12-5*3+7*(2+5)+0"))
  }

  class MiniParser extends RegexParsers{
    //expr ::= term ["+" term | "-" term}.
    def expr:  Parser[Any] = term~rep("+"~term|"-"~term)
    //term ::= factor {"*" factor}
    def term : Parser[Any] = factor~rep("*"~factor)
    //factor ::= intLiteral | "(" expr ")"
    def factor: Parser[Any] = intLiteral | "("~expr~")"
    //intLiteral ::= ["1"-"9"] {"0"-"9"}
    def intLiteral : Parser[Any] = """[1-9][0-9]*|0""".r
    
    def parse(str:String) = parseAll(expr, str)
  }
}


参考にしてるのは、このあたり。
多忙な Java 開発者のための Scala ガイド: 電卓を作る、第 2 回
パーサーコンビネータで遊んでみた - じゅんいち☆かとうの技術日誌


読んだほうがよさげなのが水島さんの記事
第17回 Scalaとパーザコンビネータ(基本編) | 日経 xTECH(クロステック)
第18回 Scalaとパーザコンビネータ(実装編) | 日経 xTECH(クロステック)


あと、「コップ本」の2版。初版も持ってたけど放置してるうちに2版でちゃった。

Scalaスケーラブルプログラミング第2版

Scalaスケーラブルプログラミング第2版