Scalaのパーサーコンビネータをつかってみました

## はじめに

Scalaのパーサーコンビネータをつかってみました。ここでは英文を解析したいと思います。しかし、汎用的な構文解析は難しいので、特定の英文を解析します。特定の英文以外は解析できないので、少し間抜けなパーサです。

## 仕様

このパーサーは特定の英文を解析します。次のような英文は解析できますが、それ以外の英文は解析できません。

- I LOVE YOU
- I LOVE MUSIC
- WE LOVE YOU
- WE LOVE MUSIC

## 実装

字句解析はライブラリが処理しています。構文解析は演算子を使って実装しました。ここでは意味解析についてはふれていません。

``` scala
import scala.util.parsing.combinator._

object App {
  def main(args : Array[String]) : Unit = {
    GrammarParser("I LOVE YOU")
    GrammarParser("I LOVE MUSIC")
    GrammarParser("WE LOVE YOU")
    GrammarParser("WE LOVE MUSIC")
  }
}

object GrammarParser extends JavaTokenParsers {
  def third = s ~ v ~ o ^^ { case subj ~ verb ~ obj => println(subj + verb + obj) }
  def s = "I"|"WE"
  def v = "LOVE"
  def o = "YOU"|"MUSIC"

  def apply(s : String) : Unit = {
    parseAll(third, s) match {
      case Success(_, _) => println("Success")
      case x => println("Failure: " + x)
    }
  }
}
```

## コンパイル方法

次のようなコマンドラインでコンパイルを実行します。

> scalac file_name.scala

## 実行方法

次のようなコマンドラインでプログラムを実行します。

> scala App

## 追加仕様

主語に彼・彼女を追加してみたいと思います。そして、三人称単数のケースを考えます。まずは、彼・彼女のみのケースを実装してみます。

## 彼・彼女のみのケース

文字列リテラルの代わりに、rメソッドを使って正規表現で実装しています。ここでは使いませんでしたが、正規表現でバックスラッシュを使ってクォートする場合はダブルクォートを三つ使ったほうがシンプルに実装できます。

実行結果として、三人称単数の`s`がつかないケースではパースが失敗することを期待します。

```scala
import scala.util.parsing.combinator._

object App {
  def main(args : Array[String]) : Unit = {
    GrammarParser("HE LOVES YOU")
    GrammarParser("HE LOVES MUSIC")
    GrammarParser("SHE LOVES YOU")
    GrammarParser("SHE LOVES MUSIC")
    GrammarParser("HE LOVE YOU")
    GrammarParser("HE LOVE MUSIC")
    GrammarParser("SHE LOVE YOU")
    GrammarParser("SHE LOVE MUSIC")
  }
}

object GrammarParser extends JavaTokenParsers {
  def third = s ~ v ~ o ^^ { case subj ~ verb ~ obj => println(subj + verb + obj) }
  def s = "S?HE".r
  def v = "LOVES"
  def o = "YOU" | "MUSIC"

  def apply(s : String) : Unit = {
    parseAll(third, s) match {
      case Success(_, _) => println("Success")
      case x => println("Failure: " + x)
    }
  }
}
```

## 私・私たち・彼・彼女のケース（誤）

単純に拡張すると、期待しない結果になります。三人称単数の`s`がつかないケースでパースが成功してしまいます。

```scala
import scala.util.parsing.combinator._

object App {
  def main(args : Array[String]) : Unit = {
    GrammarParser("I LOVE YOU")
    GrammarParser("I LOVE MUSIC")
    GrammarParser("WE LOVE YOU")
    GrammarParser("WE LOVE MUSIC")
    GrammarParser("HE LOVES YOU")
    GrammarParser("HE LOVES MUSIC")
    GrammarParser("SHE LOVES YOU")
    GrammarParser("SHE LOVES MUSIC")
    GrammarParser("HE LOVE YOU")
    GrammarParser("HE LOVE MUSIC")
    GrammarParser("SHE LOVE YOU")
    GrammarParser("SHE LOVE MUSIC")
  }
}

object GrammarParser extends JavaTokenParsers {
  def third = s ~ v ~ o ^^ { case subj ~ verb ~ obj => println(subj + verb + obj) }
  def s = "S?HE".r | "I" | "WE"
  def v = "LOVES?".r
  def o = "YOU" | "MUSIC"

  def apply(s : String) : Unit = {
    parseAll(third, s) match {
      case Success(_, _) => println("Success")
      case x => println("Failure: " + x)
    }
  }
}
```

## 私・私たち・彼・彼女のケース

三人称単数の`s`がつかないケースではパースが失敗することを期待します。そのように実装を修正してみました。

```scala
import scala.util.parsing.combinator._

object App {
  def main(args : Array[String]) : Unit = {
    GrammarParser("I LOVE YOU")
    GrammarParser("I LOVE MUSIC")
    GrammarParser("WE LOVE YOU")
    GrammarParser("WE LOVE MUSIC")
    GrammarParser("HE LOVES YOU")
    GrammarParser("HE LOVES MUSIC")
    GrammarParser("SHE LOVES YOU")
    GrammarParser("SHE LOVES MUSIC")
    GrammarParser("HE LOVE YOU")
    GrammarParser("HE LOVE MUSIC")
    GrammarParser("SHE LOVE YOU")
    GrammarParser("SHE LOVE MUSIC")
  }
}

object GrammarParser extends JavaTokenParsers {
  def third = s ~ v ~ o ^^ { case s ~ v ~ o => println(s +"\""+ v +"\""+ o) } |
              su ~ ve ~ o ^^ { case s ~ v ~ o => println(s +"\""+ v +"\""+ o) }
  def s = "I" | "WE"
  def su = "S?HE".r
  def v = "LOVE"
  def ve = "LOVES"
  def o = "YOU" | "MUSIC"

  def apply(s : String) : Unit = {
    parseAll(third, s) match {
      case Success(_, _) => println("Success")
      case x => println("Failure: " + x)
    }
  }
}
```

## まとめ

まとめとしては、主語をむやみに大きくしないことでしょうか。。。それはさておき、Scalaのパーサーコンビネータのシンプルさに驚きました。機会があればもう少し実用的なものを作ってみたいです。

## 参照URL&書籍

[ScalaのParserCombinator実践入門＋][1]
[プログラミングScala][2]
[Scala Advent Calendar 2013の4日目][3]
[Scala School][4]

[1]: http://blob.geishatokyo.com/archives/185379
[2]: http://www.oreilly.co.jp/books/9784873114811/
[3]: https://gist.github.com/gkojax/7782764
[4]: http://twitter.github.io/scala_school/