Built-in Functions: len()

Will take in a string a string and return the length of said string. This function only accepts one 
argument. This function only accepts strings. Any other type will result in an error.

git-svn-id: https://svn.tlawal.org/svn/monkey@65 f6afcba9-9ef1-4bdd-9b72-7484f5705bac
This commit is contained in:
Tijani Lawal 2023-08-14 19:40:04 +00:00
parent 64e048dfcd
commit 831b333a74
4 changed files with 81 additions and 11 deletions

20
evaluator/builtins.go Normal file
View File

@ -0,0 +1,20 @@
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 new_error("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 new_error("argument to `len` not supported, got %s", args[0].Type())
}
},
},
}

View File

@ -111,14 +111,19 @@ func eval_program(program *ast.Program, env *object.Environment) object.Object {
}
func apply_function(fn object.Object, args []object.Object) object.Object {
function, ok := fn.(*object.Function)
if !ok {
return new_error("not a function: %s", fn.Type())
switch fn := fn.(type) {
case *object.Function:
extended_env := extend_function_env(fn, args)
evaluated := Eval(fn.Body, extended_env)
return unwrap_return_value(evaluated)
case *object.Builtin:
return fn.Fn(args...)
default:
return new_error("not a funciton: %s", fn.Type())
}
extended_env := extend_function_env(function, args)
evaluated := Eval(function.Body, extended_env)
return unwrap_return_value(evaluated)
}
func extend_function_env(fn *object.Function, args []object.Object) *object.Environment {
@ -151,12 +156,15 @@ func eval_expression(expressions []ast.Expression, env *object.Environment) []ob
}
func eval_identifier(node *ast.Identifier, env *object.Environment) object.Object {
val, ok := env.Get(node.Value)
if !ok {
return new_error("identifier not found: " + node.Value)
}
if val, ok := env.Get(node.Value); ok {
return val
}
if builtin, ok := builtins[node.Value]; ok {
return builtin
}
return new_error("identifier not found: " + node.Value)
}
func eval_block_statement(block *ast.BlockStatement, env *object.Environment) object.Object {
var result object.Object

View File

@ -270,6 +270,37 @@ func TestStringConcatenation(l_test *testing.T) {
}
}
func TestBuiltinFunction(l_test *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 := test_eval(tt.input)
switch expected := tt.expected.(type) {
case int:
test_integer_object(l_test, evaluated, int64(expected))
case string:
error_object, ok := evaluated.(*object.Error)
if !ok {
l_test.Errorf("object is not Error, got=%T (%+v)", evaluated, evaluated)
continue
}
if error_object.Message != expected {
l_test.Errorf("wrong error message, expected=%q, got=%q", expected, error_object.Message)
}
}
}
}
// Helpers
func test_eval(input string) object.Object {
l_lexer := lexer.New(input)

View File

@ -17,6 +17,7 @@ const (
ERROR_OBJECT = "ERROR"
FUNCTION_OBJECT = "FUNCTION"
STRING_OBJECT = "STRING"
BUILTIN_OBJ = "BUILTIN"
)
type Object interface {
@ -110,3 +111,13 @@ type String struct {
func (s *String) Type() ObjectType { return STRING_OBJECT }
func (s *String) Inspect() string { return s.Value }
// Built-in functions
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" }