Scalaでパーサーを作ってみる〜14:組み込み関数

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


前回でFizzBuzzを動かしてみました。
http://d.hatena.ne.jp/nowokay/20111118#1321590289


これで、言語としては最低限の機能が実装できたわけですが、メッセージ表示のprintln関数が言語要素になっているのが気になります。
せっかく関数の仕組みも実装したので、printlnも通常の関数にしてしまいた。
とはいえ、どうやっても今のままでは計算をする関数が定義できるだけで、コンソールにメッセージを表示する関数を定義することはできません。


というわけで、処理自体はScalaで記述した関数を登録して組み込み関数として使えるようにしてみます。


まず組み込み関数をあらわすトレイトを用意します。

trait EmbeddedFunc{
  def exec(params:List[Any]):Any
}


FuncCallのcaseにEmbeddedFuncの処理も含めます。

 case f:EmbeddedFunc => {
      f.exec(params.map(visit))
 }


そしたら、構文定義のexprからprintLineを抜いてしまいましょう。

  //expr ::= cond | if
  def expr: Parser[AST] = condOp|ifExpr


identのキーワード判定でもprintlnが必要ないので抜いてしまいます。

  def ident :Parser[Ident] = """[A-Za-z_][a-zA-Z0-9]*""".r^?{
    case n if n != "if" && n!= "val" && n != "def" => n}^^Ident

これでprintlnが構文上から消えました。おつかれprintln。
ほかのprintln関連の定義なども消しておきます。


そしたら、組み込み関数としてのprintlnを作成します。今回は最初の引数をとりあえず表示する感じで。

object PrintLineFunc extends EmbeddedFunc{
  def exec(params:List[Any])={
    params match{
      case Nil =>{
        println()
        Nil
      }
      case v::_ =>{
        println(v)
        v
      }
    }
  }
}


あとはevalするまえに環境にprintlnという名前で組み込んでおきます。

    val visitor = new ExprVisitor
    val env = new Environment(None)
    env.set("println", PrintLineFunc)
    var result = visitor.eval(env, ast);

これでいままで通り動かせるわけですが、ちょっと物足りない。


ということで、メッセージボックスを出す関数を。

object MessageFunc extends EmbeddedFunc{
  def exec(params:List[Any])={
    params match{
      case Nil => {}
      case v::_ =>{
          javax.swing.JOptionPane.showMessageDialog(null, v.toString)
          v
      }
    }
  }
}


messageという名前で登録します。

    env.set("message", MessageFunc)


FizzBuzzが終わったらメッセージを表示するようにしてみましょう。

      }else{
        message("Finish" + x);
        0
      }


おぉ、メッセージが表示されました。


でもなんか、思ったほどの感動は なかったな。まあいいや。


ソースはこれ
https://gist.github.com/1345875/ca60a97bc9d92befa3f5e24e8b14561426d479ef