Scalaの勉強をはじめたので、とりあえず簡単なパーサーを作ってみてます。
http://d.hatena.ne.jp/nowokay/20111109#1320815540
前回で変数導入しました。
http://d.hatena.ne.jp/nowokay/20111110#1320899264
けど、
val aa = 23; println("pre block:" + aa); { val aa = 4; println("in block:" + aa); val bb = 3; println(bb); }; println("post block:" + aa); println(bb);
を実行すると
pre block:23 in block:4 3 post block:4 3
のように表示されて、ブロックのなかの変数宣言がブロックの外に影響してます。これは気持ち悪い。
ということで、変数のスコープを導入します。
class Environment(parent:Option[Environment]){ val variables = Map[String, Any]() def get(key:String):Any = { if(variables.contains(key)){ variables(key) }else{ parent match{ case Some(p) => p.get(key) case None => throw new Exception("symbol'%s' not found".format(key)) } } } def set(key:String, value:Any){ variables(key) = value } }
親環境を導入して、変数参照のときローカル環境に変数がなければ親環境を見るようにしてます。
変数がなければ、例外なげてます。
ブロックであるLinesで、ローカル環境作ってそれを使って各行の処理してます。
case Lines(exprs) =>{ val local = new Environment(Some(env)) exprs.foldLeft(Unit:Any){(result, x) => eval(local, x)} }
あと、ループで最後の式を値にするために、みずしまさんに教えてもらったfoldLeft使ってます。
evalが再帰呼び出しになるので、型を解決しておく必要があります。
def eval(env:Environment, ast:AST):Any = {
で、呼び出します。
var result = visitor.eval(new Environment(None), ast);
gakuzoさんに教えてもらった書き方でさらにすっきり
def printLine: Parser[AST] = "println"~"("~>expr<~")"^^PrintLine
これで、スコープが実現できました!
val aa = 23; println("pre block:" + aa); { val aa = 4; println("in block:" + aa); val bb = 3; println(bb); }; println("post block:" + aa); println(bb);
を実行するとこうなります。
pre block:23 in block:4 3 post block:23 例外:symbol'bb' not found
ソースはgistに。
https://gist.github.com/1345875/3fad22314df53a7a534618d58a24a00fb68960f3