瀏覽代碼

finished functions & function Calls

simon 3 年之前
父節點
當前提交
4d6d3ce515
共有 3 個文件被更改,包括 52 次插入1 次删除
  1. 30 0
      evaluator/evaluator.go
  2. 11 0
      evaluator/evaluator_test.go
  3. 11 1
      object/environment.go

+ 30 - 0
evaluator/evaluator.go

@@ -73,11 +73,41 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
 		if len(args) == 1 && isError(args[0]) {
 			return args[0]
 		}
+
+		return applyFunction(function, args)
 	}
 
 	return nil
 }
 
+func applyFunction(fn object.Object, args []object.Object) object.Object {
+	function, ok := fn.(*object.Function)
+	if !ok {
+		return newError("not a function: %s", fn.Type())
+	}
+
+	extendEnv := extendFunctionEnv(function, args)
+	evaluated := Eval(function.Body, extendEnv)
+	return unwrapReturnValue(evaluated)
+}
+
+func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
+	env := object.NewEnclosedEnvironment(fn.Env)
+
+	for paramIdx, param := range fn.Parameters {
+		env.Set(param.Value, args[paramIdx])
+	}
+
+	return env
+}
+
+func unwrapReturnValue(obj object.Object) object.Object {
+	if returnValue, ok := obj.(*object.ReturnValue); ok {
+		return returnValue.Value
+	}
+	return obj
+}
+
 func newError(format string, a ...interface{}) *object.Error {
 	return &object.Error{Msg: fmt.Sprintf(format, a...)}
 }

+ 11 - 0
evaluator/evaluator_test.go

@@ -279,3 +279,14 @@ func testEval(input string) object.Object {
 
 	return Eval(program, env)
 }
+
+func TestClosures(t *testing.T) {
+	input := `
+		let newAdder = fn(x) {
+			fn (y) { x + y };
+		};
+		let addTwo = newAdder(2); addTwo(2);
+     `
+
+	testIntegerObject(t, testEval(input), 4)
+}

+ 11 - 1
object/environment.go

@@ -2,15 +2,25 @@ package object
 
 func NewEnvironment() *Environment {
 	s := make(map[string]Object)
-	return &Environment{store: s}
+	return &Environment{store: s, outer: nil}
+}
+
+func NewEnclosedEnvironment(outer *Environment) *Environment {
+	env := NewEnvironment()
+	env.outer = outer
+	return env
 }
 
 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 {