builtins
This commit is contained in:
49
vm/vm.go
49
vm/vm.go
@@ -206,7 +206,7 @@ func (vm *VM) Run() error {
|
||||
numArgs := code.ReadUint8(ins[ip+1:])
|
||||
vm.currentFrame().ip += 1
|
||||
|
||||
err := vm.callFunction(int(numArgs))
|
||||
err := vm.executeCall(int(numArgs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -249,6 +249,17 @@ func (vm *VM) Run() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpGetBuiltin:
|
||||
builtinIndex := code.ReadUint8(ins[ip+1:])
|
||||
vm.currentFrame().ip += 1
|
||||
|
||||
definition := object.Builtins[builtinIndex]
|
||||
|
||||
err := vm.push(definition.Builtin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,12 +476,19 @@ func (vm *VM) executeMinusOperator() error {
|
||||
return vm.push(&object.Integer{Value: -value})
|
||||
}
|
||||
|
||||
func (vm *VM) callFunction(numArgs int) error {
|
||||
fn, ok := vm.stack[vm.sp-1-numArgs].(*object.CompiledFunction)
|
||||
if !ok {
|
||||
return fmt.Errorf("calling non-function")
|
||||
func (vm *VM) executeCall(numArgs int) error {
|
||||
callee := vm.stack[vm.sp-1-numArgs]
|
||||
switch callee := callee.(type) {
|
||||
case *object.CompiledFunction:
|
||||
return vm.callFunction(callee, numArgs)
|
||||
case *object.Builtin:
|
||||
return vm.callBuiltin(callee, numArgs)
|
||||
default:
|
||||
return fmt.Errorf("calling non-function and non-built-in")
|
||||
}
|
||||
}
|
||||
|
||||
func (vm *VM) callFunction(fn *object.CompiledFunction, numArgs int) error {
|
||||
if numArgs != fn.NumParameters {
|
||||
return fmt.Errorf("wrong number of arguments: want=%d, got=%d", fn.NumParameters, numArgs)
|
||||
}
|
||||
@@ -482,6 +500,27 @@ func (vm *VM) callFunction(numArgs int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
|
||||
args := vm.stack[vm.sp-numArgs : vm.sp]
|
||||
|
||||
result := builtin.Fn(args...)
|
||||
vm.sp = vm.sp - numArgs - 1
|
||||
|
||||
if result != nil {
|
||||
err := vm.push(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := vm.push(Null)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func nativeBoolToBooleanObject(input bool) *object.Boolean {
|
||||
if input {
|
||||
return True
|
||||
|
||||
@@ -106,6 +106,16 @@ func testExpectedObject(t *testing.T, expected interface{}, actual object.Object
|
||||
if actual != Null {
|
||||
t.Errorf("object is not Null: %T (%+v)", actual, actual)
|
||||
}
|
||||
|
||||
case *object.Error:
|
||||
errObj, ok := actual.(*object.Error)
|
||||
if !ok {
|
||||
t.Errorf("object is not Error: %T (%+v)", actual, actual)
|
||||
return
|
||||
}
|
||||
if errObj.Message != expected.Message {
|
||||
t.Errorf("wrong error message. expected=%q, got=%q", expected.Message, errObj.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,3 +565,49 @@ func TestCallingFunctionsWithWrongArguments(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuiltinFunctions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{`len("")`, 0},
|
||||
{`len("four")`, 4},
|
||||
{`len("hello world")`, 11},
|
||||
{
|
||||
`len(1)`,
|
||||
&object.Error{
|
||||
Message: "argument to `len` not supported, got INTEGER",
|
||||
},
|
||||
},
|
||||
{`len("one", "two")`,
|
||||
&object.Error{
|
||||
Message: "wrong number of arguments. got=2, want=1",
|
||||
},
|
||||
},
|
||||
{`len([1, 2, 3])`, 3},
|
||||
{`len([])`, 0},
|
||||
{`puts("hello", "world!")`, Null},
|
||||
{`first([1, 2, 3])`, 1},
|
||||
{`first([])`, Null},
|
||||
{`first(1)`,
|
||||
&object.Error{
|
||||
Message: "argument to `first` must be ARRAY, got INTEGER",
|
||||
},
|
||||
},
|
||||
{`last([1, 2, 3])`, 3},
|
||||
{`last([])`, Null},
|
||||
{`last(1)`,
|
||||
&object.Error{
|
||||
Message: "argument to `last` must be ARRAY, got INTEGER",
|
||||
},
|
||||
},
|
||||
{`rest([1, 2, 3])`, []int{2, 3}},
|
||||
{`rest([])`, Null},
|
||||
{`push([], 1)`, []int{1}},
|
||||
{`push(1, 1)`,
|
||||
&object.Error{
|
||||
Message: "argument to `push` must be ARRAY, got INTEGER",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user