* feat(adr-0184): add semantic-state helper package * feat(adr-0184): add referent binding helpers * feat(adr-0184): add change cue helpers * refactor(adr-0184): use semantic-state helpers in accumulation * test(adr-0184): cover semantic-state helper guards
110 lines
2.9 KiB
Python
110 lines
2.9 KiB
Python
"""ADR-0184 S1 — conservative change-cue helpers.
|
|
|
|
These helpers are behavior-equivalent extractions from
|
|
:mod:`generate.derivation.accumulate`. They classify only the closed gain/loss
|
|
cue set already used by GB-3b.1 and return ``None`` when polarity is absent or
|
|
ambiguous. Cue hits propose semantic change frames; they never commit answers.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Final
|
|
|
|
from generate.math_roundtrip import _tokens
|
|
|
|
# Closed change-cue lexeme sets (ADR-0165: lexemes, not grammar templates; refined
|
|
# by the CP ledger, not asserted complete). Sorted use keeps cue selection stable.
|
|
GAIN_VERBS: Final[frozenset[str]] = frozenset(
|
|
{
|
|
"buys",
|
|
"bought",
|
|
"gets",
|
|
"got",
|
|
"finds",
|
|
"found",
|
|
"picks",
|
|
"picked",
|
|
"earns",
|
|
"earned",
|
|
"receives",
|
|
"received",
|
|
"collects",
|
|
"collected",
|
|
"wins",
|
|
"won",
|
|
"makes",
|
|
"made",
|
|
"gains",
|
|
"gained",
|
|
"adds",
|
|
"added",
|
|
}
|
|
)
|
|
LOSS_VERBS: Final[frozenset[str]] = frozenset(
|
|
{
|
|
"loses",
|
|
"lost",
|
|
"spends",
|
|
"spent",
|
|
"uses",
|
|
"used",
|
|
"eats",
|
|
"ate",
|
|
"sells",
|
|
"sold",
|
|
"donates",
|
|
"donated",
|
|
"drops",
|
|
"dropped",
|
|
"removes",
|
|
"removed",
|
|
"breaks",
|
|
"broke",
|
|
}
|
|
)
|
|
|
|
|
|
def classify_change_polarity(clause: str) -> int | None:
|
|
"""Return ``+1`` for gain, ``-1`` for loss, or ``None`` to refuse.
|
|
|
|
Ordering is behavior-equivalent with the prior accumulation helper:
|
|
|
|
* ``more`` present -> gain;
|
|
* else an unambiguous loss verb -> loss;
|
|
* else ``gives``/``gave`` with ``to``/``away`` -> loss;
|
|
* else an unambiguous gain verb -> gain;
|
|
* else refuse by returning ``None``.
|
|
"""
|
|
|
|
tokens = set(_tokens(clause))
|
|
if "more" in tokens:
|
|
return +1
|
|
loss = bool(LOSS_VERBS & tokens)
|
|
gain = bool(GAIN_VERBS & tokens)
|
|
gives = "gives" in tokens or "gave" in tokens
|
|
directional = "to" in tokens or "away" in tokens
|
|
if loss and not gain:
|
|
return -1
|
|
if gives and directional and not gain and not loss:
|
|
return -1
|
|
if gain and not loss:
|
|
return +1
|
|
return None
|
|
|
|
|
|
def select_change_cue(clause: str, polarity: int) -> str:
|
|
"""Return a grounded cue lexeme present in ``clause`` for ``polarity``.
|
|
|
|
The returned cue is consumed by the existing derivation verifier's cue-grounding
|
|
clause. This function assumes ``polarity`` was produced by
|
|
:func:`classify_change_polarity` for the same clause.
|
|
"""
|
|
|
|
tokens = set(_tokens(clause))
|
|
if "more" in tokens and polarity > 0:
|
|
return "more"
|
|
verbs = GAIN_VERBS if polarity > 0 else LOSS_VERBS
|
|
present = sorted(verbs & tokens)
|
|
if present:
|
|
return present[0]
|
|
return "gives" # the only remaining licensed loss path (gives … to/away)
|