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