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
|
// Hare dups via `alloc(threads[parent_idx].captures...)?`
|
||||||
// (`alloc(threads[parent_idx].captures...)?`, ha:569/572). Every
|
// (ha:568-573); the alloc-dup form has no ww spelling (D3) —
|
||||||
// ww route into that dup is blocked today (re-probed post-C3,
|
// fresh slice header + spread-append, the #35-landed deref-spine
|
||||||
// PB7): the deref-spine spread SOURCE is loud-rejected (#35),
|
// source. The ok/defer-if frees (ha:570/573) drop: free() is the
|
||||||
// the per-element append is loud-rejected (#34 struct element
|
// documented no-op (#27; see finish()).
|
||||||
// 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)");
|
|
||||||
};
|
|
||||||
let captures: []capture;
|
let captures: []capture;
|
||||||
|
append(captures, (*threads)[parent_idx].captures...);
|
||||||
let rep_counters: []size;
|
let rep_counters: []size;
|
||||||
|
append(rep_counters, (*threads)[parent_idx].rep_counters...);
|
||||||
|
|
||||||
append(*threads, thread {
|
append(*threads, thread {
|
||||||
pc = new_pc,
|
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
|
// add_thread (regex.ha:557-587): same-pc dedup suppression fires only
|
||||||
// when the existing thread is unmatched AND started strictly earlier
|
// when the existing thread is unmatched AND started strictly earlier
|
||||||
// than the parent (ha:561-565); otherwise the child appends,
|
// than the parent (ha:561-565); otherwise the child appends,
|
||||||
// inheriting the parent's start/matched/failed with fresh empty
|
// inheriting the parent's start/matched/failed with DUPLICATED
|
||||||
// capture/rep_counter headers and a zeroed root_capture. The
|
// capture/rep_counter slices (empty parent → empty dup, ha:569/572)
|
||||||
// capture-dup loud bound must NOT fire on these empty-caps parents.
|
// and a zeroed root_capture.
|
||||||
@test fn add_thread_dedup_inherit() void = {
|
@test fn add_thread_dedup_inherit() void = {
|
||||||
let ts: []thread = [];
|
let ts: []thread = [];
|
||||||
append(ts, thread { pc = 0, start_idx = 5, start_bytesize = 4,
|
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(); };
|
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
|
// run_thread (regex.ha:589-742) driven directly over compile("ab")'s
|
||||||
// real program [skip, lit 'a', lit 'b', match(false)] — the arms
|
// real program [skip, lit 'a', lit 'b', match(false)] — the arms
|
||||||
// fold-2a can emit. Phases: parked-skip spawn (len 1→2, parent pc
|
// 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 = 34; fold4_compile_errors();
|
||||||
signalled = 35; fold4_find_cases();
|
signalled = 35; fold4_find_cases();
|
||||||
signalled = 36; fold4_findall();
|
signalled = 36; fold4_findall();
|
||||||
|
signalled = 37; add_thread_dup_independence();
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user