25
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-12-23

はじめに

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

仕様

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

  • I LOVE YOU
  • I LOVE MUSIC
  • WE LOVE YOU
  • WE LOVE MUSIC

実装

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

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がつかないケースではパースが失敗することを期待します。

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がつかないケースでパースが成功してしまいます。

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がつかないケースではパースが失敗することを期待します。そのように実装を修正してみました。

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実践入門+
プログラミングScala
Scala Advent Calendar 2013の4日目
Scala School

25
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?