Golang でプログラミング言語を作る__Part17
今回は、let statementと識別子のevaluation(評価関数)を作っていきます。
そした、名前(識別子)と値とを、紐付けていきます。
まずは、Let statement用のテストを作ります。
evaluator/evaluator_test.go
func TestLetStatements(t *testing.T) {
tests := []struct {
input string
expected int64
}{
{"let a = 5; a;", 5},
{"let a = 5 * 5; a;", 25},
{"let a = 5; let b = a; b;", 5},
{"let a = 5; let b = a; let c = a + b + 5; c;", 15},
}for _, tt := range tests {
testIntegerObject(t, testEval(tt.input), tt.expected)
}
}
このテストの目的は、次の2つになります。
- letで宣言されたstatementが、値を生み出しているのか。
- またその値は、宣言時の識別子と結びついているのか。
このテストをはしらせてfailしたら、次はevaluatorの実装になります。
その前に、値を保持して識別子と結び付ける必要があります。それは、environment(主にLispなどで用いられる用語)が担う機能になります。
まずは、下のコードを書いてきます。
object/environment.go
package objectfunc NewEncloseEnvironment(outer *Environment) *Environment {
env := NewEnvironment()
env.outer = outer
return env
}func NewEnvironment() *Environment {
s := make(map[string]Object)
return &Environment{store: s, outer: nil}
}type Environment struct {
store map[string]Object
outer *Environment
}func (e *Environment) Get(name string) (Object, bool) {
obj, ok := e.store[name]
if !ok && e.outer != nil {
obj, ok = e.outer.Get(name)
}
return obj, ok
}func (e *Environment) Set(name string, val Object) Object {
e.store[name] = val
return val
}
行われている処理は、シンプルです。 Get でstore内に格納されている識別子の値をとって来て、 Set で格納しています。
つまり、 名前と値とが紐付く要素をリスト内に設定する、mapと同じ事をしています。
この object.Environment をevaluator.goファイル内のEval, evalIdentifier関数の第二引数に加えていきます。
最終てきには、ほとんどのタイプのevaluatorに、このenvironmentは加えられます。何故なら、名前と値のバインドはどのタイプの文法にも使われていく為です。
evaluator/evaluator.go
func Eval(node ast.Node, env *object.Environment) object.Object {
switch node := node.(type) {
// [...]
case *ast.LetStatement:
val := Eval(node.Value, env)
if isError(val) {
return val
}
env.Set(node.Name.Value, val) // [...]
case *ast.Identifier:
return evalIdentifier(node, env)
}
return nil
}
上のLet文に対しての処理では、 env.Set() でstore内に名前と値とを一緒にして、格納しています。
最後に、実際にrepl.goファイル内の次の箇所(Eval関数を呼んでいるところ)に、envを追加します。
evaluated := evaluator.Eval(program, env)そして、main.goファイルを走らせたときに、letで正しく変数宣言がされるか確認することができます。
これで、変数宣言(let statement)の実装が終わりました。
次は、function, function callの2つについて、evaluatorを作っていきます。
