Golang でプログラミング言語を作る__Part15
前回はif文のevaluatorを実装しました。今回は、return文のを作っていきます。
ここでのreturnの機能は他のプログラミング言語同様に、一連のStatementsの解釈を止めさせ、その止まる前までの処理(evaluation)を行います。
Statements内のreturn以降は、処理されません。
まずは、オブジェクトにreturn文の定義を行います。以前行った、他の値タイプの定義内容と同様になります。
object.ReturnValue は値(Value)をwrapするものになります。ここでのreturn文の値は他のObjectタイプである必要があります。
return文のオブジェクトを作ったら、次はテストを作成します。
evaluator/evaluator_test.go
func TestReturnStatements(t *testing.T) {
tests := []struct {
input string
expected int64
}{
{"return 10;", 10},
{"return 10; 9;", 10},
{"return 2 * 5; 9;", 10},
{"9; return 2 * 5; 9;", 10},
{
`
if (10 > 1) {
if (10 > 1) {
return 10;
}return 1;
}
`,
10,
},
}for _, tt := range tests {
evaluated := testEval(tt.input)
testIntegerObject(t, evaluated, tt.expected)
}
}
上のテストケース(return文)を通るevaluatorは次のものになります。
func Eval(node ast.Node, env *object.Environment) object.Object {
switch node := node.(type) {
// [...] case *ast.Program:
return evalProgram(node, env)
case *ast.ReturnStatement:
val := Eval(node.ReturnValue, env)
if isError(val) {
return val
}
return &object.ReturnValue{Value: val}
case *ast.BlockStatement:
return evalBlockStatement(node, env) // [...]
}
return nil
}func evalProgram(program *ast.Program, env *object.Environment) object.Object {
var result object.Objectfor _, statement := range program.Statements {
result = Eval(statement, env)switch result := result.(type) {
case *object.ReturnValue:
return result.Value
case *object.Error:
return result
}
}
return result
}
ast.Programノード(一連のStatements)は、各Statementを返す evalProgram 関数に渡されます。
return文のオブジェクトが渡された時、そのオブジェクトの値を返します。
この evalProgram 関数では、return文はその値だけを返され、return文のオブジェクトは返されません。
また、”{}”ブロックステートメントタイプのASTノードの場合は、次の evalBlockStatement に処理が移ります。
func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) object.Object {
var result object.Objectfor _, statement := range block.Statements {
result = Eval(statement, env)if result != nil {
rt := result.Type()
if rt == object.RETURN_VALUE_OBJ || rt == object.ERROR_OBJ {
return result
}
}
}
return result
}
ここでは、明示的に返り値をwrapしないで、各オブジェクトのTypeのみをチェックします。
それがreturn文のオブジェクトならば、そのオブジェクトそのものを Eval 関数に渡されます。そして、 object.ReturnValue が返されます。
これによって、ソースの処理(evaluation)を通っていく過程でreturn文があった時に、その時点で処理が終わるようになります。
以上の処理を書き終えたら、テストを走らせpassすることを確認します。
これでreturn文のevaluation実装が終わりました。
次は、エラーハンドリングの実装を行っていきます。
