aboutsummaryrefslogtreecommitdiff
path: root/evaluator
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-04-04 14:29:40 +0000
committerBobby <[email protected]>2024-04-04 14:29:40 +0000
commit98b3baa1d9d05551948b6657b4130cf05c11934d (patch)
treec9ff7cc56f26e6481f0ca0cd74bae4f64abe53b0 /evaluator
parent2f28a8de055029c4501a8cfb796e7f4a1af8b719 (diff)
downloadmana-98b3baa1d9d05551948b6657b4130cf05c11934d.tar.xz
mana-98b3baa1d9d05551948b6657b4130cf05c11934d.zip
built in functions
Diffstat (limited to 'evaluator')
-rw-r--r--evaluator/builtins.go23
-rw-r--r--evaluator/evaluator.go28
-rw-r--r--evaluator/evaluator_test.go32
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)
+ }
+ }
+ }
+}