Parser Combinator 続き
http://www.scala-lang.org/docu/files/ScalaByExample.pdf
のCombinator Parsing のサンプルなんだけど、最新版のScalaだと動かない。
ドキュメントがついていけてないっぽい。
これを、今インストールしているバージョン(2.7.0)で動くものを作ろうと、
http://d.hatena.ne.jp/ryugate/20080221#p1
を参考にしつつ、あれこれやってて、結局こうなった。
import scala.util.parsing.combinator._ import scala.util.parsing.input.CharArrayReader import scala.util.parsing.input.Reader abstract class Tree case class Id(s: String) extends Tree case class Num(n: Int) extends Tree case class Lst(elems: List[Tree]) extends Tree trait ListParsers extends Parsers { type Elem = Char def letter: Parser[Elem] = elem("LETTER", Character.isLetter _) def digit: Parser[Elem] = elem("DIGIT", Character.isDigit _) def ident: Parser[Id] = letter ~ (letter | digit).* ^^ createIdNode def number: Parser[Num] = digit ~ digit.* ^^ createNumNode def list: Parser[Lst] = '(' ~> listElems.? <~ ')' ^^ createLstNode def expr: Parser[Tree] = ident | number | list ^^ createExprNode def listElems: Parser[List[Tree]] = expr ~ (',' ~> listElems).? ^^ createListElemsNode def createIdNode(value: ~[Elem, List[Elem]]): Id def createNumNode(value: ~[Elem, List[Elem]]): Num def createLstNode(value: Option[List[Tree]]): Lst def createExprNode(value: Tree): Tree def createListElemsNode(value: ~[Tree, Option[List[Tree]]]): List[Tree] } class TreeListParsers extends ListParsers { def createIdNode(value: ~[Elem, List[Elem]]): Id = value match { case ~(a, b) => Id("" + a + b.mkString) } def createNumNode(value: ~[Elem, List[Elem]]): Num = value match { case ~(a, b) => Num(("" + a + b.mkString).toInt) } def createLstNode(value: Option[List[Tree]]): Lst = value match { case Some(t) => Lst(t) case None => Lst(List()) } def createExprNode(value: Tree): Tree = value def createListElemsNode(value: ~[Tree, Option[List[Tree]]]): List[Tree] = value match { case ~(head, Some(list)) => head :: list case ~(head, None) => List(head) } } def toInput(s: String): Reader[Char] = new CharArrayReader(s.toArray) val p = new TreeListParsers println(p.expr(toInput("12"))) println(p.expr(toInput("ab"))) println(p.expr(toInput("()"))) println(p.expr(toInput("(ab,12)"))) println(p.expr(toInput("((ab),12,(cd,34))"))) 0
グラマーの定義は、ListParsers に、Treeの組み立ては、TreeListParsers に別個にしてみた。
これを、ListParsers.scala というファイルに保存して実行すると、以下のような結果が出力される。
< scala ListParsers.scala [1.3] parsed: Num(12) [1.3] parsed: Id(ab) [1.3] parsed: Lst(List()) [1.8] parsed: Lst(List(Id(ab), Num(12))) [1.18] parsed: Lst(List(Lst(List(Id(ab))), Num(12), Lst(List(Id(cd), Num(34)))))
できたはできたけど、こんな感じでいいのかよくわからんなー。
ポイントは、
- "^^"の左辺の関数の戻り値の型と、グラマー(identとか、exprとか)の型Parser[?] の ? の部分を一致させる。
- "^^"の左辺の関数の引数の型は、グラマー定義の内容で決まる。上で使ったやつを説明すると。
- type Elemで指定した型には、Parsers.scala内の以下の Implicit Conversion が適用される。
- implicit def accept(e: Elem): Parser[Elem]
- 上記のグラマーだと、「'(' ~> 」の部分に、Implicit Conversion が適用されている。