summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/v2/stdlib/text.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/d5/tengo/v2/stdlib/text.go')
-rw-r--r--vendor/github.com/d5/tengo/v2/stdlib/text.go1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/vendor/github.com/d5/tengo/v2/stdlib/text.go b/vendor/github.com/d5/tengo/v2/stdlib/text.go
new file mode 100644
index 00000000..d7d5d1da
--- /dev/null
+++ b/vendor/github.com/d5/tengo/v2/stdlib/text.go
@@ -0,0 +1,1072 @@
+package stdlib
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/d5/tengo/v2"
+)
+
+var textModule = map[string]tengo.Object{
+ "re_match": &tengo.UserFunction{
+ Name: "re_match",
+ Value: textREMatch,
+ }, // re_match(pattern, text) => bool/error
+ "re_find": &tengo.UserFunction{
+ Name: "re_find",
+ Value: textREFind,
+ }, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined
+ "re_replace": &tengo.UserFunction{
+ Name: "re_replace",
+ Value: textREReplace,
+ }, // re_replace(pattern, text, repl) => string/error
+ "re_split": &tengo.UserFunction{
+ Name: "re_split",
+ Value: textRESplit,
+ }, // re_split(pattern, text, count) => [string]/error
+ "re_compile": &tengo.UserFunction{
+ Name: "re_compile",
+ Value: textRECompile,
+ }, // re_compile(pattern) => Regexp/error
+ "compare": &tengo.UserFunction{
+ Name: "compare",
+ Value: FuncASSRI(strings.Compare),
+ }, // compare(a, b) => int
+ "contains": &tengo.UserFunction{
+ Name: "contains",
+ Value: FuncASSRB(strings.Contains),
+ }, // contains(s, substr) => bool
+ "contains_any": &tengo.UserFunction{
+ Name: "contains_any",
+ Value: FuncASSRB(strings.ContainsAny),
+ }, // contains_any(s, chars) => bool
+ "count": &tengo.UserFunction{
+ Name: "count",
+ Value: FuncASSRI(strings.Count),
+ }, // count(s, substr) => int
+ "equal_fold": &tengo.UserFunction{
+ Name: "equal_fold",
+ Value: FuncASSRB(strings.EqualFold),
+ }, // "equal_fold(s, t) => bool
+ "fields": &tengo.UserFunction{
+ Name: "fields",
+ Value: FuncASRSs(strings.Fields),
+ }, // fields(s) => [string]
+ "has_prefix": &tengo.UserFunction{
+ Name: "has_prefix",
+ Value: FuncASSRB(strings.HasPrefix),
+ }, // has_prefix(s, prefix) => bool
+ "has_suffix": &tengo.UserFunction{
+ Name: "has_suffix",
+ Value: FuncASSRB(strings.HasSuffix),
+ }, // has_suffix(s, suffix) => bool
+ "index": &tengo.UserFunction{
+ Name: "index",
+ Value: FuncASSRI(strings.Index),
+ }, // index(s, substr) => int
+ "index_any": &tengo.UserFunction{
+ Name: "index_any",
+ Value: FuncASSRI(strings.IndexAny),
+ }, // index_any(s, chars) => int
+ "join": &tengo.UserFunction{
+ Name: "join",
+ Value: textJoin,
+ }, // join(arr, sep) => string
+ "last_index": &tengo.UserFunction{
+ Name: "last_index",
+ Value: FuncASSRI(strings.LastIndex),
+ }, // last_index(s, substr) => int
+ "last_index_any": &tengo.UserFunction{
+ Name: "last_index_any",
+ Value: FuncASSRI(strings.LastIndexAny),
+ }, // last_index_any(s, chars) => int
+ "repeat": &tengo.UserFunction{
+ Name: "repeat",
+ Value: textRepeat,
+ }, // repeat(s, count) => string
+ "replace": &tengo.UserFunction{
+ Name: "replace",
+ Value: textReplace,
+ }, // replace(s, old, new, n) => string
+ "substr": &tengo.UserFunction{
+ Name: "substr",
+ Value: textSubstring,
+ }, // substr(s, lower, upper) => string
+ "split": &tengo.UserFunction{
+ Name: "split",
+ Value: FuncASSRSs(strings.Split),
+ }, // split(s, sep) => [string]
+ "split_after": &tengo.UserFunction{
+ Name: "split_after",
+ Value: FuncASSRSs(strings.SplitAfter),
+ }, // split_after(s, sep) => [string]
+ "split_after_n": &tengo.UserFunction{
+ Name: "split_after_n",
+ Value: FuncASSIRSs(strings.SplitAfterN),
+ }, // split_after_n(s, sep, n) => [string]
+ "split_n": &tengo.UserFunction{
+ Name: "split_n",
+ Value: FuncASSIRSs(strings.SplitN),
+ }, // split_n(s, sep, n) => [string]
+ "title": &tengo.UserFunction{
+ Name: "title",
+ Value: FuncASRS(strings.Title),
+ }, // title(s) => string
+ "to_lower": &tengo.UserFunction{
+ Name: "to_lower",
+ Value: FuncASRS(strings.ToLower),
+ }, // to_lower(s) => string
+ "to_title": &tengo.UserFunction{
+ Name: "to_title",
+ Value: FuncASRS(strings.ToTitle),
+ }, // to_title(s) => string
+ "to_upper": &tengo.UserFunction{
+ Name: "to_upper",
+ Value: FuncASRS(strings.ToUpper),
+ }, // to_upper(s) => string
+ "pad_left": &tengo.UserFunction{
+ Name: "pad_left",
+ Value: textPadLeft,
+ }, // pad_left(s, pad_len, pad_with) => string
+ "pad_right": &tengo.UserFunction{
+ Name: "pad_right",
+ Value: textPadRight,
+ }, // pad_right(s, pad_len, pad_with) => string
+ "trim": &tengo.UserFunction{
+ Name: "trim",
+ Value: FuncASSRS(strings.Trim),
+ }, // trim(s, cutset) => string
+ "trim_left": &tengo.UserFunction{
+ Name: "trim_left",
+ Value: FuncASSRS(strings.TrimLeft),
+ }, // trim_left(s, cutset) => string
+ "trim_prefix": &tengo.UserFunction{
+ Name: "trim_prefix",
+ Value: FuncASSRS(strings.TrimPrefix),
+ }, // trim_prefix(s, prefix) => string
+ "trim_right": &tengo.UserFunction{
+ Name: "trim_right",
+ Value: FuncASSRS(strings.TrimRight),
+ }, // trim_right(s, cutset) => string
+ "trim_space": &tengo.UserFunction{
+ Name: "trim_space",
+ Value: FuncASRS(strings.TrimSpace),
+ }, // trim_space(s) => string
+ "trim_suffix": &tengo.UserFunction{
+ Name: "trim_suffix",
+ Value: FuncASSRS(strings.TrimSuffix),
+ }, // trim_suffix(s, suffix) => string
+ "atoi": &tengo.UserFunction{
+ Name: "atoi",
+ Value: FuncASRIE(strconv.Atoi),
+ }, // atoi(str) => int/error
+ "format_bool": &tengo.UserFunction{
+ Name: "format_bool",
+ Value: textFormatBool,
+ }, // format_bool(b) => string
+ "format_float": &tengo.UserFunction{
+ Name: "format_float",
+ Value: textFormatFloat,
+ }, // format_float(f, fmt, prec, bits) => string
+ "format_int": &tengo.UserFunction{
+ Name: "format_int",
+ Value: textFormatInt,
+ }, // format_int(i, base) => string
+ "itoa": &tengo.UserFunction{
+ Name: "itoa",
+ Value: FuncAIRS(strconv.Itoa),
+ }, // itoa(i) => string
+ "parse_bool": &tengo.UserFunction{
+ Name: "parse_bool",
+ Value: textParseBool,
+ }, // parse_bool(str) => bool/error
+ "parse_float": &tengo.UserFunction{
+ Name: "parse_float",
+ Value: textParseFloat,
+ }, // parse_float(str, bits) => float/error
+ "parse_int": &tengo.UserFunction{
+ Name: "parse_int",
+ Value: textParseInt,
+ }, // parse_int(str, base, bits) => int/error
+ "quote": &tengo.UserFunction{
+ Name: "quote",
+ Value: FuncASRS(strconv.Quote),
+ }, // quote(str) => string
+ "unquote": &tengo.UserFunction{
+ Name: "unquote",
+ Value: FuncASRSE(strconv.Unquote),
+ }, // unquote(str) => string/error
+}
+
+func textREMatch(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 2 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := tengo.ToString(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ matched, err := regexp.MatchString(s1, s2)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ if matched {
+ ret = tengo.TrueValue
+ } else {
+ ret = tengo.FalseValue
+ }
+
+ return
+}
+
+func textREFind(args ...tengo.Object) (ret tengo.Object, err error) {
+ numArgs := len(args)
+ if numArgs != 2 && numArgs != 3 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ re, err := regexp.Compile(s1)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ s2, ok := tengo.ToString(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ if numArgs < 3 {
+ m := re.FindStringSubmatchIndex(s2)
+ if m == nil {
+ ret = tengo.UndefinedValue
+ return
+ }
+
+ arr := &tengo.Array{}
+ for i := 0; i < len(m); i += 2 {
+ arr.Value = append(arr.Value,
+ &tengo.ImmutableMap{Value: map[string]tengo.Object{
+ "text": &tengo.String{Value: s2[m[i]:m[i+1]]},
+ "begin": &tengo.Int{Value: int64(m[i])},
+ "end": &tengo.Int{Value: int64(m[i+1])},
+ }})
+ }
+
+ ret = &tengo.Array{Value: []tengo.Object{arr}}
+
+ return
+ }
+
+ i3, ok := tengo.ToInt(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+ m := re.FindAllStringSubmatchIndex(s2, i3)
+ if m == nil {
+ ret = tengo.UndefinedValue
+ return
+ }
+
+ arr := &tengo.Array{}
+ for _, m := range m {
+ subMatch := &tengo.Array{}
+ for i := 0; i < len(m); i += 2 {
+ subMatch.Value = append(subMatch.Value,
+ &tengo.ImmutableMap{Value: map[string]tengo.Object{
+ "text": &tengo.String{Value: s2[m[i]:m[i+1]]},
+ "begin": &tengo.Int{Value: int64(m[i])},
+ "end": &tengo.Int{Value: int64(m[i+1])},
+ }})
+ }
+
+ arr.Value = append(arr.Value, subMatch)
+ }
+
+ ret = arr
+
+ return
+}
+
+func textREReplace(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 3 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := tengo.ToString(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ s3, ok := tengo.ToString(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "string(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ re, err := regexp.Compile(s1)
+ if err != nil {
+ ret = wrapError(err)
+ } else {
+ s, ok := doTextRegexpReplace(re, s2, s3)
+ if !ok {
+ return nil, tengo.ErrStringLimit
+ }
+
+ ret = &tengo.String{Value: s}
+ }
+
+ return
+}
+
+func textRESplit(args ...tengo.Object) (ret tengo.Object, err error) {
+ numArgs := len(args)
+ if numArgs != 2 && numArgs != 3 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := tengo.ToString(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ var i3 = -1
+ if numArgs > 2 {
+ i3, ok = tengo.ToInt(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+ }
+
+ re, err := regexp.Compile(s1)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ arr := &tengo.Array{}
+ for _, s := range re.Split(s2, i3) {
+ arr.Value = append(arr.Value, &tengo.String{Value: s})
+ }
+
+ ret = arr
+
+ return
+}
+
+func textRECompile(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 1 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ re, err := regexp.Compile(s1)
+ if err != nil {
+ ret = wrapError(err)
+ } else {
+ ret = makeTextRegexp(re)
+ }
+
+ return
+}
+
+func textReplace(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 4 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := tengo.ToString(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ s3, ok := tengo.ToString(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "string(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ i4, ok := tengo.ToInt(args[3])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "fourth",
+ Expected: "int(compatible)",
+ Found: args[3].TypeName(),
+ }
+ return
+ }
+
+ s, ok := doTextReplace(s1, s2, s3, i4)
+ if !ok {
+ err = tengo.ErrStringLimit
+ return
+ }
+
+ ret = &tengo.String{Value: s}
+
+ return
+}
+
+func textSubstring(args ...tengo.Object) (ret tengo.Object, err error) {
+ argslen := len(args)
+ if argslen != 2 && argslen != 3 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := tengo.ToInt(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ strlen := len(s1)
+ i3 := strlen
+ if argslen == 3 {
+ i3, ok = tengo.ToInt(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+ }
+
+ if i2 > i3 {
+ err = tengo.ErrInvalidIndexType
+ return
+ }
+
+ if i2 < 0 {
+ i2 = 0
+ } else if i2 > strlen {
+ i2 = strlen
+ }
+
+ if i3 < 0 {
+ i3 = 0
+ } else if i3 > strlen {
+ i3 = strlen
+ }
+
+ ret = &tengo.String{Value: s1[i2:i3]}
+
+ return
+}
+
+func textPadLeft(args ...tengo.Object) (ret tengo.Object, err error) {
+ argslen := len(args)
+ if argslen != 2 && argslen != 3 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := tengo.ToInt(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ if i2 > tengo.MaxStringLen {
+ return nil, tengo.ErrStringLimit
+ }
+
+ sLen := len(s1)
+ if sLen >= i2 {
+ ret = &tengo.String{Value: s1}
+ return
+ }
+
+ s3 := " "
+ if argslen == 3 {
+ s3, ok = tengo.ToString(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "string(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+ }
+
+ padStrLen := len(s3)
+ if padStrLen == 0 {
+ ret = &tengo.String{Value: s1}
+ return
+ }
+
+ padCount := ((i2 - padStrLen) / padStrLen) + 1
+ retStr := strings.Repeat(s3, padCount) + s1
+ ret = &tengo.String{Value: retStr[len(retStr)-i2:]}
+
+ return
+}
+
+func textPadRight(args ...tengo.Object) (ret tengo.Object, err error) {
+ argslen := len(args)
+ if argslen != 2 && argslen != 3 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := tengo.ToInt(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ if i2 > tengo.MaxStringLen {
+ return nil, tengo.ErrStringLimit
+ }
+
+ sLen := len(s1)
+ if sLen >= i2 {
+ ret = &tengo.String{Value: s1}
+ return
+ }
+
+ s3 := " "
+ if argslen == 3 {
+ s3, ok = tengo.ToString(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "string(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+ }
+
+ padStrLen := len(s3)
+ if padStrLen == 0 {
+ ret = &tengo.String{Value: s1}
+ return
+ }
+
+ padCount := ((i2 - padStrLen) / padStrLen) + 1
+ retStr := s1 + strings.Repeat(s3, padCount)
+ ret = &tengo.String{Value: retStr[:i2]}
+
+ return
+}
+
+func textRepeat(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 2 {
+ return nil, tengo.ErrWrongNumArguments
+ }
+
+ s1, ok := tengo.ToString(args[0])
+ if !ok {
+ return nil, tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string(compatible)",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ i2, ok := tengo.ToInt(args[1])
+ if !ok {
+ return nil, tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ if len(s1)*i2 > tengo.MaxStringLen {
+ return nil, tengo.ErrStringLimit
+ }
+
+ return &tengo.String{Value: strings.Repeat(s1, i2)}, nil
+}
+
+func textJoin(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 2 {
+ return nil, tengo.ErrWrongNumArguments
+ }
+
+ var slen int
+ var ss1 []string
+ switch arg0 := args[0].(type) {
+ case *tengo.Array:
+ for idx, a := range arg0.Value {
+ as, ok := tengo.ToString(a)
+ if !ok {
+ return nil, tengo.ErrInvalidArgumentType{
+ Name: fmt.Sprintf("first[%d]", idx),
+ Expected: "string(compatible)",
+ Found: a.TypeName(),
+ }
+ }
+ slen += len(as)
+ ss1 = append(ss1, as)
+ }
+ case *tengo.ImmutableArray:
+ for idx, a := range arg0.Value {
+ as, ok := tengo.ToString(a)
+ if !ok {
+ return nil, tengo.ErrInvalidArgumentType{
+ Name: fmt.Sprintf("first[%d]", idx),
+ Expected: "string(compatible)",
+ Found: a.TypeName(),
+ }
+ }
+ slen += len(as)
+ ss1 = append(ss1, as)
+ }
+ default:
+ return nil, tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "array",
+ Found: args[0].TypeName(),
+ }
+ }
+
+ s2, ok := tengo.ToString(args[1])
+ if !ok {
+ return nil, tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ }
+
+ // make sure output length does not exceed the limit
+ if slen+len(s2)*(len(ss1)-1) > tengo.MaxStringLen {
+ return nil, tengo.ErrStringLimit
+ }
+
+ return &tengo.String{Value: strings.Join(ss1, s2)}, nil
+}
+
+func textFormatBool(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 1 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ b1, ok := args[0].(*tengo.Bool)
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "bool",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ if b1 == tengo.TrueValue {
+ ret = &tengo.String{Value: "true"}
+ } else {
+ ret = &tengo.String{Value: "false"}
+ }
+
+ return
+}
+
+func textFormatFloat(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 4 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ f1, ok := args[0].(*tengo.Float)
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "float",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ s2, ok := tengo.ToString(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "string(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ i3, ok := tengo.ToInt(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ i4, ok := tengo.ToInt(args[3])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "fourth",
+ Expected: "int(compatible)",
+ Found: args[3].TypeName(),
+ }
+ return
+ }
+
+ ret = &tengo.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)}
+
+ return
+}
+
+func textFormatInt(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 2 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ i1, ok := args[0].(*tengo.Int)
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "int",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := tengo.ToInt(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ ret = &tengo.String{Value: strconv.FormatInt(i1.Value, i2)}
+
+ return
+}
+
+func textParseBool(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 1 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := args[0].(*tengo.String)
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ parsed, err := strconv.ParseBool(s1.Value)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ if parsed {
+ ret = tengo.TrueValue
+ } else {
+ ret = tengo.FalseValue
+ }
+
+ return
+}
+
+func textParseFloat(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 2 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := args[0].(*tengo.String)
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := tengo.ToInt(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ parsed, err := strconv.ParseFloat(s1.Value, i2)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ ret = &tengo.Float{Value: parsed}
+
+ return
+}
+
+func textParseInt(args ...tengo.Object) (ret tengo.Object, err error) {
+ if len(args) != 3 {
+ err = tengo.ErrWrongNumArguments
+ return
+ }
+
+ s1, ok := args[0].(*tengo.String)
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "first",
+ Expected: "string",
+ Found: args[0].TypeName(),
+ }
+ return
+ }
+
+ i2, ok := tengo.ToInt(args[1])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "second",
+ Expected: "int(compatible)",
+ Found: args[1].TypeName(),
+ }
+ return
+ }
+
+ i3, ok := tengo.ToInt(args[2])
+ if !ok {
+ err = tengo.ErrInvalidArgumentType{
+ Name: "third",
+ Expected: "int(compatible)",
+ Found: args[2].TypeName(),
+ }
+ return
+ }
+
+ parsed, err := strconv.ParseInt(s1.Value, i2, i3)
+ if err != nil {
+ ret = wrapError(err)
+ return
+ }
+
+ ret = &tengo.Int{Value: parsed}
+
+ return
+}
+
+// Modified implementation of strings.Replace
+// to limit the maximum length of output string.
+func doTextReplace(s, old, new string, n int) (string, bool) {
+ if old == new || n == 0 {
+ return s, true // avoid allocation
+ }
+
+ // Compute number of replacements.
+ if m := strings.Count(s, old); m == 0 {
+ return s, true // avoid allocation
+ } else if n < 0 || m < n {
+ n = m
+ }
+
+ // Apply replacements to buffer.
+ t := make([]byte, len(s)+n*(len(new)-len(old)))
+ w := 0
+ start := 0
+ for i := 0; i < n; i++ {
+ j := start
+ if len(old) == 0 {
+ if i > 0 {
+ _, wid := utf8.DecodeRuneInString(s[start:])
+ j += wid
+ }
+ } else {
+ j += strings.Index(s[start:], old)
+ }
+
+ ssj := s[start:j]
+ if w+len(ssj)+len(new) > tengo.MaxStringLen {
+ return "", false
+ }
+
+ w += copy(t[w:], ssj)
+ w += copy(t[w:], new)
+ start = j + len(old)
+ }
+
+ ss := s[start:]
+ if w+len(ss) > tengo.MaxStringLen {
+ return "", false
+ }
+
+ w += copy(t[w:], ss)
+
+ return string(t[0:w]), true
+}