text.go 22 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. package stdlib
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. "unicode/utf8"
  8. "github.com/d5/tengo/v2"
  9. )
  10. var textModule = map[string]tengo.Object{
  11. "re_match": &tengo.UserFunction{
  12. Name: "re_match",
  13. Value: textREMatch,
  14. }, // re_match(pattern, text) => bool/error
  15. "re_find": &tengo.UserFunction{
  16. Name: "re_find",
  17. Value: textREFind,
  18. }, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined
  19. "re_replace": &tengo.UserFunction{
  20. Name: "re_replace",
  21. Value: textREReplace,
  22. }, // re_replace(pattern, text, repl) => string/error
  23. "re_split": &tengo.UserFunction{
  24. Name: "re_split",
  25. Value: textRESplit,
  26. }, // re_split(pattern, text, count) => [string]/error
  27. "re_compile": &tengo.UserFunction{
  28. Name: "re_compile",
  29. Value: textRECompile,
  30. }, // re_compile(pattern) => Regexp/error
  31. "compare": &tengo.UserFunction{
  32. Name: "compare",
  33. Value: FuncASSRI(strings.Compare),
  34. }, // compare(a, b) => int
  35. "contains": &tengo.UserFunction{
  36. Name: "contains",
  37. Value: FuncASSRB(strings.Contains),
  38. }, // contains(s, substr) => bool
  39. "contains_any": &tengo.UserFunction{
  40. Name: "contains_any",
  41. Value: FuncASSRB(strings.ContainsAny),
  42. }, // contains_any(s, chars) => bool
  43. "count": &tengo.UserFunction{
  44. Name: "count",
  45. Value: FuncASSRI(strings.Count),
  46. }, // count(s, substr) => int
  47. "equal_fold": &tengo.UserFunction{
  48. Name: "equal_fold",
  49. Value: FuncASSRB(strings.EqualFold),
  50. }, // "equal_fold(s, t) => bool
  51. "fields": &tengo.UserFunction{
  52. Name: "fields",
  53. Value: FuncASRSs(strings.Fields),
  54. }, // fields(s) => [string]
  55. "has_prefix": &tengo.UserFunction{
  56. Name: "has_prefix",
  57. Value: FuncASSRB(strings.HasPrefix),
  58. }, // has_prefix(s, prefix) => bool
  59. "has_suffix": &tengo.UserFunction{
  60. Name: "has_suffix",
  61. Value: FuncASSRB(strings.HasSuffix),
  62. }, // has_suffix(s, suffix) => bool
  63. "index": &tengo.UserFunction{
  64. Name: "index",
  65. Value: FuncASSRI(strings.Index),
  66. }, // index(s, substr) => int
  67. "index_any": &tengo.UserFunction{
  68. Name: "index_any",
  69. Value: FuncASSRI(strings.IndexAny),
  70. }, // index_any(s, chars) => int
  71. "join": &tengo.UserFunction{
  72. Name: "join",
  73. Value: textJoin,
  74. }, // join(arr, sep) => string
  75. "last_index": &tengo.UserFunction{
  76. Name: "last_index",
  77. Value: FuncASSRI(strings.LastIndex),
  78. }, // last_index(s, substr) => int
  79. "last_index_any": &tengo.UserFunction{
  80. Name: "last_index_any",
  81. Value: FuncASSRI(strings.LastIndexAny),
  82. }, // last_index_any(s, chars) => int
  83. "repeat": &tengo.UserFunction{
  84. Name: "repeat",
  85. Value: textRepeat,
  86. }, // repeat(s, count) => string
  87. "replace": &tengo.UserFunction{
  88. Name: "replace",
  89. Value: textReplace,
  90. }, // replace(s, old, new, n) => string
  91. "substr": &tengo.UserFunction{
  92. Name: "substr",
  93. Value: textSubstring,
  94. }, // substr(s, lower, upper) => string
  95. "split": &tengo.UserFunction{
  96. Name: "split",
  97. Value: FuncASSRSs(strings.Split),
  98. }, // split(s, sep) => [string]
  99. "split_after": &tengo.UserFunction{
  100. Name: "split_after",
  101. Value: FuncASSRSs(strings.SplitAfter),
  102. }, // split_after(s, sep) => [string]
  103. "split_after_n": &tengo.UserFunction{
  104. Name: "split_after_n",
  105. Value: FuncASSIRSs(strings.SplitAfterN),
  106. }, // split_after_n(s, sep, n) => [string]
  107. "split_n": &tengo.UserFunction{
  108. Name: "split_n",
  109. Value: FuncASSIRSs(strings.SplitN),
  110. }, // split_n(s, sep, n) => [string]
  111. "title": &tengo.UserFunction{
  112. Name: "title",
  113. Value: FuncASRS(strings.Title),
  114. }, // title(s) => string
  115. "to_lower": &tengo.UserFunction{
  116. Name: "to_lower",
  117. Value: FuncASRS(strings.ToLower),
  118. }, // to_lower(s) => string
  119. "to_title": &tengo.UserFunction{
  120. Name: "to_title",
  121. Value: FuncASRS(strings.ToTitle),
  122. }, // to_title(s) => string
  123. "to_upper": &tengo.UserFunction{
  124. Name: "to_upper",
  125. Value: FuncASRS(strings.ToUpper),
  126. }, // to_upper(s) => string
  127. "pad_left": &tengo.UserFunction{
  128. Name: "pad_left",
  129. Value: textPadLeft,
  130. }, // pad_left(s, pad_len, pad_with) => string
  131. "pad_right": &tengo.UserFunction{
  132. Name: "pad_right",
  133. Value: textPadRight,
  134. }, // pad_right(s, pad_len, pad_with) => string
  135. "trim": &tengo.UserFunction{
  136. Name: "trim",
  137. Value: FuncASSRS(strings.Trim),
  138. }, // trim(s, cutset) => string
  139. "trim_left": &tengo.UserFunction{
  140. Name: "trim_left",
  141. Value: FuncASSRS(strings.TrimLeft),
  142. }, // trim_left(s, cutset) => string
  143. "trim_prefix": &tengo.UserFunction{
  144. Name: "trim_prefix",
  145. Value: FuncASSRS(strings.TrimPrefix),
  146. }, // trim_prefix(s, prefix) => string
  147. "trim_right": &tengo.UserFunction{
  148. Name: "trim_right",
  149. Value: FuncASSRS(strings.TrimRight),
  150. }, // trim_right(s, cutset) => string
  151. "trim_space": &tengo.UserFunction{
  152. Name: "trim_space",
  153. Value: FuncASRS(strings.TrimSpace),
  154. }, // trim_space(s) => string
  155. "trim_suffix": &tengo.UserFunction{
  156. Name: "trim_suffix",
  157. Value: FuncASSRS(strings.TrimSuffix),
  158. }, // trim_suffix(s, suffix) => string
  159. "atoi": &tengo.UserFunction{
  160. Name: "atoi",
  161. Value: FuncASRIE(strconv.Atoi),
  162. }, // atoi(str) => int/error
  163. "format_bool": &tengo.UserFunction{
  164. Name: "format_bool",
  165. Value: textFormatBool,
  166. }, // format_bool(b) => string
  167. "format_float": &tengo.UserFunction{
  168. Name: "format_float",
  169. Value: textFormatFloat,
  170. }, // format_float(f, fmt, prec, bits) => string
  171. "format_int": &tengo.UserFunction{
  172. Name: "format_int",
  173. Value: textFormatInt,
  174. }, // format_int(i, base) => string
  175. "itoa": &tengo.UserFunction{
  176. Name: "itoa",
  177. Value: FuncAIRS(strconv.Itoa),
  178. }, // itoa(i) => string
  179. "parse_bool": &tengo.UserFunction{
  180. Name: "parse_bool",
  181. Value: textParseBool,
  182. }, // parse_bool(str) => bool/error
  183. "parse_float": &tengo.UserFunction{
  184. Name: "parse_float",
  185. Value: textParseFloat,
  186. }, // parse_float(str, bits) => float/error
  187. "parse_int": &tengo.UserFunction{
  188. Name: "parse_int",
  189. Value: textParseInt,
  190. }, // parse_int(str, base, bits) => int/error
  191. "quote": &tengo.UserFunction{
  192. Name: "quote",
  193. Value: FuncASRS(strconv.Quote),
  194. }, // quote(str) => string
  195. "unquote": &tengo.UserFunction{
  196. Name: "unquote",
  197. Value: FuncASRSE(strconv.Unquote),
  198. }, // unquote(str) => string/error
  199. }
  200. func textREMatch(args ...tengo.Object) (ret tengo.Object, err error) {
  201. if len(args) != 2 {
  202. err = tengo.ErrWrongNumArguments
  203. return
  204. }
  205. s1, ok := tengo.ToString(args[0])
  206. if !ok {
  207. err = tengo.ErrInvalidArgumentType{
  208. Name: "first",
  209. Expected: "string(compatible)",
  210. Found: args[0].TypeName(),
  211. }
  212. return
  213. }
  214. s2, ok := tengo.ToString(args[1])
  215. if !ok {
  216. err = tengo.ErrInvalidArgumentType{
  217. Name: "second",
  218. Expected: "string(compatible)",
  219. Found: args[1].TypeName(),
  220. }
  221. return
  222. }
  223. matched, err := regexp.MatchString(s1, s2)
  224. if err != nil {
  225. ret = wrapError(err)
  226. return
  227. }
  228. if matched {
  229. ret = tengo.TrueValue
  230. } else {
  231. ret = tengo.FalseValue
  232. }
  233. return
  234. }
  235. func textREFind(args ...tengo.Object) (ret tengo.Object, err error) {
  236. numArgs := len(args)
  237. if numArgs != 2 && numArgs != 3 {
  238. err = tengo.ErrWrongNumArguments
  239. return
  240. }
  241. s1, ok := tengo.ToString(args[0])
  242. if !ok {
  243. err = tengo.ErrInvalidArgumentType{
  244. Name: "first",
  245. Expected: "string(compatible)",
  246. Found: args[0].TypeName(),
  247. }
  248. return
  249. }
  250. re, err := regexp.Compile(s1)
  251. if err != nil {
  252. ret = wrapError(err)
  253. return
  254. }
  255. s2, ok := tengo.ToString(args[1])
  256. if !ok {
  257. err = tengo.ErrInvalidArgumentType{
  258. Name: "second",
  259. Expected: "string(compatible)",
  260. Found: args[1].TypeName(),
  261. }
  262. return
  263. }
  264. if numArgs < 3 {
  265. m := re.FindStringSubmatchIndex(s2)
  266. if m == nil {
  267. ret = tengo.UndefinedValue
  268. return
  269. }
  270. arr := &tengo.Array{}
  271. for i := 0; i < len(m); i += 2 {
  272. arr.Value = append(arr.Value,
  273. &tengo.ImmutableMap{Value: map[string]tengo.Object{
  274. "text": &tengo.String{Value: s2[m[i]:m[i+1]]},
  275. "begin": tengo.Int{Value: int64(m[i])},
  276. "end": tengo.Int{Value: int64(m[i+1])},
  277. }})
  278. }
  279. ret = &tengo.Array{Value: []tengo.Object{arr}}
  280. return
  281. }
  282. i3, ok := tengo.ToInt(args[2])
  283. if !ok {
  284. err = tengo.ErrInvalidArgumentType{
  285. Name: "third",
  286. Expected: "int(compatible)",
  287. Found: args[2].TypeName(),
  288. }
  289. return
  290. }
  291. m := re.FindAllStringSubmatchIndex(s2, i3)
  292. if m == nil {
  293. ret = tengo.UndefinedValue
  294. return
  295. }
  296. arr := &tengo.Array{}
  297. for _, m := range m {
  298. subMatch := &tengo.Array{}
  299. for i := 0; i < len(m); i += 2 {
  300. subMatch.Value = append(subMatch.Value,
  301. &tengo.ImmutableMap{Value: map[string]tengo.Object{
  302. "text": &tengo.String{Value: s2[m[i]:m[i+1]]},
  303. "begin": tengo.Int{Value: int64(m[i])},
  304. "end": tengo.Int{Value: int64(m[i+1])},
  305. }})
  306. }
  307. arr.Value = append(arr.Value, subMatch)
  308. }
  309. ret = arr
  310. return
  311. }
  312. func textREReplace(args ...tengo.Object) (ret tengo.Object, err error) {
  313. if len(args) != 3 {
  314. err = tengo.ErrWrongNumArguments
  315. return
  316. }
  317. s1, ok := tengo.ToString(args[0])
  318. if !ok {
  319. err = tengo.ErrInvalidArgumentType{
  320. Name: "first",
  321. Expected: "string(compatible)",
  322. Found: args[0].TypeName(),
  323. }
  324. return
  325. }
  326. s2, ok := tengo.ToString(args[1])
  327. if !ok {
  328. err = tengo.ErrInvalidArgumentType{
  329. Name: "second",
  330. Expected: "string(compatible)",
  331. Found: args[1].TypeName(),
  332. }
  333. return
  334. }
  335. s3, ok := tengo.ToString(args[2])
  336. if !ok {
  337. err = tengo.ErrInvalidArgumentType{
  338. Name: "third",
  339. Expected: "string(compatible)",
  340. Found: args[2].TypeName(),
  341. }
  342. return
  343. }
  344. re, err := regexp.Compile(s1)
  345. if err != nil {
  346. ret = wrapError(err)
  347. } else {
  348. s, ok := doTextRegexpReplace(re, s2, s3)
  349. if !ok {
  350. return nil, tengo.ErrStringLimit
  351. }
  352. ret = &tengo.String{Value: s}
  353. }
  354. return
  355. }
  356. func textRESplit(args ...tengo.Object) (ret tengo.Object, err error) {
  357. numArgs := len(args)
  358. if numArgs != 2 && numArgs != 3 {
  359. err = tengo.ErrWrongNumArguments
  360. return
  361. }
  362. s1, ok := tengo.ToString(args[0])
  363. if !ok {
  364. err = tengo.ErrInvalidArgumentType{
  365. Name: "first",
  366. Expected: "string(compatible)",
  367. Found: args[0].TypeName(),
  368. }
  369. return
  370. }
  371. s2, ok := tengo.ToString(args[1])
  372. if !ok {
  373. err = tengo.ErrInvalidArgumentType{
  374. Name: "second",
  375. Expected: "string(compatible)",
  376. Found: args[1].TypeName(),
  377. }
  378. return
  379. }
  380. var i3 = -1
  381. if numArgs > 2 {
  382. i3, ok = tengo.ToInt(args[2])
  383. if !ok {
  384. err = tengo.ErrInvalidArgumentType{
  385. Name: "third",
  386. Expected: "int(compatible)",
  387. Found: args[2].TypeName(),
  388. }
  389. return
  390. }
  391. }
  392. re, err := regexp.Compile(s1)
  393. if err != nil {
  394. ret = wrapError(err)
  395. return
  396. }
  397. arr := &tengo.Array{}
  398. for _, s := range re.Split(s2, i3) {
  399. arr.Value = append(arr.Value, &tengo.String{Value: s})
  400. }
  401. ret = arr
  402. return
  403. }
  404. func textRECompile(args ...tengo.Object) (ret tengo.Object, err error) {
  405. if len(args) != 1 {
  406. err = tengo.ErrWrongNumArguments
  407. return
  408. }
  409. s1, ok := tengo.ToString(args[0])
  410. if !ok {
  411. err = tengo.ErrInvalidArgumentType{
  412. Name: "first",
  413. Expected: "string(compatible)",
  414. Found: args[0].TypeName(),
  415. }
  416. return
  417. }
  418. re, err := regexp.Compile(s1)
  419. if err != nil {
  420. ret = wrapError(err)
  421. } else {
  422. ret = makeTextRegexp(re)
  423. }
  424. return
  425. }
  426. func textReplace(args ...tengo.Object) (ret tengo.Object, err error) {
  427. if len(args) != 4 {
  428. err = tengo.ErrWrongNumArguments
  429. return
  430. }
  431. s1, ok := tengo.ToString(args[0])
  432. if !ok {
  433. err = tengo.ErrInvalidArgumentType{
  434. Name: "first",
  435. Expected: "string(compatible)",
  436. Found: args[0].TypeName(),
  437. }
  438. return
  439. }
  440. s2, ok := tengo.ToString(args[1])
  441. if !ok {
  442. err = tengo.ErrInvalidArgumentType{
  443. Name: "second",
  444. Expected: "string(compatible)",
  445. Found: args[1].TypeName(),
  446. }
  447. return
  448. }
  449. s3, ok := tengo.ToString(args[2])
  450. if !ok {
  451. err = tengo.ErrInvalidArgumentType{
  452. Name: "third",
  453. Expected: "string(compatible)",
  454. Found: args[2].TypeName(),
  455. }
  456. return
  457. }
  458. i4, ok := tengo.ToInt(args[3])
  459. if !ok {
  460. err = tengo.ErrInvalidArgumentType{
  461. Name: "fourth",
  462. Expected: "int(compatible)",
  463. Found: args[3].TypeName(),
  464. }
  465. return
  466. }
  467. s, ok := doTextReplace(s1, s2, s3, i4)
  468. if !ok {
  469. err = tengo.ErrStringLimit
  470. return
  471. }
  472. ret = &tengo.String{Value: s}
  473. return
  474. }
  475. func textSubstring(args ...tengo.Object) (ret tengo.Object, err error) {
  476. argslen := len(args)
  477. if argslen != 2 && argslen != 3 {
  478. err = tengo.ErrWrongNumArguments
  479. return
  480. }
  481. s1, ok := tengo.ToString(args[0])
  482. if !ok {
  483. err = tengo.ErrInvalidArgumentType{
  484. Name: "first",
  485. Expected: "string(compatible)",
  486. Found: args[0].TypeName(),
  487. }
  488. return
  489. }
  490. i2, ok := tengo.ToInt(args[1])
  491. if !ok {
  492. err = tengo.ErrInvalidArgumentType{
  493. Name: "second",
  494. Expected: "int(compatible)",
  495. Found: args[1].TypeName(),
  496. }
  497. return
  498. }
  499. strlen := len(s1)
  500. i3 := strlen
  501. if argslen == 3 {
  502. i3, ok = tengo.ToInt(args[2])
  503. if !ok {
  504. err = tengo.ErrInvalidArgumentType{
  505. Name: "third",
  506. Expected: "int(compatible)",
  507. Found: args[2].TypeName(),
  508. }
  509. return
  510. }
  511. }
  512. if i2 > i3 {
  513. err = tengo.ErrInvalidIndexType
  514. return
  515. }
  516. if i2 < 0 {
  517. i2 = 0
  518. } else if i2 > strlen {
  519. i2 = strlen
  520. }
  521. if i3 < 0 {
  522. i3 = 0
  523. } else if i3 > strlen {
  524. i3 = strlen
  525. }
  526. ret = &tengo.String{Value: s1[i2:i3]}
  527. return
  528. }
  529. func textPadLeft(args ...tengo.Object) (ret tengo.Object, err error) {
  530. argslen := len(args)
  531. if argslen != 2 && argslen != 3 {
  532. err = tengo.ErrWrongNumArguments
  533. return
  534. }
  535. s1, ok := tengo.ToString(args[0])
  536. if !ok {
  537. err = tengo.ErrInvalidArgumentType{
  538. Name: "first",
  539. Expected: "string(compatible)",
  540. Found: args[0].TypeName(),
  541. }
  542. return
  543. }
  544. i2, ok := tengo.ToInt(args[1])
  545. if !ok {
  546. err = tengo.ErrInvalidArgumentType{
  547. Name: "second",
  548. Expected: "int(compatible)",
  549. Found: args[1].TypeName(),
  550. }
  551. return
  552. }
  553. if i2 > tengo.MaxStringLen {
  554. return nil, tengo.ErrStringLimit
  555. }
  556. sLen := len(s1)
  557. if sLen >= i2 {
  558. ret = &tengo.String{Value: s1}
  559. return
  560. }
  561. s3 := " "
  562. if argslen == 3 {
  563. s3, ok = tengo.ToString(args[2])
  564. if !ok {
  565. err = tengo.ErrInvalidArgumentType{
  566. Name: "third",
  567. Expected: "string(compatible)",
  568. Found: args[2].TypeName(),
  569. }
  570. return
  571. }
  572. }
  573. padStrLen := len(s3)
  574. if padStrLen == 0 {
  575. ret = &tengo.String{Value: s1}
  576. return
  577. }
  578. padCount := ((i2 - padStrLen) / padStrLen) + 1
  579. retStr := strings.Repeat(s3, padCount) + s1
  580. ret = &tengo.String{Value: retStr[len(retStr)-i2:]}
  581. return
  582. }
  583. func textPadRight(args ...tengo.Object) (ret tengo.Object, err error) {
  584. argslen := len(args)
  585. if argslen != 2 && argslen != 3 {
  586. err = tengo.ErrWrongNumArguments
  587. return
  588. }
  589. s1, ok := tengo.ToString(args[0])
  590. if !ok {
  591. err = tengo.ErrInvalidArgumentType{
  592. Name: "first",
  593. Expected: "string(compatible)",
  594. Found: args[0].TypeName(),
  595. }
  596. return
  597. }
  598. i2, ok := tengo.ToInt(args[1])
  599. if !ok {
  600. err = tengo.ErrInvalidArgumentType{
  601. Name: "second",
  602. Expected: "int(compatible)",
  603. Found: args[1].TypeName(),
  604. }
  605. return
  606. }
  607. if i2 > tengo.MaxStringLen {
  608. return nil, tengo.ErrStringLimit
  609. }
  610. sLen := len(s1)
  611. if sLen >= i2 {
  612. ret = &tengo.String{Value: s1}
  613. return
  614. }
  615. s3 := " "
  616. if argslen == 3 {
  617. s3, ok = tengo.ToString(args[2])
  618. if !ok {
  619. err = tengo.ErrInvalidArgumentType{
  620. Name: "third",
  621. Expected: "string(compatible)",
  622. Found: args[2].TypeName(),
  623. }
  624. return
  625. }
  626. }
  627. padStrLen := len(s3)
  628. if padStrLen == 0 {
  629. ret = &tengo.String{Value: s1}
  630. return
  631. }
  632. padCount := ((i2 - padStrLen) / padStrLen) + 1
  633. retStr := s1 + strings.Repeat(s3, padCount)
  634. ret = &tengo.String{Value: retStr[:i2]}
  635. return
  636. }
  637. func textRepeat(args ...tengo.Object) (ret tengo.Object, err error) {
  638. if len(args) != 2 {
  639. return nil, tengo.ErrWrongNumArguments
  640. }
  641. s1, ok := tengo.ToString(args[0])
  642. if !ok {
  643. return nil, tengo.ErrInvalidArgumentType{
  644. Name: "first",
  645. Expected: "string(compatible)",
  646. Found: args[0].TypeName(),
  647. }
  648. }
  649. i2, ok := tengo.ToInt(args[1])
  650. if !ok {
  651. return nil, tengo.ErrInvalidArgumentType{
  652. Name: "second",
  653. Expected: "int(compatible)",
  654. Found: args[1].TypeName(),
  655. }
  656. }
  657. if len(s1)*i2 > tengo.MaxStringLen {
  658. return nil, tengo.ErrStringLimit
  659. }
  660. return &tengo.String{Value: strings.Repeat(s1, i2)}, nil
  661. }
  662. func textJoin(args ...tengo.Object) (ret tengo.Object, err error) {
  663. if len(args) != 2 {
  664. return nil, tengo.ErrWrongNumArguments
  665. }
  666. var slen int
  667. var ss1 []string
  668. switch arg0 := args[0].(type) {
  669. case *tengo.Array:
  670. for idx, a := range arg0.Value {
  671. as, ok := tengo.ToString(a)
  672. if !ok {
  673. return nil, tengo.ErrInvalidArgumentType{
  674. Name: fmt.Sprintf("first[%d]", idx),
  675. Expected: "string(compatible)",
  676. Found: a.TypeName(),
  677. }
  678. }
  679. slen += len(as)
  680. ss1 = append(ss1, as)
  681. }
  682. case *tengo.ImmutableArray:
  683. for idx, a := range arg0.Value {
  684. as, ok := tengo.ToString(a)
  685. if !ok {
  686. return nil, tengo.ErrInvalidArgumentType{
  687. Name: fmt.Sprintf("first[%d]", idx),
  688. Expected: "string(compatible)",
  689. Found: a.TypeName(),
  690. }
  691. }
  692. slen += len(as)
  693. ss1 = append(ss1, as)
  694. }
  695. default:
  696. return nil, tengo.ErrInvalidArgumentType{
  697. Name: "first",
  698. Expected: "array",
  699. Found: args[0].TypeName(),
  700. }
  701. }
  702. s2, ok := tengo.ToString(args[1])
  703. if !ok {
  704. return nil, tengo.ErrInvalidArgumentType{
  705. Name: "second",
  706. Expected: "string(compatible)",
  707. Found: args[1].TypeName(),
  708. }
  709. }
  710. // make sure output length does not exceed the limit
  711. if slen+len(s2)*(len(ss1)-1) > tengo.MaxStringLen {
  712. return nil, tengo.ErrStringLimit
  713. }
  714. return &tengo.String{Value: strings.Join(ss1, s2)}, nil
  715. }
  716. func textFormatBool(args ...tengo.Object) (ret tengo.Object, err error) {
  717. if len(args) != 1 {
  718. err = tengo.ErrWrongNumArguments
  719. return
  720. }
  721. b1, ok := args[0].(tengo.Bool)
  722. if !ok {
  723. err = tengo.ErrInvalidArgumentType{
  724. Name: "first",
  725. Expected: "bool",
  726. Found: args[0].TypeName(),
  727. }
  728. return
  729. }
  730. if b1 == tengo.TrueValue {
  731. ret = &tengo.String{Value: "true"}
  732. } else {
  733. ret = &tengo.String{Value: "false"}
  734. }
  735. return
  736. }
  737. func textFormatFloat(args ...tengo.Object) (ret tengo.Object, err error) {
  738. if len(args) != 4 {
  739. err = tengo.ErrWrongNumArguments
  740. return
  741. }
  742. f1, ok := args[0].(tengo.Float)
  743. if !ok {
  744. err = tengo.ErrInvalidArgumentType{
  745. Name: "first",
  746. Expected: "float",
  747. Found: args[0].TypeName(),
  748. }
  749. return
  750. }
  751. s2, ok := tengo.ToString(args[1])
  752. if !ok {
  753. err = tengo.ErrInvalidArgumentType{
  754. Name: "second",
  755. Expected: "string(compatible)",
  756. Found: args[1].TypeName(),
  757. }
  758. return
  759. }
  760. i3, ok := tengo.ToInt(args[2])
  761. if !ok {
  762. err = tengo.ErrInvalidArgumentType{
  763. Name: "third",
  764. Expected: "int(compatible)",
  765. Found: args[2].TypeName(),
  766. }
  767. return
  768. }
  769. i4, ok := tengo.ToInt(args[3])
  770. if !ok {
  771. err = tengo.ErrInvalidArgumentType{
  772. Name: "fourth",
  773. Expected: "int(compatible)",
  774. Found: args[3].TypeName(),
  775. }
  776. return
  777. }
  778. ret = &tengo.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)}
  779. return
  780. }
  781. func textFormatInt(args ...tengo.Object) (ret tengo.Object, err error) {
  782. if len(args) != 2 {
  783. err = tengo.ErrWrongNumArguments
  784. return
  785. }
  786. i1, ok := args[0].(tengo.Int)
  787. if !ok {
  788. err = tengo.ErrInvalidArgumentType{
  789. Name: "first",
  790. Expected: "int",
  791. Found: args[0].TypeName(),
  792. }
  793. return
  794. }
  795. i2, ok := tengo.ToInt(args[1])
  796. if !ok {
  797. err = tengo.ErrInvalidArgumentType{
  798. Name: "second",
  799. Expected: "int(compatible)",
  800. Found: args[1].TypeName(),
  801. }
  802. return
  803. }
  804. ret = &tengo.String{Value: strconv.FormatInt(i1.Value, i2)}
  805. return
  806. }
  807. func textParseBool(args ...tengo.Object) (ret tengo.Object, err error) {
  808. if len(args) != 1 {
  809. err = tengo.ErrWrongNumArguments
  810. return
  811. }
  812. s1, ok := args[0].(*tengo.String)
  813. if !ok {
  814. err = tengo.ErrInvalidArgumentType{
  815. Name: "first",
  816. Expected: "string",
  817. Found: args[0].TypeName(),
  818. }
  819. return
  820. }
  821. parsed, err := strconv.ParseBool(s1.Value)
  822. if err != nil {
  823. ret = wrapError(err)
  824. return
  825. }
  826. if parsed {
  827. ret = tengo.TrueValue
  828. } else {
  829. ret = tengo.FalseValue
  830. }
  831. return
  832. }
  833. func textParseFloat(args ...tengo.Object) (ret tengo.Object, err error) {
  834. if len(args) != 2 {
  835. err = tengo.ErrWrongNumArguments
  836. return
  837. }
  838. s1, ok := args[0].(*tengo.String)
  839. if !ok {
  840. err = tengo.ErrInvalidArgumentType{
  841. Name: "first",
  842. Expected: "string",
  843. Found: args[0].TypeName(),
  844. }
  845. return
  846. }
  847. i2, ok := tengo.ToInt(args[1])
  848. if !ok {
  849. err = tengo.ErrInvalidArgumentType{
  850. Name: "second",
  851. Expected: "int(compatible)",
  852. Found: args[1].TypeName(),
  853. }
  854. return
  855. }
  856. parsed, err := strconv.ParseFloat(s1.Value, i2)
  857. if err != nil {
  858. ret = wrapError(err)
  859. return
  860. }
  861. ret = tengo.Float{Value: parsed}
  862. return
  863. }
  864. func textParseInt(args ...tengo.Object) (ret tengo.Object, err error) {
  865. if len(args) != 3 {
  866. err = tengo.ErrWrongNumArguments
  867. return
  868. }
  869. s1, ok := args[0].(*tengo.String)
  870. if !ok {
  871. err = tengo.ErrInvalidArgumentType{
  872. Name: "first",
  873. Expected: "string",
  874. Found: args[0].TypeName(),
  875. }
  876. return
  877. }
  878. i2, ok := tengo.ToInt(args[1])
  879. if !ok {
  880. err = tengo.ErrInvalidArgumentType{
  881. Name: "second",
  882. Expected: "int(compatible)",
  883. Found: args[1].TypeName(),
  884. }
  885. return
  886. }
  887. i3, ok := tengo.ToInt(args[2])
  888. if !ok {
  889. err = tengo.ErrInvalidArgumentType{
  890. Name: "third",
  891. Expected: "int(compatible)",
  892. Found: args[2].TypeName(),
  893. }
  894. return
  895. }
  896. parsed, err := strconv.ParseInt(s1.Value, i2, i3)
  897. if err != nil {
  898. ret = wrapError(err)
  899. return
  900. }
  901. ret = tengo.Int{Value: parsed}
  902. return
  903. }
  904. // Modified implementation of strings.Replace
  905. // to limit the maximum length of output string.
  906. func doTextReplace(s, old, new string, n int) (string, bool) {
  907. if old == new || n == 0 {
  908. return s, true // avoid allocation
  909. }
  910. // Compute number of replacements.
  911. if m := strings.Count(s, old); m == 0 {
  912. return s, true // avoid allocation
  913. } else if n < 0 || m < n {
  914. n = m
  915. }
  916. // Apply replacements to buffer.
  917. t := make([]byte, len(s)+n*(len(new)-len(old)))
  918. w := 0
  919. start := 0
  920. for i := 0; i < n; i++ {
  921. j := start
  922. if len(old) == 0 {
  923. if i > 0 {
  924. _, wid := utf8.DecodeRuneInString(s[start:])
  925. j += wid
  926. }
  927. } else {
  928. j += strings.Index(s[start:], old)
  929. }
  930. ssj := s[start:j]
  931. if w+len(ssj)+len(new) > tengo.MaxStringLen {
  932. return "", false
  933. }
  934. w += copy(t[w:], ssj)
  935. w += copy(t[w:], new)
  936. start = j + len(old)
  937. }
  938. ss := s[start:]
  939. if w+len(ss) > tengo.MaxStringLen {
  940. return "", false
  941. }
  942. w += copy(t[w:], ss)
  943. return string(t[0:w]), true
  944. }