builtins
This commit is contained in:
21
evaluator/builtins.go
Normal file
21
evaluator/builtins.go
Normal 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())
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -264,13 +264,17 @@ 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 newError("identifier not found: " + node.Value)
|
|
||||||
}
|
|
||||||
return val
|
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 {
|
||||||
var result []object.Object
|
var result []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)
|
||||||
|
evaluated := Eval(fn.Body, extendedEnv)
|
||||||
return unwrapReturnValue(evaluated)
|
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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user