lib/regex: fold-2c — findall/result_freeall
findall (regex.ha:923-960) over the memio seeker: one fixed stream for the whole string, per-call suffix substring, absolute io.seek(SET) past the scanner readahead after each match. The append-then-mutate m[0] fix-up is verbatim Hare (the appended header shares m's backing); the zero-length-match rune advancement guard (ha:946-952) carries the infinite-loop protection. search's |success|=2 unwrap is the D13 explicit 3-arm match (ww-core #14); nomem propagates. result_freeall (ha:1119-1124) verbatim, frees no-op (#27). Tests port Hare's own findall table (+test.ha:719-731, the three fold-2a-reachable rows) through run_findall_case's checks, plus field rows pinning adjacency, the one-result overlap pick, multibyte zero-length advancement (utf8sz step != 1), idx != bytesize, the tail-match break, and the empty no-match slice. 989's run gets the conventional timeout-180 wrap: a regression of the zero-length guard would otherwise hang the gate (no-op frees, so no quick OOM exit).
This commit is contained in:
@@ -6,8 +6,9 @@
|
||||
// (delete_thread/is_consuming_inst/add_thread/run_thread); fold 2b
|
||||
// tranche C = search, the first end-to-end match; fold 2b tranche D
|
||||
// = the exec surface (test/find — the D13 explicit-match spelling of
|
||||
// Hare's multi-success `?`, ww-core #14). Every other metacharacter
|
||||
// arm (and replace/findall) is DEFERRED behind compiler fixes —
|
||||
// Hare's multi-success `?`, ww-core #14); fold 2c =
|
||||
// findall/result_freeall over the memio seeker. Every other
|
||||
// metacharacter arm (and replace) is DEFERRED behind compiler fixes —
|
||||
// probed pre-port, pA*/pB*/PB*/PC*/PD* probes. They land with those
|
||||
// fixes.
|
||||
//
|
||||
@@ -642,6 +643,68 @@ export fn find(re: *regex, string: str) (result | nomem) = {
|
||||
};
|
||||
};
|
||||
|
||||
// Attempts to match a [[regex]] against a string and returns all
|
||||
// non-overlapping matches as a slice of [[result]]s. The caller must
|
||||
// free the return value with [[result_freeall]].
|
||||
//
|
||||
// ref/hare/regex/regex.ha:920-960. Hare's ok-flag + `defer if (!ok)
|
||||
// result_freeall(res)` (ha:924-926) is omitted: the flag's only use
|
||||
// is the defer-if, ww defer takes a single expression, and the frees
|
||||
// are the documented no-op (#27) — compile()'s omitted `defer if`
|
||||
// precedent. The `search(...)?` unwrap (ha:933) is the same explicit
|
||||
// D13 lowering as test()/find() (ww-core #14); the nomem arm
|
||||
// propagates verbatim.
|
||||
export fn findall(re: *regex, string: str) ([]result | nomem) = {
|
||||
let res: []result;
|
||||
let str_idx: size = 0;
|
||||
let str_bytesize: size = 0;
|
||||
let strm: memio.stream = memio.fixed(strings.toutf8(string));
|
||||
let str_bytes: []u8 = strings.toutf8(string);
|
||||
for (true) {
|
||||
let substring: str =
|
||||
strings.frombytes(str_bytes[str_bytesize:]);
|
||||
let r: (void | []capture | nomem) =
|
||||
search(re, substring, &strm.vt, true);
|
||||
match (r) {
|
||||
case let m: []capture => {
|
||||
// Hare appends m and THEN fixes m[0] up from
|
||||
// substring- to whole-string-relative (ha:935-939):
|
||||
// the appended header shares m's backing, so the
|
||||
// mutations below are visible through res. Kept
|
||||
// verbatim — the aliasing is the subtle bit.
|
||||
append(res, m);
|
||||
m[0].start += str_idx;
|
||||
m[0].end += str_idx;
|
||||
m[0].start_bytesize += str_bytesize;
|
||||
m[0].end_bytesize += str_bytesize;
|
||||
str_idx = m[0].end;
|
||||
str_bytesize = m[0].end_bytesize;
|
||||
if (m[0].start_bytesize == (len(str_bytes): size)) {
|
||||
// end-of-string reached
|
||||
break;
|
||||
};
|
||||
if (m[0].start_bytesize == m[0].end_bytesize) {
|
||||
// zero-length match: forward rune and byte
|
||||
// indices (ha:946-952 — the guard against
|
||||
// the classic findall infinite loop)
|
||||
str_idx += 1;
|
||||
str_bytesize += (utf8.utf8sz(
|
||||
str_bytes[str_bytesize])!: size);
|
||||
};
|
||||
// ha:953-954: each search call's scanner buffers
|
||||
// past what scanrune consumed; the absolute SET
|
||||
// repositions the underlying stream before the
|
||||
// next call builds a fresh scanner.
|
||||
io.seek(&strm.vt, (str_bytesize: io.off),
|
||||
io.whence.SET)!;
|
||||
};
|
||||
case void => break;
|
||||
case let n: nomem => return n;
|
||||
};
|
||||
};
|
||||
return res;
|
||||
};
|
||||
|
||||
// Frees a [[result]].
|
||||
//
|
||||
// ref/hare/regex/regex.ha:1113-1116, verbatim — the free() builtin is
|
||||
@@ -650,6 +713,17 @@ export fn result_free(s: result) void = {
|
||||
free(s);
|
||||
};
|
||||
|
||||
// Frees a slice of [[result]]s.
|
||||
//
|
||||
// ref/hare/regex/regex.ha:1119-1124, verbatim — both frees are the
|
||||
// documented no-op (#27).
|
||||
export fn result_freeall(s: []result) void = {
|
||||
for (let r .. s) {
|
||||
result_free(r);
|
||||
};
|
||||
free(s);
|
||||
};
|
||||
|
||||
// Converts an [[error]] into a user-friendly string.
|
||||
//
|
||||
// ref/hare/regex/regex.ha:1126-1127 (expression-bodied in Hare; ww
|
||||
|
||||
Reference in New Issue
Block a user