Repository navigation
HermitCrab: keep final-template pruning on when only lexical entries are partial - #553
johnml1135 wants to merge 3 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #553 +/- ##
==========================================
+ Coverage 74.57% 74.73% +0.16%
==========================================
Files 453 453
Lines 38342 38342
Branches 5284 5284
==========================================
+ Hits 28593 28656 +63
+ Misses 8583 8532 -51
+ Partials 1166 1154 -12 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This comment has been minimized.
This comment has been minimized.
57a3b7e to
f1ac77e
Compare
PR #553 shows ignoring partial lexical entries in the final-template prune guard is safe, so Sena gets the prune once it merges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| /// template that follows a non-template rule only when this is false. Partial lexical entries do not | ||
| /// count: <see cref="SynthesisAffixTemplatesRule"/> applies no template to a word whose root is partial, | ||
| /// and when it substitutes an inflectional-family relative for the root, the relative's word is | ||
| /// non-partial, so a following non-partial rule is blocked either way. |
There was a problem hiding this comment.
FYI: F3. The summary's reasoning leaves out blocking. A blockable template slot rule can call Word.CheckBlocking (SynthesisAffixProcessRule.cs:199), which swaps in a partial family relative. SynthesisAffixTemplatesRule.cs:45 then marks that word final, and a non-partial rule may follow it because input.IsPartial (SynthesisAffixProcessRule.cs:67). The output matches the relative analyzed directly as a root, so I expect no parse is lost. That is Unverified. Consider cutting the summary to its first sentence.
This comment has been minimized.
This comment has been minimized.
|
I think that you can remove Morpher.IsPartial in the code and in the comments, since it is superceded by Morpher.HasPartialRules. |
The analysis-side final-template prune (491) was switched off whenever Morpher.IsPartial was true, and IsPartial counts partial lexical entries. Grammars with partial entries but no partial rules therefore never pruned. A partial entry cannot make the prune lose a parse. A word takes its partiality from its root, but SynthesisAffixTemplatesRule applies no template to a word whose root morpheme is partial, so a derivation that unapplies a final template can never end at a partial root. Partial rules differ: they make a word partial after the template has applied, which lets a non-partial rule follow the final template, so they still disable the prune. Add Morpher.HasPartialRules and read it in the two analysis guards. IsPartial keeps its meaning. Lexical-pattern guesses were already excluded from IsPartial and pruned. Closes #551 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The summary claimed a derivation that unapplies a final template can never end at a partial root. SynthesisAffixTemplatesRule.Apply calls ChooseInflectionalStem before the root check, so a partial root can take a template through a non-partial family relative. State the contract and why the prune stays sound. The test left the partial "sab" entry in the shared Language; remove it in a finally block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nothing reads it now that the analysis-side final-template checks use HasPartialRules. The three test preconditions assert GetPartialMorphemes() directly. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
f1ac77e to
b507a89
Compare
Done in b507a89: I removed |
| /// non-partial, so a following non-partial rule is blocked either way. | ||
| /// </summary> | ||
| public bool IsPartial { get; } | ||
| public bool HasPartialRules { get; } |
There was a problem hiding this comment.
FYI: F4. Removing Morpher.IsPartial is a breaking public API change. It shipped in v3.9.5 and v3.9.6 (added in #491), so a consumer that reads it fails to compile, or throws MissingMethodException on a drop-in upgrade. I know this was requested. If no consumer such as FieldWorks reads it, nothing more is needed. Otherwise keep it as an [Obsolete] property for one release. Unverified: I did not search FieldWorks.
There was a problem hiding this comment.
FieldWorks doesn't read it. I think that nothing further needs to be done.
| var morpher = new Morpher(TraceManager, Language); | ||
| Assert.That(morpher.IsPartial, Is.True); | ||
| Assert.That(morpher.GetPartialMorphemes(), Is.Not.Empty); | ||
| morpher.AlwaysEnforceFinalTemplates = true; |
There was a problem hiding this comment.
FYI: F5. No test covers the analysis-side || _morpher.AlwaysEnforceFinalTemplates override any more. The base grammar's only partial morphemes are lexical entries, so HasPartialRules is false here and at lines 697-743 and 860, and the flag has no effect on analysis. Your new test shows the same depth-2 prune at line 547 with the flag off. A partial rule with the flag on (for example, at the end of the new test) would cover it.
| Is.Not.Empty, | ||
| "entry 54 has an empty syntactic feature structure" | ||
| ); | ||
| morpher.AlwaysEnforceFinalTemplates = alwaysEnforceFinalTemplates; |
There was a problem hiding this comment.
FYI: F6. The two TestCases now run the same path. Entry 54 is a LexEntry, so HasPartialRules is false, and analysis prunes final templates whether alwaysEnforceFinalTemplates is true or false. Copy-pruning with the final-template prune off is no longer tested. A partial rule in the fixture would restore the false case.
Public API: Findings: F1 addressed, F2 addressed, F3 open (unverified), F4 new (unverified), F5 new, F6 new. Reviewed at b507a89 |
Closes #551.
Problem
The final-template prune from #491 is skipped whenever
Morpher.IsPartialis true, andIsPartialcounts partial lexical entries. A grammar with partial entries and no partial rules never prunes.Why entries can be ignored
#551 suspected this was unsound: a word inherits partiality from its root (
Word.cs:150), and synthesis lets a non-partial rule follow a final template when the input word is partial (SynthesisAffixProcessRule.cs:67). That would let a partial root license a derivation the prune rejects.It does not, because
SynthesisAffixTemplatesRuleskips every template wheninput.RootAllomorph.Morpheme.IsPartial. No template, final or not, applies to a word whose root is partial. One nuance:SynthesisAffixTemplatesRule.ApplycallsChooseInflectionalStembefore that check, so a partial root with a non-partialLexFamilyrelative can still take a template, with the relative as the root. The prune stays sound because that word is non-partial, so a following non-partial rule is blocked either way.Partial rules are different. A partial rule makes the word partial after the root, and the template can still apply, so a non-partial rule can then follow a final template. They keep disabling the prune.
Change
Morpher.HasPartialRules: true if any partial morpheme is not aLexEntry.IsPartialis removed; nothing reads it now.AnalysisAffixTemplateRuleandAnalysisAffixProcessRulereadHasPartialRulesinstead ofIsPartial.AffixTemplateTests.PartialLexicalEntryDoesNotDisableEarlyPruningOfFinalTemplate: with a partial entry and no partial rule the prune fires (trace depth 2) and a partial root parses the same as before (sab,sabvparse;sabd,sabdvdo not). With a partial rule the prune stays off andsagdvstill parses as32 PAST NOM. The test fails with the old guard (the prune is reached at depth 6 instead of 2).Evidence
conformance/edge-cases/final-template-partial-rooton Add a conformance suite for HermitCrab, with its adequacy argument #480 (integrate-conformance-frameworkat fa8d9b2) has partial roots in a lower stratum and in the template's own stratum. Every final-template string on a partial root has no analysis, with non-partial controls../local_check.sh --agent-strictpassed on this branch before it was rebased onto master. After the rebase and theIsPartialremoval, onlyAffixTemplateTestsandPruneDisagreeingCopiesTestswere rerun (46/46).Not covered
Inflectional-family stem substitution (
ChooseInflectionalStem) replaces the root with a family relative before the template check. I traced it by reading only (see the nuance above). No fixture exercises it.Lexical-pattern guesses were already excluded from the partial-morpheme set and pruned.
🤖 Generated with Claude Code
This change is