Skip to content

Commit

Permalink
Built-in Functions: len()
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tjlawal committed Aug 14, 2023
1 parent c0253cb commit d4abc62
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 11 deletions.
20 changes: 20 additions & 0 deletions evaluator/builtins.go
Original file line number Diff line number Diff line change
@@ -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())
}
},
},
}
28 changes: 18 additions & 10 deletions evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -151,11 +156,14 @@ 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
}
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 {
Expand Down
31 changes: 31 additions & 0 deletions evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 12 additions & 1 deletion object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
ERROR_OBJECT = "ERROR"
FUNCTION_OBJECT = "FUNCTION"
STRING_OBJECT = "STRING"
BUILTIN_OBJ = "BUILTIN"
)

type Object interface {
Expand Down Expand Up @@ -109,4 +110,14 @@ type String struct {
}

func (s *String) Type() ObjectType { return STRING_OBJECT }
func (s *String) Inspect() string { return s.Value }
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" }

0 comments on commit d4abc62

Please sign in to comment.