Skip to content

Commit

Permalink
object: add ObjectMethod structure to validate call arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkusFreitag committed Dec 19, 2021
1 parent 4a7322a commit 112f974
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 24 deletions.
75 changes: 75 additions & 0 deletions object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package object

import (
"bytes"
"fmt"
"strings"

"github.com/flipez/rocket-lang/ast"
Expand Down Expand Up @@ -88,3 +89,77 @@ func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() }
func (rv *ReturnValue) InvokeMethod(method string, env Environment, args ...Object) Object {
return nil
}

type ObjectMethod struct {
argOverloading bool
argPattern [][]string
method func(*String, []Object) Object
}

func (om *ObjectMethod) validateArgs(args []Object) error {
if len(args) < len(om.argPattern) {
return fmt.Errorf("ZU WENIG INFOS")
}

if len(args) > len(om.argPattern) && !om.argOverloading {
return fmt.Errorf("ZU VIELE INFOS")
}

for idx, pattern := range om.argPattern {
var valid bool
for _, argType := range pattern {
if ObjectType(argType) == args[idx].Type() {
valid = true
break
}
}
if !valid {
return fmt.Errorf("FALSCHER TYPE AUF POSITION %d got=%s, want=%s", idx, args[idx].Type(), strings.Join(pattern, "|"))
}
}

/* not needed for now, as there is no string method with flexible argument count
if om.argOverloading {
lastPattern := om.argPattern[len(om.argPattern)-1]
for idx, arg := range args[len(om.argPattern)-1:] {
var valid bool
for _, argType := range lastPattern {
if argType == args.Type() {
valid = true
break
}
}
if !valid {
return fmt.Errorf("FALSCHER TYPE AUF POSITION %d got=%s, want=%s", idx, arg.Type(), strings.Join(lastPattern, "|"))
}
}
}
*/

return nil
}

func (om *ObjectMethod) Usage(name string) Object {
var args string

if len(om.argPattern) > 0 {
types := make([]string, len(om.argPattern))
for idx, pattern := range om.argPattern {
types[idx] = strings.Join(pattern, "|")
}
args = strings.Join(types, ", ")

if om.argOverloading {
args += "..."
}
}

return &String{Value: fmt.Sprintf("%s(%s)", name, args)}
}

func (om *ObjectMethod) Call(s *String, args []Object) Object {
if err := om.validateArgs(args); err != nil {
return &Error{Message: err.Error()}
}
return om.method(s, args)
}
63 changes: 39 additions & 24 deletions object/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,52 @@ type String struct {
Value string
}

var stringObjectMethods = map[string]ObjectMethod{
"count": ObjectMethod{
argPattern: [][]string{
[]string{STRING_OBJ, INTEGER_OBJ}, // first argument can be string or int
},
method: func(s *String, args []Object) Object {
arg := args[0].Inspect()
return &Integer{Value: int64(strings.Count(s.Value, arg))}
},
},

"find": ObjectMethod{
argPattern: [][]string{
[]string{STRING_OBJ, INTEGER_OBJ}, // first argument can be string or int
},
method: func(s *String, args []Object) Object {
arg := args[0].Inspect()
return &Integer{Value: int64(strings.Index(s.Value, arg))}
},
},

"size": ObjectMethod{
method: func(s *String, _ []Object) Object {
return &Integer{Value: int64(utf8.RuneCountInString(s.Value))}
},
},
}

func (s *String) Type() ObjectType { return STRING_OBJ }
func (s *String) Inspect() string { return s.Value }
func (s *String) InvokeMethod(method string, env Environment, args ...Object) Object {
switch method {
case "count":
if len(args) < 1 {
return &Error{Message: "Missing argument to count()!"}
}
// Note that this coerces into a string :)
arg := args[0].Inspect()
return &Integer{Value: int64(strings.Count(s.Value, arg))}
case "find":
if len(args) < 1 {
return &Error{Message: "Missing argument to find()!"}
case "methods":
result := make([]Object, len(stringObjectMethods), len(stringObjectMethods))
var i int
for name := range stringObjectMethods {
result[i] = &String{Value: name}
i++
}

// Note that this coerces into a string :)
arg := args[0].Inspect()
return &Integer{Value: int64(strings.Index(s.Value, arg))}
case "size":
return &Integer{Value: int64(utf8.RuneCountInString(s.Value))}
return &Array{Elements: result}
case "plz_i":
i, err := strconv.ParseInt(s.Value, 10, 64)
if err != nil {
i = 0
}
return &Integer{Value: i}
case "methods":
names := []string{"count", "find", "size", "methods", "replace", "reverse", "split", "toupper", "tolower", "type"}

result := make([]Object, len(names), len(names))
for i, txt := range names {
result[i] = &String{Value: txt}
}
return &Array{Elements: result}
case "replace":
if len(args) < 2 {
return &Error{Message: "Missing arguments to replace()!"}
Expand Down Expand Up @@ -85,6 +96,10 @@ func (s *String) InvokeMethod(method string, env Environment, args ...Object) Ob
return &String{Value: strings.ToUpper(s.Value)}
case "type":
return &String{Value: "string"}
default:
if objMethod, ok := stringObjectMethods[method]; ok {
return objMethod.Call(s, args)
}
}

return nil
Expand Down

0 comments on commit 112f974

Please sign in to comment.