builtins
Some checks failed
Build / build (push) Failing after 1h2m55s
Test / build (push) Failing after 29m38s

This commit is contained in:
Chuck Smith
2024-03-12 16:35:24 -04:00
parent 1d2c7f0a51
commit e373e9f68a
11 changed files with 354 additions and 125 deletions

View File

@@ -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

View File

@@ -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)
}