diff --git a/builtins/builtins.go b/builtins/builtins.go index 284bcaf..0e4186a 100644 --- a/builtins/builtins.go +++ b/builtins/builtins.go @@ -8,39 +8,43 @@ import ( // Builtins ... var Builtins = map[string]*object.Builtin{ - "len": {Name: "len", Fn: Len}, - "input": {Name: "input", Fn: Input}, - "print": {Name: "print", Fn: Print}, - "first": {Name: "first", Fn: First}, - "last": {Name: "last", Fn: Last}, - "rest": {Name: "rest", Fn: Rest}, - "push": {Name: "push", Fn: Push}, - "pop": {Name: "pop", Fn: Pop}, - "exit": {Name: "exit", Fn: Exit}, - "assert": {Name: "assert", Fn: Assert}, - "bool": {Name: "bool", Fn: Bool}, - "int": {Name: "int", Fn: Int}, - "str": {Name: "str", Fn: Str}, - "type": {Name: "type", Fn: TypeOf}, - "args": {Name: "args", Fn: Args}, - "lower": {Name: "lower", Fn: Lower}, - "upper": {Name: "upper", Fn: Upper}, - "join": {Name: "join", Fn: Join}, - "split": {Name: "split", Fn: Split}, - "find": {Name: "find", Fn: Find}, - "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}, + "len": {Name: "len", Fn: Len}, + "input": {Name: "input", Fn: Input}, + "print": {Name: "print", Fn: Print}, + "first": {Name: "first", Fn: First}, + "last": {Name: "last", Fn: Last}, + "rest": {Name: "rest", Fn: Rest}, + "push": {Name: "push", Fn: Push}, + "pop": {Name: "pop", Fn: Pop}, + "exit": {Name: "exit", Fn: Exit}, + "assert": {Name: "assert", Fn: Assert}, + "bool": {Name: "bool", Fn: Bool}, + "int": {Name: "int", Fn: Int}, + "str": {Name: "str", Fn: Str}, + "type": {Name: "type", Fn: TypeOf}, + "args": {Name: "args", Fn: Args}, + "lower": {Name: "lower", Fn: Lower}, + "upper": {Name: "upper", Fn: Upper}, + "join": {Name: "join", Fn: Join}, + "split": {Name: "split", Fn: Split}, + "find": {Name: "find", Fn: Find}, + "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}, + "min": {Name: "min", Fn: Min}, + "max": {Name: "max", Fn: Max}, + "sorted": {Name: "sorted", Fn: Sorted}, + "reversed": {Name: "reversed", Fn: Reversed}, } // BuiltinsIndex ... diff --git a/builtins/max.go b/builtins/max.go new file mode 100644 index 0000000..0267762 --- /dev/null +++ b/builtins/max.go @@ -0,0 +1,29 @@ +package builtins + +import ( + "monkey/object" + "sort" +) + +// Max ... +func Max(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if a, ok := args[0].(*object.Array); ok { + // TODO: Make this more generic + xs := make([]int, len(a.Elements)) + for n, e := range a.Elements { + if i, ok := e.(*object.Integer); ok { + xs = append(xs, int(i.Value)) + } else { + return newError("item #%d not an `int` got=%s", n, e.Type()) + } + } + sort.Ints(xs) + return &object.Integer{Value: int64(xs[len(xs)-1])} + } + return newError("argument #1 to `max` expected to be `array` got=%T", args[0].Type()) +} diff --git a/builtins/min.go b/builtins/min.go new file mode 100644 index 0000000..e9aa539 --- /dev/null +++ b/builtins/min.go @@ -0,0 +1,29 @@ +package builtins + +import ( + "monkey/object" + "sort" +) + +// Min ... +func Min(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if a, ok := args[0].(*object.Array); ok { + // TODO: Make this more generic + xs := make([]int, len(a.Elements)) + for n, e := range a.Elements { + if i, ok := e.(*object.Integer); ok { + xs = append(xs, int(i.Value)) + } else { + return newError("item #%d not an `int` got=%s", n, e.Type()) + } + } + sort.Ints(xs) + return &object.Integer{Value: int64(xs[0])} + } + return newError("argument #1 to `min` expected to be `array` got=%T", args[0].Type()) +} diff --git a/builtins/reversed.go b/builtins/reversed.go new file mode 100644 index 0000000..bb33a4d --- /dev/null +++ b/builtins/reversed.go @@ -0,0 +1,20 @@ +package builtins + +import ( + "monkey/object" +) + +// Reversed ... +func Reversed(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if a, ok := args[0].(*object.Array); ok { + newArray := a.Copy() + newArray.Reverse() + return newArray + } + return newError("argument #1 to `reversed` expected to be `array` got=%T", args[0].Type()) +} diff --git a/builtins/sorted.go b/builtins/sorted.go new file mode 100644 index 0000000..aabfed3 --- /dev/null +++ b/builtins/sorted.go @@ -0,0 +1,21 @@ +package builtins + +import ( + "monkey/object" + "sort" +) + +// Sorted ... +func Sorted(args ...object.Object) object.Object { + if len(args) != 1 { + return newError("wrong number of arguments. got=%d, want=1", + len(args)) + } + + if a, ok := args[0].(*object.Array); ok { + newArray := a.Copy() + sort.Sort(newArray) + return newArray + } + return newError("argument #1 to `sorted` expected to be `array` got=%T", args[0].Type()) +} diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 35c25e9..8f874d4 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -903,7 +903,7 @@ func TestBuiltins(t *testing.T) { code.Make(code.OpArray, 0), code.Make(code.OpCall, 1), code.Make(code.OpPop), - code.Make(code.OpGetBuiltin, 25), + code.Make(code.OpGetBuiltin, 27), code.Make(code.OpArray, 0), code.Make(code.OpConstant, 0), code.Make(code.OpCall, 2), diff --git a/main.go b/main.go index b6ae08b..3e2a40e 100644 --- a/main.go +++ b/main.go @@ -75,6 +75,9 @@ func main() { log.Fatal("no source file given to compile") } f, err := os.Open(args[0]) + if err != nil { + log.Fatal(err) + } defer f.Close() b, err := io.ReadAll(f) diff --git a/object/array.go b/object/array.go index 48adf00..4256369 100644 --- a/object/array.go +++ b/object/array.go @@ -49,3 +49,31 @@ func (ao *Array) Equal(other Object) bool { } return false } +func (ao *Array) Copy() *Array { + elements := make([]Object, len(ao.Elements)) + for i, e := range ao.Elements { + elements[i] = e + } + return &Array{Elements: elements} +} + +func (ao *Array) Reverse() { + for i, j := 0, len(ao.Elements)-1; i < j; i, j = i+1, j-1 { + ao.Elements[i], ao.Elements[j] = ao.Elements[j], ao.Elements[i] + } +} + +func (ao *Array) Len() int { + return len(ao.Elements) +} + +func (ao *Array) Swap(i, j int) { + ao.Elements[i], ao.Elements[j] = ao.Elements[j], ao.Elements[i] +} + +func (ao *Array) Less(i, j int) bool { + if cmp, ok := ao.Elements[i].(Comparable); ok { + return cmp.Less(ao.Elements[j]) + } + return false +} diff --git a/object/bool.go b/object/bool.go index 55fc12d..558a171 100644 --- a/object/bool.go +++ b/object/bool.go @@ -26,3 +26,16 @@ func (b *Boolean) Equal(other Object) bool { } return false } +func (b *Boolean) Int() int64 { + if b.Value { + return 1 + } + return 0 +} + +func (b *Boolean) Less(other Object) bool { + if obj, ok := other.(*Boolean); ok { + return b.Int() < obj.Int() + } + return false +} diff --git a/object/int.go b/object/int.go index fdd7354..a9453c6 100644 --- a/object/int.go +++ b/object/int.go @@ -24,3 +24,9 @@ func (i *Integer) Equal(other Object) bool { } return false } +func (i *Integer) Less(other Object) bool { + if obj, ok := other.(*Integer); ok { + return i.Value < obj.Value + } + return true +} diff --git a/object/null.go b/object/null.go index e693fea..ac2c162 100644 --- a/object/null.go +++ b/object/null.go @@ -15,3 +15,6 @@ func (n *Null) Equal(other Object) bool { _, ok := other.(*Null) return ok } +func (n *Null) Less(other Object) bool { + return false +} diff --git a/object/object.go b/object/object.go index a58c931..3b6e9bf 100644 --- a/object/object.go +++ b/object/object.go @@ -23,6 +23,7 @@ const ( // Returns `true` iif the types and values are identical, `false` otherwise. type Comparable interface { Equal(other Object) bool + Less(other Object) bool } // Immutable is the interface for all immutable objects which must implement diff --git a/object/str.go b/object/str.go index 815903d..a5202f4 100644 --- a/object/str.go +++ b/object/str.go @@ -24,3 +24,9 @@ func (s *String) Equal(other Object) bool { } return false } +func (s *String) Less(other Object) bool { + if obj, ok := other.(*String); ok { + return s.Value < obj.Value + } + return false +}