Skip to content

Commit

Permalink
Add more builtin functions
Browse files Browse the repository at this point in the history
  • Loading branch information
kitasuke committed Nov 11, 2018
1 parent 194cbbc commit edc8d6c
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 3 deletions.
81 changes: 79 additions & 2 deletions evaluator/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ package evaluator
import "github.com/kitasuke/monkey-go/object"

const (
BuiltinFuncNameLen = "len"
BuiltinFuncNameLen = "len"
BuiltinFuncNameFirst = "first"
BuiltinFuncNameLast = "last"
BuiltinFuncNameRest = "rest"
BuiltinFuncNamePush = "push"
)

var builtins = map[string]*object.Builtin{
Expand All @@ -14,11 +18,84 @@ var builtins = map[string]*object.Builtin{
}

switch arg := args[0].(type) {
case *object.Array:
return &object.Integer{Value: int64(len(arg.Elements))}
case *object.String:
return &object.Integer{Value: int64(len(arg.Value))}
default:
return newError("argument to `len` not supported, got %s", args[0].Type())
return newError("argument to %q not supported, got %s", BuiltinFuncNameLen, args[0].Type())
}
},
},
BuiltinFuncNameFirst: {
Fn: func(args ...object.Object) object.Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args))
}
if args[0].Type() != object.ArrayObj {
return newError("argument to %q must be %s, got %s", BuiltinFuncNameFirst, object.ArrayObj, args[0].Type())
}

arr := args[0].(*object.Array)
if len(arr.Elements) > 0 {
return arr.Elements[0]
}
return Null
},
},
BuiltinFuncNameLast: {
Fn: func(args ...object.Object) object.Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args))
}
if args[0].Type() != object.ArrayObj {
return newError("argument to %q must be %s, got %s", BuiltinFuncNameLast, object.ArrayObj, args[0].Type())
}

arr := args[0].(*object.Array)
length := len(arr.Elements)
if length > 0 {
return arr.Elements[length-1]
}
return Null
},
},
BuiltinFuncNameRest: {
Fn: func(args ...object.Object) object.Object {
if len(args) != 1 {
return newError("wrong number of arguments. got=%d, want=1", len(args))
}
if args[0].Type() != object.ArrayObj {
return newError("argument to %q must be %s, got %s", BuiltinFuncNameRest, object.ArrayObj, args[0].Type())
}

arr := args[0].(*object.Array)
length := len(arr.Elements)
if length > 0 {
newElements := make([]object.Object, length-1, length-1)
copy(newElements, arr.Elements[1:length])
return &object.Array{Elements: newElements}
}
return Null
},
},
BuiltinFuncNamePush: {
Fn: func(args ...object.Object) object.Object {
if len(args) != 2 {
return newError("wrong number of arguments. got=%d, want=2", len(args))
}
if args[0].Type() != object.ArrayObj {
return newError("argument to %q must be %s, got %s", BuiltinFuncNamePush, object.ArrayObj, args[0].Type())
}

arr := args[0].(*object.Array)
length := len(arr.Elements)

newElements := make([]object.Object, length+1, length+1)
copy(newElements, arr.Elements)
newElements[length] = args[1]

return &object.Array{Elements: newElements}
},
},
}
34 changes: 33 additions & 1 deletion evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,23 @@ func TestBuiltinFunctions(t *testing.T) {
{`len("")`, 0},
{`len("four")`, 4},
{`len("hello world")`, 11},
{`len(1)`, fmt.Sprintf("argument to `len` not supported, got %s", object.IntegerObj)},
{`len(1)`, fmt.Sprintf("argument to %q not supported, got %s", BuiltinFuncNameLen, object.IntegerObj)},
{`len("one", "two")`, "wrong number of arguments. got=2, want=1"},
{`first([1, 2, 3])`, 1},
{`first([])`, nil},
{`first(1)`, fmt.Sprintf("argument to %q must be %s, got %s", BuiltinFuncNameFirst, object.ArrayObj, object.IntegerObj)},
{`first(1, 2)`, "wrong number of arguments. got=2, want=1"},
{`last([1, 2, 3])`, 3},
{`last([])`, nil},
{`last(1)`, fmt.Sprintf("argument to %q must be %s, got %s", BuiltinFuncNameLast, object.ArrayObj, object.IntegerObj)},
{`last(1, 2)`, "wrong number of arguments. got=2, want=1"},
{`rest([1, 2, 3])`, []int{2, 3}},
{`rest([])`, nil},
{`rest(1)`, fmt.Sprintf("argument to %q must be %s, got %s", BuiltinFuncNameRest, object.ArrayObj, object.IntegerObj)},
{`rest(1, 2)`, "wrong number of arguments. got=2, want=1"},
{`push([], 1)`, []int{1}},
{`push(1, 2)`, fmt.Sprintf("argument to %q must be %s, got %s", BuiltinFuncNamePush, object.ArrayObj, object.IntegerObj)},
{`push(1)`, "wrong number of arguments. got=1, want=2"},
}

for _, tt := range tests {
Expand All @@ -279,6 +294,8 @@ func TestBuiltinFunctions(t *testing.T) {
switch expected := tt.expected.(type) {
case int:
testIntegerObject(t, evaluated, int64(expected))
case nil:
testNullObject(t, evaluated)
case string:
errObj, ok := evaluated.(*object.Error)
if !ok {
Expand All @@ -289,6 +306,21 @@ func TestBuiltinFunctions(t *testing.T) {
if errObj.Message != expected {
t.Errorf("wrong error message. expected=%q, got=%q", expected, errObj.Message)
}
case []int:
array, ok := evaluated.(*object.Array)
if !ok {
t.Errorf("obj not %s. got=%T (%+v)", object.ArrayObj, evaluated, evaluated)
continue
}
if len(array.Elements) != len(expected) {
t.Errorf("wrong num of elements. want=%d, got=%d",
len(expected), len(array.Elements))
continue
}

for i, element := range expected {
testIntegerObject(t, array.Elements[i], int64(element))
}
}
}
}
Expand Down

0 comments on commit edc8d6c

Please # to comment.