Skip to content

Commit

Permalink
FEAT: functions' evaluation works and closures work too;
Browse files Browse the repository at this point in the history
149/208 @ pdf
  • Loading branch information
MKaczkow committed Oct 8, 2024
1 parent b1571e4 commit dd53c3f
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 3 deletions.
1 change: 1 addition & 0 deletions monkey/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ is represented as:
* implementing internal error-handling is kinda similar to handling return statements, in the sense, that they both stop further evaluation
* `environment` is used to keep track of values of variables, which are stored in `hash map`
* function also carry their own `environment`, which allows using `closures`
* extendint `environment` is done by creating new `environment` with reference to the old one (weird, but alows each `function` to have its own `scope`)

### repl
* `REPL` - `Read-Eval-Print Loop`
Expand Down
34 changes: 34 additions & 0 deletions monkey/interpreter/evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,46 @@ 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())
}

extendedEnv := extendFunctionEnv(function, args)
evaluated := Eval(function.Body, extendedEnv)
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 evalExpressions(
exps []ast.Expression,
env *object.Environment,
Expand Down
10 changes: 10 additions & 0 deletions monkey/interpreter/evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,16 @@ func TestFunctionApplication(t *testing.T) {
}
}

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)
}

func testEval(input string) object.Object {
l := lexer.New(input)
p := parser.New(l)
Expand Down
16 changes: 13 additions & 3 deletions monkey/interpreter/object/environment.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
package object

func NewEnclosedEnvironment(outer *Environment) *Environment {
env := NewEnvironment()
env.outer = outer
return env
}

func NewEnvironment() *Environment {
s := make(map[string]Object)
return &Environment{store: s}
s := make(map[string]Object) // map symbol to it's meaning (value)
return &Environment{store: s, outer: nil}
}

type Environment struct {
store map[string]Object
store map[string]Object // map symbol to it's meaning (value)
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
}

Expand Down

0 comments on commit dd53c3f

Please sign in to comment.