Scalaでパーサーを作ってみる〜7:変数

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


前回で、コードブロックを導入して複数の式が評価できるようになりました。
http://d.hatena.ne.jp/nowokay/20111108#1320745071


ときたら、次は変数を使いたくなるのが人情。
まずは、変数を覚えておくための「環境」を用意します。というか、変数テーブルのことをプログラム言語の世界で環境というらしいです。

class Environment{
  val variables = Map[String, Any]()
  def get(key:String) = {
    variables(key)
  }
  def set(key:String, value:Any){
    variables(key) = value
  }
}


Mapは変更するのでmutableです。

import scala.collection.mutable.Map


そしたら、式を評価するvisit関数をeval関数のローカル関数にして、引数として環境を管理するようにします。これはみずしまさんのサンプルからもってきました。

  def eval(env:Environment, ast:AST) = {
    def visit(ast:AST):Any = {
      〜
    }
    visit(ast)
  }


呼び出しのときに環境を作成して渡します。

var result = visitor.eval(new Environment(), ast);


そしたら、変数と変数定義の構文要素を用意しましょう。

case class Ident(name: String) extends AST
case class Assignment(variable: String, value: AST) extends AST


変数の構文規則です。ここで、ifやvalなどのキーワードははじいてます。これもみずしまさんのコードから。

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


あとは変数の定義です。今回はvalで変数定義しましょう。

  def assignment:Parser[Assignment] = "val"~>ident~"="~expr^^{
    case v~_~value => Assignment(v.name, value)
  }


linesのそれぞれの行をlineとして抜き出して、そこで式か変数定義が受けれるようにしておきます。ここで、変数定義は式ではないということになっています。

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


factorに変数を受けれるようにします。

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


それでは、変数のための構文の実行を行います。
変数割り当てのときに環境に値をつっこみます。

        case Assignment(vr, value) =>{
            val v = visit(value)
            env.set(vr, v)
        }


変数の評価で環境から値をとってきます。

        case Ident(name) => {
            env.get(name)
        }


そうすると、こんな感じで変数が定義して使えるようになりました!

val aa = 23;
println(aa * 2)

だいぶプログラム言語です!


ソースはgistに。
https://gist.github.com/1345875/36c9b2d5b2bc41745d0050e0fc35c0e824ecb312