This commit is contained in:
Chuck Smith
2024-01-21 11:41:17 -05:00
parent 13c9062fed
commit 6bb06370bb
4 changed files with 87 additions and 11 deletions

21
evaluator/builtins.go Normal file
View File

@@ -0,0 +1,21 @@
package evaluator
import "monkey/object"
var builtins = map[string]*object.Builtin{
"len": &object.Builtin{
Fn: func(args ...object.Object) object.Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args))
}
switch arg := args[0].(type) {
case *object.String:
return &object.Integer{Value: int64(len(arg.Value))}
default:
return newError("argument to `len` not supported, got %s", args[0].Type())
}
},
},
}

View File

@@ -264,11 +264,15 @@ func newError(format string, a ...interface{}) *object.Error {
} }
func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object { func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object {
val, ok := env.Get(node.Value) if val, ok := env.Get(node.Value); ok {
if !ok { return val
return newError("identifier not found: " + node.Value)
} }
return val
if builtin, ok := builtins[node.Value]; ok {
return builtin
}
return newError("identifier not found: " + node.Value)
} }
func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object { func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object {
@@ -286,14 +290,20 @@ func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Ob
} }
func applyFunction(fn object.Object, args []object.Object) object.Object { func applyFunction(fn object.Object, args []object.Object) object.Object {
function, ok := fn.(*object.Function) switch fn := fn.(type) {
if !ok {
newError("not a function: %s", fn.Type())
}
extendedEnv := extendFunctionEnv(function, args) case *object.Function:
evaluated := Eval(function.Body, extendedEnv) extendedEnv := extendFunctionEnv(fn, args)
return unwrapReturnValue(evaluated) evaluated := Eval(fn.Body, extendedEnv)
return unwrapReturnValue(evaluated)
case *object.Builtin:
return fn.Fn(args...)
default:
return newError("not a function: %s", fn.Type())
}
} }
func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment { func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {

View File

@@ -326,6 +326,37 @@ func TestStringConcatenation(t *testing.T) {
} }
} }
func TestBuiltinFunction(t *testing.T) {
tests := []struct {
input string
expected interface{}
}{
{`len("")`, 0},
{`len("four")`, 4},
{`len("hello world")`, 11},
{`len(1)`, "argument to `len` not supported, got INTEGER"},
{`len("one", "two")`, "wrong number of arguments. got=2, want=1"},
}
for _, tt := range tests {
evaluated := testEval(tt.input)
switch expected := tt.expected.(type) {
case int:
testIntegerObject(t, evaluated, int64(expected))
case string:
errObj, ok := evaluated.(*object.Error)
if !ok {
t.Errorf("object is not Error. got=%T (%+v)", evaluated, evaluated)
continue
}
if errObj.Message != expected {
t.Errorf("wrong error message. expected=%q, got=%q", expected, errObj.Message)
}
}
}
}
func testEval(input string) object.Object { func testEval(input string) object.Object {
l := lexer.New(input) l := lexer.New(input)
p := parser.New(l) p := parser.New(l)

View File

@@ -17,6 +17,7 @@ const (
ERROR_OBJ = "ERROR" ERROR_OBJ = "ERROR"
FUNCTION_OBJ = "FUNCTION" FUNCTION_OBJ = "FUNCTION"
STRING_OBJ = "STRING" STRING_OBJ = "STRING"
BUILTIN_OBJ = "BUILTIN"
) )
type Object interface { type Object interface {
@@ -119,3 +120,16 @@ func (s *String) Type() ObjectType {
func (s *String) Inspect() string { func (s *String) Inspect() string {
return s.Value return s.Value
} }
type BuiltinFunction func(args ...Object) Object
type Builtin struct {
Fn BuiltinFunction
}
func (b Builtin) Type() ObjectType {
return BUILTIN_OBJ
}
func (b Builtin) Inspect() string {
return "builtin function"
}