builtin_fn_str.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package eval
  2. import (
  3. "errors"
  4. "regexp"
  5. "strconv"
  6. "strings"
  7. "src.elv.sh/pkg/eval/vals"
  8. "src.elv.sh/pkg/wcwidth"
  9. )
  10. // String operations.
  11. // ErrInputOfEawkMustBeString is thrown when eawk gets a non-string input.
  12. var ErrInputOfEawkMustBeString = errors.New("input of eawk must be string")
  13. // TODO(xiaq): Document -override-wcswidth.
  14. func init() {
  15. addBuiltinFns(map[string]any{
  16. "<s": func(a, b string) bool { return a < b },
  17. "<=s": func(a, b string) bool { return a <= b },
  18. "==s": func(a, b string) bool { return a == b },
  19. "!=s": func(a, b string) bool { return a != b },
  20. ">s": func(a, b string) bool { return a > b },
  21. ">=s": func(a, b string) bool { return a >= b },
  22. "to-string": toString,
  23. "base": base,
  24. "wcswidth": wcwidth.Of,
  25. "-override-wcwidth": wcwidth.Override,
  26. "eawk": eawk,
  27. })
  28. }
  29. func toString(fm *Frame, args ...any) error {
  30. out := fm.ValueOutput()
  31. for _, a := range args {
  32. err := out.Put(vals.ToString(a))
  33. if err != nil {
  34. return err
  35. }
  36. }
  37. return nil
  38. }
  39. // ErrBadBase is thrown by the "base" builtin if the base is smaller than 2 or
  40. // greater than 36.
  41. var ErrBadBase = errors.New("bad base")
  42. func base(fm *Frame, b int, nums ...int) error {
  43. if b < 2 || b > 36 {
  44. return ErrBadBase
  45. }
  46. out := fm.ValueOutput()
  47. for _, num := range nums {
  48. err := out.Put(strconv.FormatInt(int64(num), b))
  49. if err != nil {
  50. return err
  51. }
  52. }
  53. return nil
  54. }
  55. var eawkWordSep = regexp.MustCompile("[ \t]+")
  56. func eawk(fm *Frame, f Callable, inputs Inputs) error {
  57. broken := false
  58. var err error
  59. inputs(func(v any) {
  60. if broken {
  61. return
  62. }
  63. line, ok := v.(string)
  64. if !ok {
  65. broken = true
  66. err = ErrInputOfEawkMustBeString
  67. return
  68. }
  69. args := []any{line}
  70. for _, field := range eawkWordSep.Split(strings.Trim(line, " \t"), -1) {
  71. args = append(args, field)
  72. }
  73. newFm := fm.Fork("fn of eawk")
  74. // TODO: Close port 0 of newFm.
  75. ex := f.Call(newFm, args, NoOpts)
  76. newFm.Close()
  77. if ex != nil {
  78. switch Reason(ex) {
  79. case nil, Continue:
  80. // nop
  81. case Break:
  82. broken = true
  83. default:
  84. broken = true
  85. err = ex
  86. }
  87. }
  88. })
  89. return err
  90. }