488 lines
11 KiB
Go
488 lines
11 KiB
Go
package stdlib
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/d5/tengo/objects"
|
|
)
|
|
|
|
var textModule = map[string]objects.Object{
|
|
// re_match(pattern, text) => bool/error
|
|
"re_match": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
if len(args) != 2 {
|
|
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
|
|
}
|
|
|
|
matched, err := regexp.MatchString(s1, s2)
|
|
if err != nil {
|
|
ret = wrapError(err)
|
|
return
|
|
}
|
|
|
|
if matched {
|
|
ret = objects.TrueValue
|
|
} else {
|
|
ret = objects.FalseValue
|
|
}
|
|
|
|
return
|
|
},
|
|
},
|
|
|
|
// re_find(pattern, text) => array(array({text:,begin:,end:}))/undefined
|
|
// re_find(pattern, text, maxCount) => array(array({text:,begin:,end:}))/undefined
|
|
"re_find": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
numArgs := len(args)
|
|
if numArgs != 2 && numArgs != 3 {
|
|
err = objects.ErrWrongNumArguments
|
|
return
|
|
}
|
|
|
|
s1, ok := objects.ToString(args[0])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
|
|
re, err := regexp.Compile(s1)
|
|
if err != nil {
|
|
ret = wrapError(err)
|
|
return
|
|
}
|
|
|
|
s2, ok := objects.ToString(args[1])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
|
|
if numArgs < 3 {
|
|
m := re.FindStringSubmatchIndex(s2)
|
|
if m == nil {
|
|
ret = objects.UndefinedValue
|
|
return
|
|
}
|
|
|
|
arr := &objects.Array{}
|
|
for i := 0; i < len(m); i += 2 {
|
|
arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
|
"text": &objects.String{Value: s2[m[i]:m[i+1]]},
|
|
"begin": &objects.Int{Value: int64(m[i])},
|
|
"end": &objects.Int{Value: int64(m[i+1])},
|
|
}})
|
|
}
|
|
|
|
ret = &objects.Array{Value: []objects.Object{arr}}
|
|
|
|
return
|
|
}
|
|
|
|
i3, ok := objects.ToInt(args[2])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
m := re.FindAllStringSubmatchIndex(s2, i3)
|
|
if m == nil {
|
|
ret = objects.UndefinedValue
|
|
return
|
|
}
|
|
|
|
arr := &objects.Array{}
|
|
for _, m := range m {
|
|
subMatch := &objects.Array{}
|
|
for i := 0; i < len(m); i += 2 {
|
|
subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
|
"text": &objects.String{Value: s2[m[i]:m[i+1]]},
|
|
"begin": &objects.Int{Value: int64(m[i])},
|
|
"end": &objects.Int{Value: int64(m[i+1])},
|
|
}})
|
|
}
|
|
|
|
arr.Value = append(arr.Value, subMatch)
|
|
}
|
|
|
|
ret = arr
|
|
|
|
return
|
|
},
|
|
},
|
|
|
|
// re_replace(pattern, text, repl) => string/error
|
|
"re_replace": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
if len(args) != 3 {
|
|
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
|
|
}
|
|
|
|
re, err := regexp.Compile(s1)
|
|
if err != nil {
|
|
ret = wrapError(err)
|
|
} else {
|
|
ret = &objects.String{Value: re.ReplaceAllString(s2, s3)}
|
|
}
|
|
|
|
return
|
|
},
|
|
},
|
|
|
|
// re_split(pattern, text) => array(string)/error
|
|
// re_split(pattern, text, maxCount) => array(string)/error
|
|
"re_split": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
numArgs := len(args)
|
|
if numArgs != 2 && numArgs != 3 {
|
|
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
|
|
}
|
|
|
|
var i3 = -1
|
|
if numArgs > 2 {
|
|
i3, ok = objects.ToInt(args[2])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
}
|
|
|
|
re, err := regexp.Compile(s1)
|
|
if err != nil {
|
|
ret = wrapError(err)
|
|
return
|
|
}
|
|
|
|
arr := &objects.Array{}
|
|
for _, s := range re.Split(s2, i3) {
|
|
arr.Value = append(arr.Value, &objects.String{Value: s})
|
|
}
|
|
|
|
ret = arr
|
|
|
|
return
|
|
},
|
|
},
|
|
|
|
// re_compile(pattern) => Regexp/error
|
|
"re_compile": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
if len(args) != 1 {
|
|
err = objects.ErrWrongNumArguments
|
|
return
|
|
}
|
|
|
|
s1, ok := objects.ToString(args[0])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
|
|
re, err := regexp.Compile(s1)
|
|
if err != nil {
|
|
ret = wrapError(err)
|
|
} else {
|
|
ret = stringsRegexpImmutableMap(re)
|
|
}
|
|
|
|
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 {
|
|
return &objects.ImmutableMap{
|
|
Value: map[string]objects.Object{
|
|
// match(text) => bool
|
|
"match": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
if len(args) != 1 {
|
|
err = objects.ErrWrongNumArguments
|
|
return
|
|
}
|
|
|
|
s1, ok := objects.ToString(args[0])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
|
|
if re.MatchString(s1) {
|
|
ret = objects.TrueValue
|
|
} else {
|
|
ret = objects.FalseValue
|
|
}
|
|
|
|
return
|
|
},
|
|
},
|
|
|
|
// find(text) => array(array({text:,begin:,end:}))/undefined
|
|
// find(text, maxCount) => array(array({text:,begin:,end:}))/undefined
|
|
"find": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
numArgs := len(args)
|
|
if numArgs != 1 && numArgs != 2 {
|
|
err = objects.ErrWrongNumArguments
|
|
return
|
|
}
|
|
|
|
s1, ok := objects.ToString(args[0])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
|
|
if numArgs == 1 {
|
|
m := re.FindStringSubmatchIndex(s1)
|
|
if m == nil {
|
|
ret = objects.UndefinedValue
|
|
return
|
|
}
|
|
|
|
arr := &objects.Array{}
|
|
for i := 0; i < len(m); i += 2 {
|
|
arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
|
"text": &objects.String{Value: s1[m[i]:m[i+1]]},
|
|
"begin": &objects.Int{Value: int64(m[i])},
|
|
"end": &objects.Int{Value: int64(m[i+1])},
|
|
}})
|
|
}
|
|
|
|
ret = &objects.Array{Value: []objects.Object{arr}}
|
|
|
|
return
|
|
}
|
|
|
|
i2, ok := objects.ToInt(args[1])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
m := re.FindAllStringSubmatchIndex(s1, i2)
|
|
if m == nil {
|
|
ret = objects.UndefinedValue
|
|
return
|
|
}
|
|
|
|
arr := &objects.Array{}
|
|
for _, m := range m {
|
|
subMatch := &objects.Array{}
|
|
for i := 0; i < len(m); i += 2 {
|
|
subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
|
"text": &objects.String{Value: s1[m[i]:m[i+1]]},
|
|
"begin": &objects.Int{Value: int64(m[i])},
|
|
"end": &objects.Int{Value: int64(m[i+1])},
|
|
}})
|
|
}
|
|
|
|
arr.Value = append(arr.Value, subMatch)
|
|
}
|
|
|
|
ret = arr
|
|
|
|
return
|
|
},
|
|
},
|
|
|
|
// replace(src, repl) => string
|
|
"replace": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
if len(args) != 2 {
|
|
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
|
|
}
|
|
|
|
ret = &objects.String{Value: re.ReplaceAllString(s1, s2)}
|
|
|
|
return
|
|
},
|
|
},
|
|
|
|
// split(text) => array(string)
|
|
// split(text, maxCount) => array(string)
|
|
"split": &objects.UserFunction{
|
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
numArgs := len(args)
|
|
if numArgs != 1 && numArgs != 2 {
|
|
err = objects.ErrWrongNumArguments
|
|
return
|
|
}
|
|
|
|
s1, ok := objects.ToString(args[0])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
|
|
var i2 = -1
|
|
if numArgs > 1 {
|
|
i2, ok = objects.ToInt(args[1])
|
|
if !ok {
|
|
err = objects.ErrInvalidTypeConversion
|
|
return
|
|
}
|
|
}
|
|
|
|
arr := &objects.Array{}
|
|
for _, s := range re.Split(s1, i2) {
|
|
arr.Value = append(arr.Value, &objects.String{Value: s})
|
|
}
|
|
|
|
ret = arr
|
|
|
|
return
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|