Explorar o código

built-in function: len()

simon %!s(int64=3) %!d(string=hai) anos
pai
achega
ca0563df26
Modificáronse 4 ficheiros con 78 adicións e 10 borrados
  1. 19 0
      evaluator/builtins.go
  2. 15 10
      evaluator/evaluator.go
  3. 32 0
      evaluator/evaluator_test.go
  4. 12 0
      object/object.go

+ 19 - 0
evaluator/builtins.go

@@ -0,0 +1,19 @@
+package evaluator
+
+import "github/runnignwater/monkey/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", args[0].Type())
+			}
+		},
+	},
+}

+ 15 - 10
evaluator/evaluator.go

@@ -84,14 +84,16 @@ 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)
-	if !ok {
+	switch fn := fn.(type) {
+	case *object.Function:
+		extendEnv := extendFunctionEnv(fn, args)
+		evaluated := Eval(fn.Body, extendEnv)
+		return unwrapReturnValue(evaluated)
+	case *object.Builtin:
+		return fn.Fn(args...)
+	default:
 		return newError("not a function: %s", fn.Type())
 	}
-
-	extendEnv := extendFunctionEnv(function, args)
-	evaluated := Eval(function.Body, extendEnv)
-	return unwrapReturnValue(evaluated)
 }
 
 func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment {
@@ -249,11 +251,14 @@ func evalBangOperatorExpression(right object.Object) object.Object {
 }
 
 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
+	}
+
+	if builtin, ok := builtins[node.Value]; ok {
+		return builtin
 	}
-	return val
+	return newError("identifier not found: " + node.Value)
 }
 
 func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object {

+ 32 - 0
evaluator/evaluator_test.go

@@ -322,3 +322,35 @@ func TestStringConcatenation(t *testing.T) {
 		t.Errorf("String has wrong value. got=%q", str.Value)
 	}
 }
+
+// ------------------------------------------------------------------------Built-in Function
+
+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)
+			}
+			if errObj.Msg != expected {
+				t.Errorf("wrong error message. expected=%q, got=%q", expected, errObj.Msg)
+			}
+		}
+	}
+}

+ 12 - 0
object/object.go

@@ -18,6 +18,7 @@ const (
 	ErrorObj       = "ERROR"
 	FunctionObj    = "FUNCTION"
 	StringObj      = "STRING"
+	BuiltinObj     = "BUILTIN"
 )
 
 // Object source code as an Object
@@ -26,6 +27,9 @@ type Object interface {
 	Inspect() string
 }
 
+// BuiltinFunction 内建函数数据
+type BuiltinFunction func(args ...Object) Object
+
 type Integer struct {
 	Value int64
 }
@@ -90,3 +94,11 @@ type String struct {
 
 func (s *String) Type() ObjType   { return StringObj }
 func (s *String) Inspect() string { return s.Value }
+
+type Builtin struct {
+	Fn BuiltinFunction
+}
+
+func (b Builtin) Type() ObjType { return BuiltinObj }
+
+func (b Builtin) Inspect() string { return "builtin function" }