awkrs 0.4.13

Awk implementation in Rust with broad CLI compatibility, parallel records, and experimental Cranelift JIT
Documentation
# Knuth-Morris-Pratt substring search.
# Input lines:
#   "PAT <pattern>"      set / reset the pattern (failure function rebuilt)
#   "TXT <text>"         find every occurrence in <text>; print "<text>: <p1> <p2> ..."
#                        (1-based positions; empty list → "NONE")
#
# Failure function fail[i] stored in a global associative array, rebuilt
# whenever the pattern changes.

function build_fail(p,   m, i, k) {
  m = length(p)
  delete fail
  fail[1] = 0
  k = 0
  for (i = 2; i <= m; i++) {
    while (k > 0 && substr(p, k + 1, 1) != substr(p, i, 1)) k = fail[k]
    if (substr(p, k + 1, 1) == substr(p, i, 1)) k++
    fail[i] = k
  }
}

function kmp_search(t, p,   n, m, q, i, hits) {
  n = length(t); m = length(p)
  if (m == 0 || n == 0) return ""
  q = 0
  hits = ""
  for (i = 1; i <= n; i++) {
    while (q > 0 && substr(p, q + 1, 1) != substr(t, i, 1)) q = fail[q]
    if (substr(p, q + 1, 1) == substr(t, i, 1)) q++
    if (q == m) {
      hits = hits ((hits == "") ? "" : " ") (i - m + 1)
      q = fail[q]
    }
  }
  return hits
}

$1 == "PAT" {
  pat = substr($0, 5)
  build_fail(pat)
  printf "PAT set: %s  (failure: ", pat
  sep = ""
  for (i = 1; i <= length(pat); i++) { printf "%s%d", sep, fail[i]; sep = "," }
  print ")"
  next
}

$1 == "TXT" {
  txt = substr($0, 5)
  h = kmp_search(txt, pat)
  printf "%s -> %s\n", txt, (h == "") ? "NONE" : h
  next
}