regex: fold 5b — parse_repetition leaf (ha:486-545)

Dead until the { arm lands (tranche-A precedent); 13-row direct
private-fn table, error texts byte-exact. Hare's verbatim
((void|size),(void|size),size) tuple return can't cross a union
boundary yet — cstage's (tuple|error) return store is cgen-unwired and
wwstage's variant-match rejects the tuple case arm (filed #47) — so it
respells as the private repparts struct per the scope-fold5 §3
pre-signed fallback; graduates back to the tuple when #47 closes.
Riders: call-result .N tuple read loud-rejects (filed #48, bind-first
local); strings.index's standing i32 convention (#8) stays internal,
widened at each size boundary; ha:494's same-name re-bind is rejected,
second local feb.
This commit is contained in:
2026-06-05 04:33:33 +09:00
parent 26d375410e
commit c0c15945be
2 changed files with 180 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ package regex;
import bufio;
import io;
import memio;
import strconv;
import strings;
import types;
import encoding.utf8;
@@ -628,6 +629,100 @@ export fn compile(expr: str) (regex | error | nomem) = {
};
};
// parse_repetition's Hare-verbatim return is the 3-tuple
// ((void | size), (void | size), size) (regex.ha:488); a tuple with
// tagged elements can't yet cross a union boundary in ww — cstage's
// return store into (tuple | error) is cgen-unwired
// (cg_widen_tagged_store) and wwstage's variant-match rejects the
// tuple case arm (filed, ww-core #47) — so the tuple respells as this
// private struct, positionally: .0 → min, .1 → max, .2 → replen.
// GRADUATION: when #47 closes, repparts reverts to the verbatim tuple
// with the test rows unchanged.
type repparts = struct {
min: (void | size),
max: (void | size),
replen: size,
};
// ref/hare/regex/regex.ha:486-545, whole. strings.index returns
// (i32 | void) in ww — the standing lib-wide str-index-i32 convention
// (lib/strings/strings.ww:54, #8) vs Hare's (size | void)
// (ref/hare/strings/index.ha:10) — so the index results stay i32
// INTERNALLY (the ha:499 comparison is same-domain) and each value
// widens explicitly at the boundary Hare types as size (the repparts
// field stores). ha:494 re-binds `first_endbrace` over itself; ww
// rejects a same-scope re-decl ("let redeclared"), so the unwrapped
// value is `feb`.
fn parse_repetition(s: str) (repparts | error) = {
let first_comma: (i32 | void) = strings.index(s, ",");
let first_endbrace: (i32 | void) = strings.index(s, "}");
if (first_endbrace is void) {
return "Repetition expression syntax error '{n}'": error;
};
let feb: i32 = first_endbrace as i32;
let min_str: str = "";
let max_str: str = "";
let is_single_arg: bool = false;
if (first_comma is void || feb < first_comma as i32) {
let cut: (str, str) = strings.cut(s, "}");
min_str = cut.0;
max_str = cut.0;
is_single_arg = true;
} else {
let cut: (str, str) = strings.cut(s, ",");
min_str = cut.0;
// Hare reads .0 straight off the call (ha:507); a tuple-elem
// read on a CALL result is loud-unsupported (filed, ww-core
// #48) — bound local first.
let cut2: (str, str) = strings.cut(cut.1, "}");
max_str = cut2.0;
};
let min: (void | size) = void;
let max: (void | size) = void;
if (min_str.len > 0) {
// Hare's match-EXPRESSION with if-yields (ha:514-525)
// spells as a statement match (no yield-expr; the search
// scanrune precedent, #51). stoi's base is explicit — ww
// has no default args (the newscanner precedent; cf
// lib/encoding/hex/hex.ww:131).
match (strconv.stoi(min_str, strconv.base.DEC)) {
case let res: int => {
if (res < 0) {
return "Negative repetition count '{-n}'": error;
};
min = (res: size);
};
case => return "Repetition expression syntax error '{n}'": error;
};
} else {
// `min = 0;` (ha:524): an untyped-int widen-store into
// (void | size) mis-tags (#33) — cast form pinned.
min = (0: size);
};
if (max_str.len > 0) {
match (strconv.stoi(max_str, strconv.base.DEC)) {
case let res: int => {
if (res < 0) {
return "Negative repetition count '{-n}'": error;
};
max = (res: size);
};
case => return "Repetition expression syntax error '{n}'": error;
};
};
// Hare's if-EXPRESSION (ha:539-544) spells as a statement.
let rep_len: size = (min_str.len: size);
if (!is_single_arg) {
rep_len = (min_str.len: size) + 1 + (max_str.len: size);
};
return repparts { min = min, max = max, replen = rep_len };
};
// ref/hare/regex/regex.ha:547-551, verbatim — both free()s are the
// documented no-op (#27; see finish()), kept for source parity. ww
// has no pointer auto-deref, so Hare's `threads[i]` spells