Whole file
rhasspy/unicode-rbnf
The author described this change as “Fix zero remainder rules”. It counts as a record because the check below fails on the code as it stood at f0010272f and passes on 9b64edcff, with nothing else changed between the two runs.
Projectrhasspy/unicode-rbnf
Fix saved2024-07-10
Sharing licenceMIT · LICENSE.md
Change size+15 −7
What the code was meant to do, written into the code itself as a save note
Fix zero remainder rules
The change
| 50 | 50 | class SubType(str, Enum): | |
| 51 | 51 | """Type of substitution.""" | |
| 52 | 52 | ||
| 53 | - | REMAINER = "remainder" | |
| 53 | + | REMAINDER = "remainder" | |
| 54 | 54 | """Use remainder for rule value.""" | |
| 55 | 55 | ||
| 56 | 56 | QUOTIENT = "quotient" | |
| ⋯ | |||
| 91 | 91 | TEXT = "text" | |
| 92 | 92 | SUB_OPTIONAL_BEFORE = "optional_before" | |
| 93 | 93 | SUB_OPTIONAL_AFTER = "optional_after" | |
| 94 | - | SUB_REMAINER = "remainder" | |
| 94 | + | SUB_REMAINDER = "remainder" | |
| 95 | 95 | SUB_QUOTIENT = "quotient" | |
| 96 | 96 | SUB_RULESET_NAME = "sub_ruleset_name" | |
| 97 | 97 | REPLACE_RULESET_NAME = "replace_ruleset_name" | |
| ⋯ | |||
| 164 | 164 | if c in (">", "→"): | |
| 165 | 165 | # Divide the number by the rule's divisor and format the remainder | |
| 166 | 166 | if state in {ParseState.TEXT, ParseState.SUB_OPTIONAL_BEFORE}: | |
| 167 | - | state = ParseState.SUB_REMAINER | |
| 167 | + | state = ParseState.SUB_REMAINDER | |
| 168 | 168 | part = SubRulePart( | |
| 169 | - | SubType.REMAINER, | |
| 169 | + | SubType.REMAINDER, | |
| 170 | 170 | is_optional=is_sub_optional, | |
| 171 | 171 | text_before=sub_text_before, | |
| 172 | 172 | ) | |
| 173 | 173 | rule.parts.append(part) | |
| 174 | 174 | sub_text_before = "" | |
| 175 | - | elif state in {ParseState.SUB_REMAINER, ParseState.SUB_RULESET_NAME}: | |
| 175 | + | elif state in {ParseState.SUB_REMAINDER, ParseState.SUB_RULESET_NAME}: | |
| 176 | 176 | if is_sub_optional: | |
| 177 | 177 | state = ParseState.SUB_OPTIONAL_AFTER | |
| 178 | 178 | else: | |
| ⋯ | |||
| 196 | 196 | raise ValueError(f"Got {c} in {state}") | |
| 197 | 197 | elif c == "%": | |
| 198 | 198 | # =%rule_name= replacement | |
| 199 | - | if state in {ParseState.SUB_QUOTIENT, ParseState.SUB_REMAINER}: | |
| 199 | + | if state in {ParseState.SUB_QUOTIENT, ParseState.SUB_REMAINDER}: | |
| 200 | 200 | assert isinstance(part, SubRulePart) | |
| 201 | 201 | state = ParseState.SUB_RULESET_NAME | |
| 202 | 202 | part.ruleset_name = "" | |
| ⋯ | |||
| 493 | 493 | yield part.text | |
| 494 | 494 | elif isinstance(part, SubRulePart): | |
| 495 | 495 | if (part.type == SubType.QUOTIENT) and (q > 0): | |
| 496 | + | if (q == 0) and (part.ruleset_name is None): | |
| 497 | + | # Rulesets can use quotients of zero | |
| 498 | + | continue | |
| 499 | + | ||
| 496 | 500 | if part.text_before: | |
| 497 | 501 | yield part.text_before | |
| 498 | 502 | yield from self.iter_format_number( | |
| ⋯ | |||
| 500 | 504 | ) | |
| 501 | 505 | if part.text_after: | |
| 502 | 506 | yield part.text_after | |
| 503 | - | elif (part.type == SubType.REMAINER) and (r > 0): | |
| 507 | + | elif part.type == SubType.REMAINDER: | |
| 508 | + | if (r == 0) and (part.ruleset_name is None): | |
| 509 | + | # Rulesets can use remainders of zero | |
| 510 | + | continue | |
| 511 | + | ||
| 504 | 512 | if part.text_before: | |
| 505 | 513 | yield part.text_before | |
| 506 | 514 | yield from self.iter_format_number( | |
The check that tells the two apart
fail→pass·tests/test_en.py::test_english
Check file tests/test_en.py, taken without changes from the fix and copied onto the older code, so the exact same check runs against both versions.
Origin and history
The code before itf0010272f1076cfc5abc179e21c418ed689857d5
Broken version dated2024-02-06
Moduleunicode_rbnf.engine
Units changedParseState, RbnfEngine, RbnfRule, SubType
Fingerprint0620089ccc4e8646
Checked2026-08-18 by goldset/0.1
Every field above is generated by our program. None of it is written by hand.