re.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Package re implements a regular expression module.
  2. package re
  3. import (
  4. "regexp"
  5. "src.elv.sh/pkg/eval"
  6. "src.elv.sh/pkg/eval/errs"
  7. "src.elv.sh/pkg/eval/vals"
  8. )
  9. // Ns is the namespace for the re: module.
  10. var Ns = eval.BuildNsNamed("re").
  11. AddGoFns(map[string]any{
  12. "quote": regexp.QuoteMeta,
  13. "match": match,
  14. "find": find,
  15. "replace": replace,
  16. "split": split,
  17. }).Ns()
  18. type matchOpts struct{ Posix bool }
  19. func (*matchOpts) SetDefaultOptions() {}
  20. func match(opts matchOpts, argPattern, source string) (bool, error) {
  21. pattern, err := makePattern(argPattern, opts.Posix, false)
  22. if err != nil {
  23. return false, err
  24. }
  25. return pattern.MatchString(source), nil
  26. }
  27. // Struct for holding options to find. Also used by split.
  28. type findOpts struct {
  29. Posix bool
  30. Longest bool
  31. Max int
  32. }
  33. func (o *findOpts) SetDefaultOptions() { o.Max = -1 }
  34. func find(fm *eval.Frame, opts findOpts, argPattern, source string) error {
  35. out := fm.ValueOutput()
  36. pattern, err := makePattern(argPattern, opts.Posix, opts.Longest)
  37. if err != nil {
  38. return err
  39. }
  40. matches := pattern.FindAllSubmatchIndex([]byte(source), opts.Max)
  41. for _, match := range matches {
  42. start, end := match[0], match[1]
  43. groups := vals.EmptyList
  44. for i := 0; i < len(match); i += 2 {
  45. start, end := match[i], match[i+1]
  46. text := ""
  47. // FindAllSubmatchIndex may return negative indices to indicate
  48. // that the pattern didn't appear in the text.
  49. if start >= 0 && end >= 0 {
  50. text = source[start:end]
  51. }
  52. groups = groups.Conj(submatchStruct{text, start, end})
  53. }
  54. err := out.Put(matchStruct{source[start:end], start, end, groups})
  55. if err != nil {
  56. return err
  57. }
  58. }
  59. return nil
  60. }
  61. type replaceOpts struct {
  62. Posix bool
  63. Longest bool
  64. Literal bool
  65. }
  66. func (*replaceOpts) SetDefaultOptions() {}
  67. func replace(fm *eval.Frame, opts replaceOpts, argPattern string, argRepl any, source string) (string, error) {
  68. pattern, err := makePattern(argPattern, opts.Posix, opts.Longest)
  69. if err != nil {
  70. return "", err
  71. }
  72. if opts.Literal {
  73. repl, ok := argRepl.(string)
  74. if !ok {
  75. return "", &errs.BadValue{What: "literal replacement",
  76. Valid: "string", Actual: vals.Kind(argRepl)}
  77. }
  78. return pattern.ReplaceAllLiteralString(source, repl), nil
  79. }
  80. switch repl := argRepl.(type) {
  81. case string:
  82. return pattern.ReplaceAllString(source, repl), nil
  83. case eval.Callable:
  84. var errReplace error
  85. replFunc := func(s string) string {
  86. if errReplace != nil {
  87. return ""
  88. }
  89. values, err := fm.CaptureOutput(func(fm *eval.Frame) error {
  90. return repl.Call(fm, []any{s}, eval.NoOpts)
  91. })
  92. if err != nil {
  93. errReplace = err
  94. return ""
  95. }
  96. if len(values) != 1 {
  97. errReplace = &errs.ArityMismatch{What: "replacement function output",
  98. ValidLow: 1, ValidHigh: 1, Actual: len(values)}
  99. return ""
  100. }
  101. output, ok := values[0].(string)
  102. if !ok {
  103. errReplace = &errs.BadValue{What: "replacement function output",
  104. Valid: "string", Actual: vals.Kind(values[0])}
  105. return ""
  106. }
  107. return output
  108. }
  109. return pattern.ReplaceAllStringFunc(source, replFunc), errReplace
  110. default:
  111. return "", &errs.BadValue{What: "replacement",
  112. Valid: "string or function", Actual: vals.Kind(argRepl)}
  113. }
  114. }
  115. func split(fm *eval.Frame, opts findOpts, argPattern, source string) error {
  116. out := fm.ValueOutput()
  117. pattern, err := makePattern(argPattern, opts.Posix, opts.Longest)
  118. if err != nil {
  119. return err
  120. }
  121. pieces := pattern.Split(source, opts.Max)
  122. for _, piece := range pieces {
  123. err := out.Put(piece)
  124. if err != nil {
  125. return err
  126. }
  127. }
  128. return nil
  129. }
  130. func makePattern(p string, posix, longest bool) (*regexp.Regexp, error) {
  131. pattern, err := compile(p, posix)
  132. if err != nil {
  133. return nil, err
  134. }
  135. if longest {
  136. pattern.Longest()
  137. }
  138. return pattern, nil
  139. }
  140. func compile(pattern string, posix bool) (*regexp.Regexp, error) {
  141. if posix {
  142. return regexp.CompilePOSIX(pattern)
  143. }
  144. return regexp.Compile(pattern)
  145. }