如何使用 ragel 正确实现正则表达式 /cat[s]?(\b|$)/ ?

How to implement regex /cat[s]?(\b|$)/ with ragel correclty?

我想加快我用 Go 编写的程序的速度,并将正则表达式转换为使用 ragel 的有限状态机。在转换类似于 /cat[s]?(\b|$)/ 的正则表达式(它匹配字边框或输入结尾)时,我不知道如何正确匹配输入结尾,所以我做了这个解决方法:

package main

import(
  "strings"
  "fmt"
  "unicode"
)

func Match(data []byte) bool {
  data = []byte(strings.ToLower(string(data)))

  %%{
    machine test;
    write data;
  }%%

  cs, p, pe, eof := 0, 0, len(data), len(data)
  _ = eof

  var matchPos int

  %%{
    main := ('cat' 's'?) @{matchPos = p};

    write init;
    write exec;
  }%%

  return checkMatch(data, matchPos+1)
}

func checkMatch(data []byte, p int) bool {
  if p == len(data) {
    return true
  }
  tail := string(data[p:])
  c := []rune(tail)[0]
  if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
    return true
  }
  return false
}

func main () {
  vs := []string{
    "cat",
    "cats",
    "cat and dog",
    "cats and dogs",
    "caterpillar",
  }
  for _, v := range vs {
    fmt.Printf("'%s': %v\n", v, Match([]byte(v)))
  }
}

输出正确:

'cat': true
'cats': true
'cat and dog': true
'cats and dogs': true
'caterpillar': false

不过我确实认为有更好的方法。使用 ragel 处理输入结束的 "proper" 方法是什么?

处理输入结束的正确方法当然是使用 EOF 动作。并使用一般的动作,像这样(减少 Match 功能):

  var matched bool

  %%{
    action setMatched {
      matched = true
    }

    main := ('cat' 's'?) %/setMatched ([ \t] >setMatched);

    write init;
    write exec;
  }%%
  // Variable generated and not used by Ragel.
  _ = _test_trans_actions

  return matched

产生以下输出(注意添加了一个重要的测试用例):

'cat': true
'cats': true
'cat and dog': true
'cats and dogs': true
'catspaw': false
'caterpillar': false

并且像这样工作:

它添加的是 setMatched 操作,该操作由第一台机器 (cats?) 的最终状态之一 (%/setMatched) 中的 EOF 触发,或者在进入时触发(>setMatched)第二个(差不多\b,但实际上可以用内部space机器代替)。它完全消除了 checkMatch 的需要。