diff options
| author | Bobby <[email protected]> | 2024-04-04 14:29:40 +0000 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-04-04 14:29:40 +0000 |
| commit | 98b3baa1d9d05551948b6657b4130cf05c11934d (patch) | |
| tree | c9ff7cc56f26e6481f0ca0cd74bae4f64abe53b0 /evaluator | |
| parent | 2f28a8de055029c4501a8cfb796e7f4a1af8b719 (diff) | |
| download | mana-98b3baa1d9d05551948b6657b4130cf05c11934d.tar.xz mana-98b3baa1d9d05551948b6657b4130cf05c11934d.zip | |
built in functions
Diffstat (limited to 'evaluator')
| -rw-r--r-- | evaluator/builtins.go | 23 | ||||
| -rw-r--r-- | evaluator/evaluator.go | 28 | ||||
| -rw-r--r-- | evaluator/evaluator_test.go | 32 |
3 files changed, 73 insertions, 10 deletions
diff --git a/evaluator/builtins.go b/evaluator/builtins.go new file mode 100644 index 0000000..0e6846a --- /dev/null +++ b/evaluator/builtins.go @@ -0,0 +1,23 @@ +package evaluator + +import ( + "mana/object" +) + +var builtins = map[string]*object.Builtin{ + "len": { + 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", arg.Type()) + } + }, + }, +} diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 0eadd92..86d1889 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -98,15 +98,19 @@ func Eval(node ast.Node, env *object.Environment) object.Object { } func applyFunction(fn object.Object, args []object.Object) object.Object { - function, ok := fn.(*object.Function) + switch fn := fn.(type) { - if !ok { + case *object.Function: + extendedEnv := extendFunctionEnv(fn, args) + evaluated := Eval(fn.Body, extendedEnv) + return unwrapReturnValue(evaluated) + + case *object.Builtin: + return fn.Fn(args...) + + default: return newError("not a function: %s", fn.Type()) } - - extendedEnv := extendFunctionEnv(function, args) - evaulated := Eval(function.Body, extendedEnv) - return unwrapReturnValue(evaulated) } func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment { @@ -288,11 +292,15 @@ func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Obje } func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object { - val, ok := env.Get(node.Value) - if !ok { - return newError("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 newError("identifier not found: " + node.Value) } func isTruthy(obj object.Object) bool { diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index 9738d9e..22fd80a 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -345,3 +345,35 @@ func TestStringConcatenation(t *testing.T) { t.Errorf("String has wrong value. got=%q", str.Value) } } + +func TestBuiltinFunctions(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) + } + } + } +} |
