add more strings functions to text module

This commit is contained in:
Daniel Kang 2019-01-28 19:30:32 -08:00
parent 69a703bea2
commit 309b03bb30
5 changed files with 414 additions and 4 deletions

View file

@ -395,6 +395,31 @@ func FuncASRS(fn func(string) string) *objects.UserFunction {
}
}
// FuncASRSs transform a function of 'func(string) []string' signature into a user function object.
func FuncASRSs(fn func(string) []string) *objects.UserFunction {
return &objects.UserFunction{
Value: func(args ...objects.Object) (objects.Object, error) {
if len(args) != 1 {
return nil, objects.ErrWrongNumArguments
}
s1, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
res := fn(s1)
arr := &objects.Array{}
for _, osArg := range res {
arr.Value = append(arr.Value, &objects.String{Value: osArg})
}
return arr, nil
},
}
}
// FuncASRSE transform a function of 'func(string) (string, error)' signature into a user function object.
// User function will return 'true' if underlying native function returns nil.
func FuncASRSE(fn func(string) (string, error)) *objects.UserFunction {
@ -462,6 +487,171 @@ func FuncASSRE(fn func(string, string) error) *objects.UserFunction {
}
}
// FuncASSRSs transform a function of 'func(string, string) []string' signature into a user function object.
func FuncASSRSs(fn func(string, string) []string) *objects.UserFunction {
return &objects.UserFunction{
Value: func(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
s1, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
s2, ok := objects.ToString(args[1])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
arr := &objects.Array{}
for _, res := range fn(s1, s2) {
arr.Value = append(arr.Value, &objects.String{Value: res})
}
return arr, nil
},
}
}
// FuncASSIRSs transform a function of 'func(string, string, int) []string' signature into a user function object.
func FuncASSIRSs(fn func(string, string, int) []string) *objects.UserFunction {
return &objects.UserFunction{
Value: func(args ...objects.Object) (objects.Object, error) {
if len(args) != 3 {
return nil, objects.ErrWrongNumArguments
}
s1, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
s2, ok := objects.ToString(args[1])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
i3, ok := objects.ToInt(args[2])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
arr := &objects.Array{}
for _, res := range fn(s1, s2, i3) {
arr.Value = append(arr.Value, &objects.String{Value: res})
}
return arr, nil
},
}
}
// FuncASSRI transform a function of 'func(string, string) int' signature into a user function object.
func FuncASSRI(fn func(string, string) int) *objects.UserFunction {
return &objects.UserFunction{
Value: func(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
s1, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
s2, ok := objects.ToString(args[1])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
return &objects.Int{Value: int64(fn(s1, s2))}, nil
},
}
}
// FuncASSRS transform a function of 'func(string, string) string' signature into a user function object.
func FuncASSRS(fn func(string, string) string) *objects.UserFunction {
return &objects.UserFunction{
Value: func(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
s1, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
s2, ok := objects.ToString(args[1])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
return &objects.String{Value: fn(s1, s2)}, nil
},
}
}
// FuncASSRB transform a function of 'func(string, string) bool' signature into a user function object.
func FuncASSRB(fn func(string, string) bool) *objects.UserFunction {
return &objects.UserFunction{
Value: func(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
s1, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
s2, ok := objects.ToString(args[1])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
if fn(s1, s2) {
return objects.TrueValue, nil
}
return objects.FalseValue, nil
},
}
}
// FuncASsSRS transform a function of 'func([]string, string) string' signature into a user function object.
func FuncASsSRS(fn func([]string, string) string) *objects.UserFunction {
return &objects.UserFunction{
Value: func(args ...objects.Object) (objects.Object, error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
var ss1 []string
arr, ok := args[0].(*objects.Array)
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
for _, a := range arr.Value {
as, ok := objects.ToString(a)
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
ss1 = append(ss1, as)
}
s2, ok := objects.ToString(args[1])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
return &objects.String{Value: fn(ss1, s2)}, nil
},
}
}
// FuncASI64RE transform a function of 'func(string, int64) error' signature
// into a user function object.
func FuncASI64RE(fn func(string, int64) error) *objects.UserFunction {
@ -510,6 +700,30 @@ func FuncAIIRE(fn func(int, int) error) *objects.UserFunction {
}
}
// FuncASIRS transform a function of 'func(string, int) string' signature
// into a user function object.
func FuncASIRS(fn func(string, int) string) *objects.UserFunction {
return &objects.UserFunction{
Value: func(args ...objects.Object) (ret objects.Object, err error) {
if len(args) != 2 {
return nil, objects.ErrWrongNumArguments
}
s1, ok := objects.ToString(args[0])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
i2, ok := objects.ToInt(args[1])
if !ok {
return nil, objects.ErrInvalidTypeConversion
}
return &objects.String{Value: fn(s1, i2)}, nil
},
}
}
// FuncASIIRE transform a function of 'func(string, int, int) error' signature
// into a user function object.
func FuncASIIRE(fn func(string, int, int) error) *objects.UserFunction {
@ -607,8 +821,8 @@ func FuncAIRSsE(fn func(int) ([]string, error)) *objects.UserFunction {
}
arr := &objects.Array{}
for _, osArg := range res {
arr.Value = append(arr.Value, &objects.String{Value: osArg})
for _, r := range res {
arr.Value = append(arr.Value, &objects.String{Value: r})
}
return arr, nil

View file

@ -2,6 +2,8 @@ package stdlib_test
import (
"errors"
"strconv"
"strings"
"testing"
"github.com/d5/tengo/assert"
@ -115,6 +117,15 @@ func TestFuncASRS(t *testing.T) {
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASRSs(t *testing.T) {
uf := stdlib.FuncASRSs(func(a string) []string { return []string{a} })
ret, err := uf.Call(&objects.String{Value: "foo"})
assert.NoError(t, err)
assert.Equal(t, array(&objects.String{Value: "foo"}), ret)
ret, err = uf.Call()
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASI64RE(t *testing.T) {
uf := stdlib.FuncASI64RE(func(a string, b int64) error { return nil })
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.Int{Value: 5})
@ -168,7 +179,24 @@ func TestFuncASRSE(t *testing.T) {
}
func TestFuncASSRE(t *testing.T) {
uf := stdlib.FuncASSRE(func(a, b string) error { return nil })
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
assert.NoError(t, err)
uf = stdlib.FuncASSRE(func(a, b string) error { return errors.New("some error") })
ret, err = uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
assert.NoError(t, err)
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
ret, err = uf.Call(&objects.String{Value: "foo"})
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASsRS(t *testing.T) {
uf := stdlib.FuncASsSRS(func(a []string, b string) string { return strings.Join(a, b) })
ret, err := uf.Call(array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), &objects.String{Value: " "})
assert.NoError(t, err)
assert.Equal(t, &objects.String{Value: "foo bar"}, ret)
ret, err = uf.Call(&objects.String{Value: "foo"})
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncARF(t *testing.T) {
@ -247,6 +275,17 @@ func TestFuncAFFRF(t *testing.T) {
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASIRS(t *testing.T) {
uf := stdlib.FuncASIRS(func(a string, b int) string { return strings.Repeat(a, b) })
ret, err := uf.Call(&objects.String{Value: "ab"}, &objects.Int{Value: 2})
assert.NoError(t, err)
assert.Equal(t, &objects.String{Value: "abab"}, ret)
ret, err = uf.Call()
assert.Equal(t, objects.ErrWrongNumArguments, err)
ret, err = uf.Call(objects.TrueValue)
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncAIFRF(t *testing.T) {
uf := stdlib.FuncAIFRF(func(a int, b float64) float64 {
return float64(a) + b
@ -303,6 +342,24 @@ func TestFuncAIRSsE(t *testing.T) {
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASSRSs(t *testing.T) {
uf := stdlib.FuncASSRSs(func(a, b string) []string { return []string{a, b} })
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
assert.NoError(t, err)
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), ret)
ret, err = uf.Call()
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASSIRSs(t *testing.T) {
uf := stdlib.FuncASSIRSs(func(a, b string, c int) []string { return []string{a, b, strconv.Itoa(c)} })
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.Int{Value: 5})
assert.NoError(t, err)
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.String{Value: "5"}), ret)
ret, err = uf.Call()
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncARB(t *testing.T) {
uf := stdlib.FuncARB(func() bool { return true })
ret, err := uf.Call()
@ -355,6 +412,33 @@ func TestFuncAYRIE(t *testing.T) {
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASSRI(t *testing.T) {
uf := stdlib.FuncASSRI(func(a, b string) int { return len(a) + len(b) })
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
assert.NoError(t, err)
assert.Equal(t, &objects.Int{Value: 6}, ret)
ret, err = uf.Call(&objects.String{Value: "foo"})
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASSRS(t *testing.T) {
uf := stdlib.FuncASSRS(func(a, b string) string { return a + b })
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
assert.NoError(t, err)
assert.Equal(t, &objects.String{Value: "foobar"}, ret)
ret, err = uf.Call(&objects.String{Value: "foo"})
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func TestFuncASSRB(t *testing.T) {
uf := stdlib.FuncASSRB(func(a, b string) bool { return len(a) > len(b) })
ret, err := uf.Call(&objects.String{Value: "123"}, &objects.String{Value: "12"})
assert.NoError(t, err)
assert.Equal(t, objects.TrueValue, ret)
ret, err = uf.Call(&objects.String{Value: "foo"})
assert.Equal(t, objects.ErrWrongNumArguments, err)
}
func array(elements ...objects.Object) *objects.Array {
return &objects.Array{Value: elements}
}

View file

@ -2,6 +2,7 @@ package stdlib
import (
"regexp"
"strings"
"github.com/d5/tengo/objects"
)
@ -232,6 +233,98 @@ var textModule = map[string]objects.Object{
return
},
},
// compare(a, b) => int
"compare": FuncASSRI(strings.Compare),
// contains(s, substr) => bool
"contains": FuncASSRB(strings.Contains),
// contains_any(s, chars) => bool
"contains_any": FuncASSRB(strings.ContainsAny),
// count(s, substr) => int
"count": FuncASSRI(strings.Count),
// "equal_fold(s, t) => bool
"equal_fold": FuncASSRB(strings.EqualFold),
// fields(s) => array(string)
"fields": FuncASRSs(strings.Fields),
// has_prefix(s, prefix) => bool
"has_prefix": FuncASSRB(strings.HasPrefix),
// has_suffix(s, suffix) => bool
"has_suffix": FuncASSRB(strings.HasSuffix),
// index(s, substr) => int
"index": FuncASSRI(strings.Index),
// index_any(s, chars) => int
"index_any": FuncASSRI(strings.IndexAny),
// join(arr, sep) => string
"join": FuncASsSRS(strings.Join),
// last_index(s, substr) => int
"last_index": FuncASSRI(strings.LastIndex),
// last_index_any(s, chars) => int
"last_index_any": FuncASSRI(strings.LastIndexAny),
// repeat(s, count) => string
"repeat": FuncASIRS(strings.Repeat),
// replace(s, old, new, n) => string
"replace": &objects.UserFunction{
Value: func(args ...objects.Object) (ret objects.Object, err error) {
if len(args) != 4 {
err = objects.ErrWrongNumArguments
return
}
s1, ok := objects.ToString(args[0])
if !ok {
err = objects.ErrInvalidTypeConversion
return
}
s2, ok := objects.ToString(args[1])
if !ok {
err = objects.ErrInvalidTypeConversion
return
}
s3, ok := objects.ToString(args[2])
if !ok {
err = objects.ErrInvalidTypeConversion
return
}
i4, ok := objects.ToInt(args[3])
if !ok {
err = objects.ErrInvalidTypeConversion
return
}
ret = &objects.String{Value: strings.Replace(s1, s2, s3, i4)}
return
},
},
// split(s, sep) => []string
"split": FuncASSRSs(strings.Split),
// split_after(s, sep) => []string
"split_after": FuncASSRSs(strings.SplitAfter),
// split_after_n(s, sep, n) => []string
"split_after_n": FuncASSIRSs(strings.SplitAfterN),
// split_n(s, sep, n) => []string
"split_n": FuncASSIRSs(strings.SplitN),
// title(s) => string
"title": FuncASRS(strings.Title),
// to_lower(s) => string
"to_lower": FuncASRS(strings.ToLower),
// to_title(s) => string
"to_title": FuncASRS(strings.ToTitle),
// to_upper(s) => string
"to_upper": FuncASRS(strings.ToUpper),
// trim_left(s, cutset) => string
"trim_left": FuncASSRS(strings.TrimLeft),
// trim_prefix(s, prefix) => string
"trim_prefix": FuncASSRS(strings.TrimPrefix),
// trim_right(s, cutset) => string
"trim_right": FuncASSRS(strings.TrimRight),
// trim_space(s) => string
"trim_space": FuncASRS(strings.TrimSpace),
// trim_suffix(s, suffix) => string
"trim_suffix": FuncASSRS(strings.TrimSuffix),
}
func stringsRegexpImmutableMap(re *regexp.Regexp) *objects.ImmutableMap {

View file

@ -159,5 +159,25 @@ func TestTextRE(t *testing.T) {
module(t, "text").call("re_split", d.pattern, d.text, d.count).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
module(t, "text").call("re_compile", d.pattern).call("split", d.text, d.count).expect(d.expected, "pattern: %q, text: %q", d.pattern, d.text)
}
}
func TestText(t *testing.T) {
module(t, "text").call("compare", "", "").expect(0)
module(t, "text").call("compare", "", "a").expect(-1)
module(t, "text").call("compare", "a", "").expect(1)
module(t, "text").call("compare", "a", "a").expect(0)
module(t, "text").call("compare", "a", "b").expect(-1)
module(t, "text").call("compare", "b", "a").expect(1)
module(t, "text").call("compare", "abcde", "abcde").expect(0)
module(t, "text").call("compare", "abcde", "abcdf").expect(-1)
module(t, "text").call("compare", "abcdf", "abcde").expect(1)
module(t, "text").call("contains", "", "").expect(true)
module(t, "text").call("contains", "", "a").expect(false)
module(t, "text").call("contains", "a", "").expect(true)
module(t, "text").call("contains", "a", "a").expect(true)
module(t, "text").call("contains", "abcde", "a").expect(true)
module(t, "text").call("contains", "abcde", "abcde").expect(true)
module(t, "text").call("contains", "abc", "abcde").expect(false)
module(t, "text").call("contains", "ab cd", "bc").expect(false)
}

View file

@ -60,7 +60,6 @@ func TestVariable(t *testing.T) {
Name: "d",
Value: nil,
ValueType: "undefined",
StringValue: "",
Object: objects.UndefinedValue,
IsUndefined: true,
},