diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index f4ffc13..c719cd5 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -521,7 +521,7 @@ func evalIndexExpression(node *ast.IndexExpression, left object.Object, index ob switch { case left.Type() == object.STRING_OBJ && index.Type() == object.NUMBER_OBJ: return evalStringIndexExpression(node, left, index) - case left.Type() == object.LIST_OBJ && index.Type() == object.NUMBER_OBJ: + case left.Type() == object.LIST_OBJ: return evalListIndexExpression(node, left, index) case left.Type() == object.MAP_OBJ: return evalMapIndexExpression(node.Token.Line, left, index) @@ -707,6 +707,18 @@ func evalForInExpression(fie *ast.ForInExpression, env *object.Environment) obje } } + return value.NULL + case *object.Map: + for _, p := range i.Pairs { + env.Set(fie.Key, p.Key) + env.Set(fie.Value, p.Value) + block := Eval(fie.Block, env) + + if error.IsError(block) { + return block + } + } + return value.NULL default: return error.NewError(fie.Token.Line, error.UnusableForLoop, i.Inspect()) diff --git a/object/object.go b/object/object.go index f7ca1df..aea753d 100644 --- a/object/object.go +++ b/object/object.go @@ -7,6 +7,7 @@ import ( "bytes" "fmt" "hash/fnv" + "regexp" "strconv" "strings" "unicode/utf8" @@ -336,12 +337,44 @@ func (rv *ReturnValue) CallMethod(method string, args []Object) Object { func (s *String) CallMethod(method string, args []Object) Object { switch method { + case "matches": + matches, err := regexp.Match(s.Value, []byte(args[0].(*String).Value)) + + if err != nil { + return &Error{Message: err.Error()} + } + + return &Boolean{Value: matches} + case "find": + re := regexp.MustCompile(s.Value) + + found := re.FindStringSubmatch(args[0].(*String).Value) + + if len(found) > 0 { + return &String{Value: found[1]} + } + + return &String{} + case "findAll": + re := regexp.MustCompile(s.Value) + list := &List{} + found := re.FindStringSubmatch(args[0].(*String).Value) + + for _, f := range found { + list.Elements = append(list.Elements, &String{Value: f}) + } + + return list case "endsWith": hasPrefix := strings.HasSuffix(s.Value, args[0].(*String).Value) return &Boolean{Value: hasPrefix} case "length": return &Number{Value: decimal.NewFromInt(int64(utf8.RuneCountInString(s.Value)))} + case "replace": + value := strings.Replace(s.Value, args[0].(*String).Value, args[1].(*String).Value, -1) + + return &String{Value: value} case "split": split := strings.Split(s.Value, args[0].(*String).Value) list := &List{} diff --git a/utilities/utilities.go b/utilities/utilities.go index 577ca75..2c05916 100644 --- a/utilities/utilities.go +++ b/utilities/utilities.go @@ -74,13 +74,17 @@ func NativeBoolToBooleanObject(input bool) *object.Boolean { // IsTruthy returns the truthy value of the passed object. func IsTruthy(obj object.Object) bool { - switch obj { - case value.NULL: - return false - case value.TRUE: - return true - case value.FALSE: + switch obj := obj.(type) { + case *object.Null: return false + case *object.Boolean: + return obj.Value + case *object.String: + return len(obj.Value) > 0 + case *object.List: + return len(obj.Elements) > 0 + case *object.Map: + return len(obj.Pairs) > 0 default: return true }