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