Scalaでパーサーを作ってみる〜6:コードブロック

Scalaの勉強をはじめたので、とりあえず簡単なパーサーを作ってみてます。
http://d.hatena.ne.jp/nowokay/20111101#1320102262


前回は、文字列とprintlnを導入しました。
http://d.hatena.ne.jp/nowokay/20111107#1320637899


そうすると、複数の式を実行したくなります。ということで、コードブロックを導入します。
コードブロックは、「{ 〜 }」の形で複数の式を記述します。式の区切りは「;(セミコロン)」です。
残念ながら、セミコロンは省略できません。残念ながら。


さて、まずブロック内の式をまとめる構文要素のLinesケースクラスを作ります。

case class Lines(exprs:List[AST]) extends AST


構文規則の定義です。セミコロン区切りです。repsepとoptを使ってます。

  //lines ::= expr {";" expr} [";"]
  def lines: Parser[AST] = repsep(expr, ";")<~opt(";")^^{
    values => Lines(values)
  }


factorに中括弧つきのlinesを組み込みます。

  //factor ::= intLiteral | stringLiteral | "(" expr ")" | "{" lines "}"
  def factor: Parser[AST] = intLiteral | stringLiteral | 
    "("~>expr<~")"^^{ x=>x } | "{"~>lines<~"}"^^{x=>x}


あとは、Linesの処理です。ここでは、最後の行を式の結果としています。

      case Lines(exprs) =>{
          var result:Any = Unit
          for(x <- exprs){
            result = visit(x)
          }
          result
      }

for式って、最後の値を値として返すんじゃないんですね。


あとはパースの開始をexprじゃなくlinesからにします。

  def parse(str:String) = parseAll(lines, str)


そうすると、こんな(ひどい)式が動くようになりました!

    val expr = """
println("result: "+(3+(if ({
  println("cond");
  3 < 5
}) println(12 * 2) else if(3+2) 15 + 3 else 0)));
12+6;
"""


結果はこんな感じ

cond
24
result: 27
18


がぜんプログラムっぽくなってきた!

そろそろソースが100行を越えたので、gistに置くようにしました。
https://gist.github.com/1345875/cc401d8e81d007b4701c5b5b5c1587e096d48aee