読者です 読者をやめる 読者になる 読者になる

テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(scala report) 簡単なSGFを読めるようになった

SGFパーサがちょっとだけ動くようになった.

今まで書いたパーサ構築用のコードは以下. Scalaは良い...

class SGF extends RegexParsers {
  import sgf._

  def concat(xs: List[String]) = xs.fold(""){(acc,e) => acc+e}

  def pAll = pCollection
  def pCollection = pGameTree.+ ^^ { Collection(_) }
  def pGameTree: Parser[GameTree] =
    "(" ~ pSequence ~ pGameTree.* ~ ")" ^^ {
      case "(" ~ seq ~ gts ~ ")" => {
        GameTree(seq, gts)
      }
    }
  def pSequence = pNode.+ ^^ { Sequence(_) }
  def pNode = ";" ~> pProperty.* ^^ { Node(_) }
  def pPropIdent = pUcLetter.+ ^^ { case xs => PropIdent(concat(xs)) }
  def pProperty =
    pPropIdent into {
      case pid @ PropIdent(id) => {
        id match {
          // TODO more and refactor
          case "KM" => "[" ~> pReal <~ "]" ^^ { case x => Property(pid, List(PropValue(x))) }
        }
      }
    }

  def pNone = "" ^^ { _ => None }
  def pUcLetter = """[A-Z]""".r
  def pDigit = """[0-9]""".r
  def pNumber_ = ("+"|"-") ~ rep1(pDigit) ^^ {
    case sig ~ digits => {
      val a: Int = sig match {
        case "+" => 1
        case "-" => -1
      }
      val b: Int = concat(digits).toInt
      a * b
    }
  }
  def pNumber = pNumber_ ^^ { Number(_) }
  def pReal = pNumber_ ~ "." ~ rep1(pDigit) ^^ {
    case int ~ "." ~ decimal => {
      val a: Int = int
      val b: Float = ("0." + concat(decimal)).toFloat
      Real(a + b)
    }
  }
  def pDouble = ("1"|"2") ^^ { case x => Double(x.toInt) }
  def pColor = ("B"|"W") ^^ { case x => Color(x.charAt(0)) }
}

こいつを使うと, "(;KM[+0.5])"のような超ミニマム(かつValid)なSGF文字列をパースすることが出来る. 結果は, [1.12] parsed: Collection(List(GameTree(Sequence(List(Node(List(Property(PropIdent(KM),List(PropValue(Real(0.5)))))))),List())))のような感じ. 可読性はないが, 型に守られているはずなので安心は出来る.

今後は, pPropertyを充実させていけばいいはず. コードの重複も増えてくるので, コンビネータを定義して重複を削ることも必要になる. あと以下にもwhitespaceに弱そうなので, 適切にskipするコードを入れる必要があるかも知れない. このまま一気に行く.