feat(syntax): _resolve_inline_pick — uniform random branch + recurse

This commit is contained in:
DumiFlex
2026-04-28 05:39:27 +03:00
parent 51a859b095
commit 2f489002e1
2 changed files with 67 additions and 2 deletions
+19 -2
View File
@@ -49,8 +49,7 @@ def _resolve_tokens(
elif tok.kind == TokenKind.REF:
parts.append(_resolve_ref(tok, ctx, depth, visited))
elif tok.kind == TokenKind.DP_BRACE:
# Implemented in Task 10
raise NotImplementedError("DP_BRACE resolution lands in Task 10")
parts.append(_resolve_inline_pick(tok, ctx, depth, visited))
elif tok.kind == TokenKind.DP_MULTI:
# Implemented in Task 11
raise NotImplementedError("DP_MULTI resolution lands in Task 11")
@@ -104,6 +103,24 @@ def _pick_weighted(options: list[dict], rng) -> dict | None:
return options[-1]
def _resolve_inline_pick(
tok: Token,
ctx: ResolveContext,
depth: int,
visited: tuple[str, ...],
) -> str:
"""Pick one branch uniformly at random; recursively resolve its content."""
branches: list[str] = tok.meta.get("branches", [])
if not branches:
return ""
chosen_idx = ctx.rng.randrange(len(branches))
chosen = branches[chosen_idx]
if not chosen:
return ""
nested_tokens = tokenize_text(chosen)
return _resolve_tokens(nested_tokens, ctx, depth=depth, visited=visited)
def _resolve_ref(
tok: Token,
ctx: ResolveContext,
+48
View File
@@ -233,3 +233,51 @@ def test_resolve_ref_repeated_resolves_independently():
assert parts[1] in {"A", "B", "C", "D"}
# Pinned from first observed run with seed=42:
assert out == "C-A"
# ---------------------------------------------------------------------------
# DP_BRACE resolution tests (Task 10)
# ---------------------------------------------------------------------------
def test_resolve_inline_pick_one_of_branches():
"""Single-pick must return one of the branches verbatim (uniformly)."""
ctx = _ctx(rng=random.Random(0))
out = resolve_text("{a|b|c}", ctx)
assert out in {"a", "b", "c"}
def test_resolve_inline_pick_deterministic_with_seed():
ctx1 = _ctx(rng=random.Random(42))
ctx2 = _ctx(rng=random.Random(42))
assert resolve_text("{x|y|z}", ctx1) == resolve_text("{x|y|z}", ctx2)
def test_resolve_inline_pick_nested_inline():
ctx = _ctx(rng=random.Random(0))
out = resolve_text("{a|{b|c}|d}", ctx)
assert out in {"a", "b", "c", "d"}
def test_resolve_inline_pick_with_var_in_branch():
ctx = _ctx(rng=random.Random(0), _vars={"color": "red"})
out = resolve_text("{$color|blue|green}", ctx)
# Pick depends on rng; result is one of the resolved branches.
assert out in {"red", "blue", "green"}
def test_resolve_inline_pick_empty_branch_valid():
"""An empty branch is a valid pick — emits empty string."""
# Force the empty branch by using a controlled rng. With seed=0 and
# 3 branches, observe which gets picked; if it's not the empty one,
# we still verify the resolver doesn't error.
ctx = _ctx(rng=random.Random(0))
out = resolve_text("{a||c}", ctx)
assert out in {"a", "", "c"}
def test_resolve_inline_pick_single_branch_is_text_token():
"""`{just_one}` is NOT a pick (no pipe) — tokenizer returns it as text,
so resolver passes through verbatim."""
ctx = _ctx()
assert resolve_text("{just_one}", ctx) == "{just_one}"