Skip to content

Commit

Permalink
Correctly return all available methods on objects.
Browse files Browse the repository at this point in the history
This allows the following to work, as expected:

    let b = [].methods();

    let i = 0;
    for( i < len(b) ){
      puts( "\t" + b[i] + "\n");
      i++;
    }

Output:
        empty?
        filter
        find
        join
        len
        map
        methods
        reverse
        sort
        sorted?
        string
        swap

This closes #34.

(Bigger change than I'd like, as it required me to poll the environment
to get definitions, which meant passing the env. to invokeMethod.)
  • Loading branch information
skx committed Sep 30, 2018
1 parent ed91933 commit 500674c
Show file tree
Hide file tree
Showing 14 changed files with 127 additions and 22 deletions.
2 changes: 1 addition & 1 deletion evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
14 changes: 14 additions & 0 deletions object/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package object
import (
"fmt"
"os"
"strings"
)

// Environment stores our functions, variables, constants, etc.
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 14 additions & 2 deletions object/object_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package object

import (
"bytes"
"sort"
"strings"
)

Expand Down Expand Up @@ -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 {
Expand Down
21 changes: 18 additions & 3 deletions object/object_bool.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package object

import "fmt"
import (
"fmt"
"sort"
"strings"
)

// Boolean wraps bool and implements Object and Hashable interface.
type Boolean struct {
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion object/object_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}

Expand Down
2 changes: 1 addition & 1 deletion object/object_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 15 additions & 2 deletions object/object_float.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package object

import (
"hash/fnv"
"sort"
"strconv"
"strings"
)

// Float wraps float64 and implements Object and Hashable interfaces.
Expand Down Expand Up @@ -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 {
Expand Down
16 changes: 14 additions & 2 deletions object/object_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package object

import (
"bytes"
"sort"
"strings"

"github.com/skx/monkey/ast"
Expand Down Expand Up @@ -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 {
Expand Down
16 changes: 14 additions & 2 deletions object/object_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package object
import (
"bytes"
"fmt"
"sort"
"strings"
)

Expand Down Expand Up @@ -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 {
Expand Down
21 changes: 18 additions & 3 deletions object/object_int.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package object

import "fmt"
import (
"fmt"
"sort"
"strings"
)

// Integer wraps int64 and implements Object and Hashable interfaces.
type Integer struct {
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion object/object_null.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion object/object_return.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 14 additions & 2 deletions object/object_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package object

import (
"hash/fnv"
"sort"
"strings"
"unicode/utf8"
)
Expand Down Expand Up @@ -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()!"}
Expand All @@ -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 {
Expand Down

0 comments on commit 500674c

Please sign in to comment.