Golang でプログラミング言語を作る__Part17

Tuyoshi Akiyama
Aug 22, 2017 · 5 min read

今回は、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つになります。

  1. letで宣言されたstatementが、値を生み出しているのか。
  2. またその値は、宣言時の識別子と結びついているのか。

このテストをはしらせて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を作っていきます。

)
Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade