regex: fold 5a — add_thread dups parent captures/rep_counters (ha:568-573)
The loud bound (capture dup not yet portable, #35/#34/#7) flips to the real dup now that #35's spread place-chain sources landed: fresh slice header + spread-append, the D3 spelling of Hare's alloc-dup. The ok/defer-if frees drop (free() is the documented no-op, #27). Dup-independence rows drive add_thread directly: values carried, backing independent both directions, empty parent → empty dup.
This commit is contained in:
@@ -626,23 +626,15 @@ fn add_thread(threads: *[]thread, parent_idx: size, new_pc: size) (void | nomem)
|
||||
};
|
||||
};
|
||||
|
||||
// Hare dups the parent's captures/rep_counters here
|
||||
// (`alloc(threads[parent_idx].captures...)?`, ha:569/572). Every
|
||||
// ww route into that dup is blocked today (re-probed post-C3,
|
||||
// PB7): the deref-spine spread SOURCE is loud-rejected (#35),
|
||||
// the per-element append is loud-rejected (#34 struct element
|
||||
// source), and the whole-element let-copy drops bytes (#7/F5).
|
||||
// The abort is sound, not a semantic hole: fold-2a's compile()
|
||||
// cannot emit inst_groupstart/inst_repeat, so both slices are
|
||||
// provably empty in every program this fold can run; the empty
|
||||
// case appends honest zeroed headers below. The verbatim dup
|
||||
// lands with the group/repeat fold (ww-core #3).
|
||||
if ((*threads)[parent_idx].captures.len != 0
|
||||
|| (*threads)[parent_idx].rep_counters.len != 0) {
|
||||
abort("regex: capture dup not yet portable (#35/#34/#7)");
|
||||
};
|
||||
// Hare dups via `alloc(threads[parent_idx].captures...)?`
|
||||
// (ha:568-573); the alloc-dup form has no ww spelling (D3) —
|
||||
// fresh slice header + spread-append, the #35-landed deref-spine
|
||||
// source. The ok/defer-if frees (ha:570/573) drop: free() is the
|
||||
// documented no-op (#27; see finish()).
|
||||
let captures: []capture;
|
||||
append(captures, (*threads)[parent_idx].captures...);
|
||||
let rep_counters: []size;
|
||||
append(rep_counters, (*threads)[parent_idx].rep_counters...);
|
||||
|
||||
append(*threads, thread {
|
||||
pc = new_pc,
|
||||
|
||||
@@ -504,9 +504,9 @@ fn ic_one(v: regex.inst, want: bool) void = {
|
||||
// add_thread (regex.ha:557-587): same-pc dedup suppression fires only
|
||||
// when the existing thread is unmatched AND started strictly earlier
|
||||
// than the parent (ha:561-565); otherwise the child appends,
|
||||
// inheriting the parent's start/matched/failed with fresh empty
|
||||
// capture/rep_counter headers and a zeroed root_capture. The
|
||||
// capture-dup loud bound must NOT fire on these empty-caps parents.
|
||||
// inheriting the parent's start/matched/failed with DUPLICATED
|
||||
// capture/rep_counter slices (empty parent → empty dup, ha:569/572)
|
||||
// and a zeroed root_capture.
|
||||
@test fn add_thread_dedup_inherit() void = {
|
||||
let ts: []thread = [];
|
||||
append(ts, thread { pc = 0, start_idx = 5, start_bytesize = 4,
|
||||
@@ -546,6 +546,59 @@ fn ic_one(v: regex.inst, want: bool) void = {
|
||||
if (ts3[2].start_idx != (5: size)) { fail(); };
|
||||
};
|
||||
|
||||
// add_thread dup (regex.ha:568-573): the child carries a COPY of the
|
||||
// parent's captures/rep_counters — values equal, backing independent
|
||||
// in both directions (mutate parent → child unchanged, mutate child →
|
||||
// parent unchanged). Empty parent → empty dup (the pre-flip rows above
|
||||
// stay byte-for-byte). Driven directly, the dedup-test precedent.
|
||||
@test fn add_thread_dup_independence() void = {
|
||||
let caps: []capture = [];
|
||||
append(caps, capture {
|
||||
content = "ab", start = 1, start_bytesize = 1,
|
||||
end = 2, end_bytesize = 2,
|
||||
});
|
||||
append(caps, capture {
|
||||
content = "c", start = 3, start_bytesize = 3,
|
||||
end = 4, end_bytesize = 4,
|
||||
});
|
||||
let reps: []size = [];
|
||||
append(reps, (5: size));
|
||||
append(reps, (6: size));
|
||||
let ts: []thread = [];
|
||||
append(ts, thread { pc = 0, start_idx = 1, captures = caps,
|
||||
rep_counters = reps, ... });
|
||||
let r: (void | nomem) = add_thread(&ts, 0, 9);
|
||||
if (!(r is void)) { fail(); };
|
||||
if (len(ts) != 2) { fail(); };
|
||||
// dup carried the parent's values
|
||||
if (ts[1].captures.len != 2) { fail(); };
|
||||
if (strings.compare(ts[1].captures[0].content, "ab") != 0) { fail(); };
|
||||
if (ts[1].captures[0].start != (1: size)) { fail(); };
|
||||
if (ts[1].captures[1].end != (4: size)) { fail(); };
|
||||
if (ts[1].rep_counters.len != 2) { fail(); };
|
||||
if (ts[1].rep_counters[0] != (5: size)) { fail(); };
|
||||
if (ts[1].rep_counters[1] != (6: size)) { fail(); };
|
||||
// independence, parent → child: mutate the parent post-add
|
||||
ts[0].captures[0].start = 100;
|
||||
ts[0].captures[0].content = "zz";
|
||||
ts[0].rep_counters[0] = 77;
|
||||
if (ts[1].captures[0].start != (1: size)) { fail(); };
|
||||
if (strings.compare(ts[1].captures[0].content, "ab") != 0) { fail(); };
|
||||
if (ts[1].rep_counters[0] != (5: size)) { fail(); };
|
||||
// independence, child → parent
|
||||
ts[1].captures[1].end = 200;
|
||||
ts[1].rep_counters[1] = 88;
|
||||
if (ts[0].captures[1].end != (4: size)) { fail(); };
|
||||
if (ts[0].rep_counters[1] != (6: size)) { fail(); };
|
||||
// empty parent → empty dup
|
||||
let ts2: []thread = [];
|
||||
append(ts2, thread { pc = 0, ... });
|
||||
let r2: (void | nomem) = add_thread(&ts2, 0, 3);
|
||||
if (!(r2 is void)) { fail(); };
|
||||
if (ts2[1].captures.len != 0) { fail(); };
|
||||
if (ts2[1].rep_counters.len != 0) { fail(); };
|
||||
};
|
||||
|
||||
// run_thread (regex.ha:589-742) driven directly over compile("ab")'s
|
||||
// real program [skip, lit 'a', lit 'b', match(false)] — the arms
|
||||
// fold-2a can emit. Phases: parked-skip spawn (len 1→2, parent pc
|
||||
@@ -1744,5 +1797,6 @@ export fn main() i32 = {
|
||||
signalled = 34; fold4_compile_errors();
|
||||
signalled = 35; fold4_find_cases();
|
||||
signalled = 36; fold4_findall();
|
||||
signalled = 37; add_thread_dup_independence();
|
||||
return 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user