diff --git a/builtins/abs.go b/builtins/abs.go new file mode 100644 index 0000000..eeb8399 --- /dev/null +++ b/builtins/abs.go @@ -0,0 +1,20 @@ +package builtins + +import "monkey/object" + +// Abs ... +func Abs(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if i, ok := args[0].(*object.Integer); ok { + value := i.Value + if value < 0 { + value = value * -1 + } + return &object.Integer{Value: value} + } + return newError("argument to `abs` not supported, got %s", args[0].Type()) +} diff --git a/builtins/bin.go b/builtins/bin.go new file mode 100644 index 0000000..e7ae87e --- /dev/null +++ b/builtins/bin.go @@ -0,0 +1,20 @@ +package builtins + +import ( + "fmt" + "monkey/object" + "strconv" +) + +// Bin ... +func Bin(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if i, ok := args[0].(*object.Integer); ok { + return &object.String{Value: fmt.Sprintf("0b%s", strconv.FormatInt(i.Value, 2))} + } + return newError("argument to `bin` not supported, got %s", args[0].Type()) +} diff --git a/builtins/builtins.go b/builtins/builtins.go index 5ecfeda..284bcaf 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -31,6 +31,16 @@ var Builtins = map[string]*object.Builtin{ "read": {Name: "read", Fn: Read}, "write": {Name: "write", Fn: Write}, "ffi": {Name: "ffi", Fn: FFI}, + "abs": {Name: "abs", Fn: Abs}, + "bin": {Name: "bin", Fn: Bin}, + "hex": {Name: "hex", Fn: Hex}, + "ord": {Name: "ord", Fn: Ord}, + "chr": {Name: "chr", Fn: Chr}, + "divmod": {Name: "divmod", Fn: Divmod}, + "hash": {Name: "hash", Fn: HashOf}, + "id": {Name: "id", Fn: IdOf}, + "oct": {Name: "oct", Fn: Oct}, + "pow": {Name: "pow", Fn: Pow}, } // BuiltinsIndex ... diff --git a/builtins/chr.go b/builtins/chr.go new file mode 100644 index 0000000..6619110 --- /dev/null +++ b/builtins/chr.go @@ -0,0 +1,19 @@ +package builtins + +import ( + "fmt" + "monkey/object" +) + +// Chr ... +func Chr(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if i, ok := args[0].(*object.Integer); ok { + return &object.String{Value: fmt.Sprintf("%c", rune(i.Value))} + } + return newError("argument to `chr` not supported, got %s", args[0].Type()) +} diff --git a/builtins/divmod.go b/builtins/divmod.go new file mode 100644 index 0000000..01701b1 --- /dev/null +++ b/builtins/divmod.go @@ -0,0 +1,24 @@ +package builtins + +import "monkey/object" + +// Divmod ... +func Divmod(args ...object.Object) object.Object { + if len(args) != 2 { + return newError("wrong number of arguments. got=%d, want=2", + len(args)) + } + + if a, ok := args[0].(*object.Integer); ok { + if b, ok := args[1].(*object.Integer); ok { + elements := make([]object.Object, 2) + elements[0] = &object.Integer{Value: a.Value / b.Value} + elements[1] = &object.Integer{Value: a.Value % b.Value} + return &object.Array{Elements: elements} + } else { + return newError("expected argument #2 to `divmod` to be `int` got=%s", args[1].Type()) + } + } else { + return newError("expected argument #1 to `divmod` to be `int` got=%s", args[0].Type()) + } +} diff --git a/builtins/hash.go b/builtins/hash.go new file mode 100644 index 0000000..c13a170 --- /dev/null +++ b/builtins/hash.go @@ -0,0 +1,16 @@ +package builtins + +import "monkey/object" + +// HashOf ... +func HashOf(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if hash, ok := args[0].(object.Hashable); ok { + return &object.Integer{Value: int64(hash.HashKey().Value)} + } + return newError("argument #1 to `hash()` is not hashable: %s", args[0].Inspect()) +} diff --git a/builtins/hex.go b/builtins/hex.go new file mode 100644 index 0000000..952ce1d --- /dev/null +++ b/builtins/hex.go @@ -0,0 +1,20 @@ +package builtins + +import ( + "fmt" + "monkey/object" + "strconv" +) + +// Hex ... +func Hex(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if i, ok := args[0].(*object.Integer); ok { + return &object.String{Value: fmt.Sprintf("0x%s", strconv.FormatInt(i.Value, 16))} + } + return newError("argument to `hex` not supported, got %s", args[0].Type()) +} diff --git a/builtins/id.go b/builtins/id.go new file mode 100644 index 0000000..7214ac0 --- /dev/null +++ b/builtins/id.go @@ -0,0 +1,38 @@ +package builtins + +import ( + "fmt" + "monkey/object" +) + +// IdOf ... +func IdOf(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + arg := args[0] + + if n, ok := arg.(*object.Null); ok { + return &object.String{Value: fmt.Sprintf("%p", n)} + } else if b, ok := arg.(*object.Boolean); ok { + return &object.String{Value: fmt.Sprintf("%p", b)} + } else if i, ok := arg.(*object.Integer); ok { + return &object.String{Value: fmt.Sprintf("%p", i)} + } else if s, ok := arg.(*object.String); ok { + return &object.String{Value: fmt.Sprintf("%p", s)} + } else if a, ok := arg.(*object.Array); ok { + return &object.String{Value: fmt.Sprintf("%p", a)} + } else if h, ok := arg.(*object.Hash); ok { + return &object.String{Value: fmt.Sprintf("%p", h)} + } else if f, ok := arg.(*object.Function); ok { + return &object.String{Value: fmt.Sprintf("%p", f)} + } else if c, ok := arg.(*object.Closure); ok { + return &object.String{Value: fmt.Sprintf("%p", c)} + } else if b, ok := arg.(*object.Builtin); ok { + return &object.String{Value: fmt.Sprintf("%p", b)} + } else { + return newError("argument 31 to `id()` unsupported got=%T", arg.Type()) + } +} diff --git a/builtins/oct.go b/builtins/oct.go new file mode 100644 index 0000000..e107ad8 --- /dev/null +++ b/builtins/oct.go @@ -0,0 +1,20 @@ +package builtins + +import ( + "fmt" + "monkey/object" + "strconv" +) + +// Oct ... +func Oct(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if i, ok := args[0].(*object.Integer); ok { + return &object.String{Value: fmt.Sprintf("0%s", strconv.FormatInt(i.Value, 8))} + } + return newError("argument to `oct` not supported, got %s", args[0].Type()) +} diff --git a/builtins/ord.go b/builtins/ord.go new file mode 100644 index 0000000..0dc21f4 --- /dev/null +++ b/builtins/ord.go @@ -0,0 +1,19 @@ +package builtins + +import "monkey/object" + +// Ord ... +func Ord(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if s, ok := args[0].(*object.String); ok { + if len(s.Value) == 1 { + return &object.Integer{Value: int64(s.Value[0])} + } + return newError("`ord()` expected a character but got string of length %d", len(s.Value)) + } + return newError("argument to `ord` not supported, got %s", args[0].Type()) +} diff --git a/builtins/pow.go b/builtins/pow.go new file mode 100644 index 0000000..63f02c8 --- /dev/null +++ b/builtins/pow.go @@ -0,0 +1,34 @@ +package builtins + +import "monkey/object" + +func pow(x, y int64) int64 { + p := int64(1) + for y > 0 { + if y&1 != 0 { + p *= x + } + y >>= 1 + x *= x + } + return p +} + +// Pow ... +func Pow(args ...object.Object) object.Object { + if len(args) != 2 { + return newError("wrong number of arguments. got=%d, want=2", + len(args)) + } + + if x, ok := args[0].(*object.Integer); ok { + if y, ok := args[1].(*object.Integer); ok { + value := pow(x.Value, y.Value) + return &object.Integer{Value: value} + } else { + return newError("expected argument #2 to `pow` to be `int` got=%s", args[1].Type()) + } + } else { + return newError("expected argument #1 to `pow` to be `int` got=%s", args[0].Type()) + } +} diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index cc920dc..f030019 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -879,11 +879,11 @@ func TestBuiltins(t *testing.T) { `, expectedConstants: []interface{}{1}, expectedInstructions: []code.Instructions{ - code.Make(code.OpGetBuiltin, 11), + code.Make(code.OpGetBuiltin, 18), code.Make(code.OpArray, 0), code.Make(code.OpCall, 1), code.Make(code.OpPop), - code.Make(code.OpGetBuiltin, 15), + code.Make(code.OpGetBuiltin, 25), code.Make(code.OpArray, 0), code.Make(code.OpConstant, 0), code.Make(code.OpCall, 2), @@ -894,7 +894,7 @@ func TestBuiltins(t *testing.T) { input: `fn() { return len([]) }`, expectedConstants: []interface{}{ []code.Instructions{ - code.Make(code.OpGetBuiltin, 11), + code.Make(code.OpGetBuiltin, 18), code.Make(code.OpArray, 0), code.Make(code.OpCall, 1), code.Make(code.OpReturn),