diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 33e65d2..96cd040 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -881,7 +881,7 @@ func evalObjectCallExpression(call *ast.ObjectCallExpression, env *object.Enviro // `invokeMethod` interface on the object. // args := evalExpression(call.Call.(*ast.CallExpression).Arguments, env) - ret := obj.InvokeMethod(method.Function.String(), args...) + ret := obj.InvokeMethod(method.Function.String(), *env, args...) if ret != nil { return ret } diff --git a/object/environment.go b/object/environment.go index bca6bf3..dadf6ec 100644 --- a/object/environment.go +++ b/object/environment.go @@ -3,6 +3,7 @@ package object import ( "fmt" "os" + "strings" ) // Environment stores our functions, variables, constants, etc. @@ -28,6 +29,19 @@ func NewEnclosedEnvironment(outer *Environment) *Environment { return env } +// Names returns the names of every known-value with the +// given prefix +func (e *Environment) Names(prefix string) []string { + var ret []string + + for key, _ := range e.store { + if strings.HasPrefix(key, prefix) { + ret = append(ret, key) + } + } + return ret +} + // Get returns the value of a given variable, by name. func (e *Environment) Get(name string) (Object, bool) { obj, ok := e.store[name] diff --git a/object/object.go b/object/object.go index e2048aa..a823f62 100644 --- a/object/object.go +++ b/object/object.go @@ -36,7 +36,7 @@ type Object interface { // InvokeMethod invokes a method against the object. // (Built-in methods only.) - InvokeMethod(method string, args ...Object) Object + InvokeMethod(method string, env Environment, args ...Object) Object } // Hashable type can be hashed diff --git a/object/object_array.go b/object/object_array.go index 3ec0ce0..853fd89 100644 --- a/object/object_array.go +++ b/object/object_array.go @@ -2,6 +2,7 @@ package object import ( "bytes" + "sort" "strings" ) @@ -44,12 +45,23 @@ func (ao *Array) Inspect() string { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (ao *Array) InvokeMethod(method string, args ...Object) Object { +func (ao *Array) InvokeMethod(method string, env Environment, args ...Object) Object { if method == "len" { return &Integer{Value: int64(len(ao.Elements))} } if method == "methods" { - names := []string{"len", "methods", "string"} + static := []string{"len", "methods", "string"} + dynamic := env.Names("array.") + + var names []string + for _, e := range static { + names = append(names, e) + } + for _, e := range dynamic { + bits := strings.Split(e, ".") + names = append(names, bits[1]) + } + sort.Strings(names) result := make([]Object, len(names), len(names)) for i, txt := range names { diff --git a/object/object_bool.go b/object/object_bool.go index d00f698..5840132 100644 --- a/object/object_bool.go +++ b/object/object_bool.go @@ -1,6 +1,10 @@ package object -import "fmt" +import ( + "fmt" + "sort" + "strings" +) // Boolean wraps bool and implements Object and Hashable interface. type Boolean struct { @@ -44,9 +48,20 @@ func (b *Boolean) HashKey() HashKey { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (b *Boolean) InvokeMethod(method string, args ...Object) Object { +func (b *Boolean) InvokeMethod(method string, env Environment, args ...Object) Object { if method == "methods" { - names := []string{"methods", "string", "type"} + static := []string{"methods", "string", "type"} + dynamic := env.Names("bool.") + + var names []string + for _, e := range static { + names = append(names, e) + } + for _, e := range dynamic { + bits := strings.Split(e, ".") + names = append(names, bits[1]) + } + sort.Strings(names) result := make([]Object, len(names), len(names)) for i, txt := range names { diff --git a/object/object_builtin.go b/object/object_builtin.go index bbcb575..41e09b6 100644 --- a/object/object_builtin.go +++ b/object/object_builtin.go @@ -34,7 +34,7 @@ func (b *Builtin) Inspect() string { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (b *Builtin) InvokeMethod(method string, args ...Object) Object { +func (b *Builtin) InvokeMethod(method string, env Environment, args ...Object) Object { if method == "methods" { names := []string{"methods", "type"} diff --git a/object/object_error.go b/object/object_error.go index 4c60d83..69e709a 100644 --- a/object/object_error.go +++ b/object/object_error.go @@ -31,7 +31,7 @@ func (e *Error) Inspect() string { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (e *Error) InvokeMethod(method string, args ...Object) Object { +func (e *Error) InvokeMethod(method string, env Environment, args ...Object) Object { // // There are no methods available upon a return-object. diff --git a/object/object_float.go b/object/object_float.go index ea16415..028ec8d 100644 --- a/object/object_float.go +++ b/object/object_float.go @@ -2,7 +2,9 @@ package object import ( "hash/fnv" + "sort" "strconv" + "strings" ) // Float wraps float64 and implements Object and Hashable interfaces. @@ -43,9 +45,20 @@ func (f *Float) HashKey() HashKey { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (f *Float) InvokeMethod(method string, args ...Object) Object { +func (f *Float) InvokeMethod(method string, env Environment, args ...Object) Object { if method == "methods" { - names := []string{"methods", "string", "type"} + static := []string{"methods", "string", "type"} + dynamic := env.Names("float.") + + var names []string + for _, e := range static { + names = append(names, e) + } + for _, e := range dynamic { + bits := strings.Split(e, ".") + names = append(names, bits[1]) + } + sort.Strings(names) result := make([]Object, len(names), len(names)) for i, txt := range names { diff --git a/object/object_function.go b/object/object_function.go index 6235645..183e0c5 100644 --- a/object/object_function.go +++ b/object/object_function.go @@ -2,6 +2,7 @@ package object import ( "bytes" + "sort" "strings" "github.com/skx/monkey/ast" @@ -49,9 +50,20 @@ func (f *Function) Inspect() string { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (f *Function) InvokeMethod(method string, args ...Object) Object { +func (f *Function) InvokeMethod(method string, env Environment, args ...Object) Object { if method == "methods" { - names := []string{"methods", "type"} + static := []string{"methods", "type"} + dynamic := env.Names("function.") + + var names []string + for _, e := range static { + names = append(names, e) + } + for _, e := range dynamic { + bits := strings.Split(e, ".") + names = append(names, bits[1]) + } + sort.Strings(names) result := make([]Object, len(names), len(names)) for i, txt := range names { diff --git a/object/object_hash.go b/object/object_hash.go index 1980af3..07525d3 100644 --- a/object/object_hash.go +++ b/object/object_hash.go @@ -3,6 +3,7 @@ package object import ( "bytes" "fmt" + "sort" "strings" ) @@ -64,9 +65,20 @@ func (h *Hash) Inspect() string { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (h *Hash) InvokeMethod(method string, args ...Object) Object { +func (h *Hash) InvokeMethod(method string, env Environment, args ...Object) Object { if method == "methods" { - names := []string{"keys", "methods", "string", "type"} + static := []string{"keys", "methods", "string", "type"} + dynamic := env.Names("hash.") + + var names []string + for _, e := range static { + names = append(names, e) + } + for _, e := range dynamic { + bits := strings.Split(e, ".") + names = append(names, bits[1]) + } + sort.Strings(names) result := make([]Object, len(names), len(names)) for i, txt := range names { diff --git a/object/object_int.go b/object/object_int.go index da22045..728565a 100644 --- a/object/object_int.go +++ b/object/object_int.go @@ -1,6 +1,10 @@ package object -import "fmt" +import ( + "fmt" + "sort" + "strings" +) // Integer wraps int64 and implements Object and Hashable interfaces. type Integer struct { @@ -38,12 +42,23 @@ func (i *Integer) HashKey() HashKey { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (i *Integer) InvokeMethod(method string, args ...Object) Object { +func (i *Integer) InvokeMethod(method string, env Environment, args ...Object) Object { if method == "chr" { return &String{Value: string(i.Value)} } if method == "methods" { - names := []string{"chr", "methods", "string", "type"} + static := []string{"chr", "methods", "string", "type"} + dynamic := env.Names("integer.") + + var names []string + for _, e := range static { + names = append(names, e) + } + for _, e := range dynamic { + bits := strings.Split(e, ".") + names = append(names, bits[1]) + } + sort.Strings(names) result := make([]Object, len(names), len(names)) for i, txt := range names { diff --git a/object/object_null.go b/object/object_null.go index 179fa20..561dbc9 100644 --- a/object/object_null.go +++ b/object/object_null.go @@ -26,6 +26,6 @@ func (n *Null) Inspect() string { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (n *Null) InvokeMethod(method string, args ...Object) Object { +func (n *Null) InvokeMethod(method string, env Environment, args ...Object) Object { return nil } diff --git a/object/object_return.go b/object/object_return.go index 1859bb6..69eb33f 100644 --- a/object/object_return.go +++ b/object/object_return.go @@ -31,7 +31,7 @@ func (rv *ReturnValue) Inspect() string { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (rv *ReturnValue) InvokeMethod(method string, args ...Object) Object { +func (rv *ReturnValue) InvokeMethod(method string, env Environment, args ...Object) Object { // // There are no methods available upon a return-object. diff --git a/object/object_string.go b/object/object_string.go index 8917101..d5bff52 100644 --- a/object/object_string.go +++ b/object/object_string.go @@ -4,6 +4,7 @@ package object import ( "hash/fnv" + "sort" "strings" "unicode/utf8" ) @@ -46,7 +47,7 @@ func (s *String) HashKey() HashKey { // InvokeMethod invokes a method against the object. // (Built-in methods only.) -func (s *String) InvokeMethod(method string, args ...Object) Object { +func (s *String) InvokeMethod(method string, env Environment, args ...Object) Object { if method == "count" { if len(args) < 1 { return &Error{Message: "Missing argument to count()!"} @@ -68,7 +69,18 @@ func (s *String) InvokeMethod(method string, args ...Object) Object { return &Integer{Value: int64(utf8.RuneCountInString(s.Value))} } if method == "methods" { - names := []string{"count", "find", "len", "methods", "replace", "split", "type"} + static := []string{"count", "find", "len", "methods", "ord", "replace", "split", "type"} + dynamic := env.Names("string.") + + var names []string + for _, e := range static { + names = append(names, e) + } + for _, e := range dynamic { + bits := strings.Split(e, ".") + names = append(names, bits[1]) + } + sort.Strings(names) result := make([]Object, len(names), len(names)) for i, txt := range names {