Skip to content

Commit

Permalink
evaluate lambda expression
Browse files Browse the repository at this point in the history
  • Loading branch information
JunNishimura committed Jul 6, 2024
1 parent 303178c commit 608cc47
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 12 deletions.
23 changes: 19 additions & 4 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ func evalSymbol(symbol *ast.Symbol, env *object.Environment) object.Object {

// evaluate cdr of the cons cell as arguments to the command car
func evalList(sexp *ast.ConsCell, env *object.Environment) object.Object {
// check if the car is a special form
if specialForm, ok := sexp.Car().(*ast.SpecialForm); ok {
return specialForms[specialForm.TokenLiteral()].Fn(sexp.Cdr(), env)
}

// Evaluate the car of the cons cell
car := Eval(sexp.Car(), env)
if isError(car) {
Expand Down Expand Up @@ -156,10 +161,16 @@ func evalValueList(consCell *ast.ConsCell, env *object.Environment) []object.Obj
func applyFunction(fn object.Object, args []object.Object) object.Object {
switch fn := fn.(type) {
case *object.Function:
extendedEnv := extendFunctionEnv(fn, args)
extendedEnv, err := extendFunctionEnv(fn, args)
if err != nil {
return newError(err.Error())
}
return Eval(fn.Body, extendedEnv)
case *object.Symbol:
extendedEnv := extendFunctionEnv(fn.Function, args)
extendedEnv, err := extendFunctionEnv(fn.Function, args)
if err != nil {
return newError(err.Error())
}
symbolFunc := fn.Function
return Eval(symbolFunc.Body, extendedEnv)
case *object.Builtin:
Expand All @@ -169,12 +180,16 @@ func applyFunction(fn object.Object, args []object.Object) object.Object {
}
}

func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
func extendFunctionEnv(fn *object.Function, args []object.Object) (*object.Environment, error) {
if len(fn.Parameters) != len(args) {
return nil, fmt.Errorf("function expects %d arguments, but got %d", len(fn.Parameters), len(args))
}

env := object.NewEnclosedEnvironment(fn.Env)

for i, param := range fn.Parameters {
env.Set(param.Value, args[i])
}

return env
return env, nil
}
17 changes: 17 additions & 0 deletions evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,20 @@ func TestEvalIntegerExpression(t *testing.T) {
testIntegerObject(t, evaluated, tt.expected)
}
}

func TestLambdaExpression(t *testing.T) {
tests := []struct {
input string
expected int64
}{
{"((lambda () 5))", 5},
{"((lambda (x) x) 5)", 5},
{"((lambda (x y) (+ x y)) 5 5)", 10},
{"(+ ((lambda () 1)) ((lambda (x y) (+ x y)) 1 2))", 4},
}

for _, tt := range tests {
evaluated := testEval(tt.input)
testIntegerObject(t, evaluated, tt.expected)
}
}
63 changes: 63 additions & 0 deletions evaluator/specialform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package evaluator

import (
"fmt"

"github.com/JunNishimura/go-lisp/ast"
"github.com/JunNishimura/go-lisp/object"
)

var specialForms = map[string]*object.SpecialForm{
"lambda": {
Fn: func(sexp ast.SExpression, env *object.Environment) object.Object {
consCell, ok := sexp.(*ast.ConsCell)
if !ok {
return newError("cdr of lambda must be a cons cell, got %T", sexp)
}

params, err := evalLambdaParams(consCell.Car())
if err != nil {
return newError(err.Error())
}

cdr := consCell.Cdr()
consCell, ok = cdr.(*ast.ConsCell)
if !ok {
return newError("cdr of lambda must be a cons cell, got %T", cdr)
}
body := consCell.Car()

return &object.Function{Parameters: params, Body: body, Env: env}
},
},
}

func evalLambdaParams(sexp ast.SExpression) ([]*ast.Symbol, error) {
params := []*ast.Symbol{}

if _, ok := sexp.(*ast.Nil); ok {
return params, nil
}

consCell, ok := sexp.(*ast.ConsCell)
if !ok {
return nil, fmt.Errorf("parameters must be a list, got %T", sexp)
}

for {
symbol, ok := consCell.Car().(*ast.Symbol)
if !ok {
return nil, fmt.Errorf("parameter must be a symbol, got %T", consCell.Car())
}
params = append(params, symbol)

switch cdr := consCell.Cdr().(type) {
case *ast.Nil:
return params, nil
case *ast.ConsCell:
consCell = cdr
default:
return nil, fmt.Errorf("parameters must be a list, got %T", consCell.Cdr())
}
}
}
25 changes: 17 additions & 8 deletions object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import (
)

const (
ERROR_OBJ = "ERROR"
NIL_OBJ = "NIL"
INTEGER_OBJ = "INTEGER"
FUNCTION_OBJ = "FUNCTION"
SYMBOL_OBJ = "SYMBOL"
CONSCELL_OBJ = "CONSCELL"
LIST_OBJ = "LIST"
BUILTIN_OBJ = "BUILTIN"
ERROR_OBJ = "ERROR"
NIL_OBJ = "NIL"
INTEGER_OBJ = "INTEGER"
FUNCTION_OBJ = "FUNCTION"
SYMBOL_OBJ = "SYMBOL"
SPECIALFORM_OBJ = "SPECIALFORM"
CONSCELL_OBJ = "CONSCELL"
LIST_OBJ = "LIST"
BUILTIN_OBJ = "BUILTIN"
)

type BuiltInFunction func(args ...Object) Object
type SpecialFormFunction func(ast.SExpression, *Environment) Object

type ObjectType string

Expand Down Expand Up @@ -87,6 +89,13 @@ type Builtin struct {
func (b *Builtin) Type() ObjectType { return BUILTIN_OBJ }
func (b *Builtin) Inspect() string { return "builtin function" }

type SpecialForm struct {
Fn SpecialFormFunction
}

func (sf *SpecialForm) Type() ObjectType { return SPECIALFORM_OBJ }
func (sf *SpecialForm) Inspect() string { return "special form" }

type ConsCell struct {
Car Object
Cdr Object
Expand Down

0 comments on commit 608cc47

Please sign in to comment.