From 18910f2b76876442a52d3b55bba7df8bb0fd4543 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Thu, 15 Jan 2026 07:53:48 -0800 Subject: [PATCH 1/6] LT-22353: Fix bug in NarrowAnalysisRewriteRuleSpec --- .../NarrowAnalysisRewriteRuleSpec.cs | 1 + .../PhonologicalRules/RewriteRuleTests.cs | 157 ++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/NarrowAnalysisRewriteRuleSpec.cs b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/NarrowAnalysisRewriteRuleSpec.cs index 820842c91..469f3020a 100644 --- a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/NarrowAnalysisRewriteRuleSpec.cs +++ b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/NarrowAnalysisRewriteRuleSpec.cs @@ -51,6 +51,7 @@ Constraint constraint in _analysisRhs.Children.Cast.New().Annotation(highVowel).Annotation(highVowel).Value + }; + Allophonic.PhonologicalRules.Add(rule1); + rule1.Subrules.Add( + new RewriteSubrule + { + LeftEnvironment = Pattern.New().Annotation(backRndVowel).Value + } + ); + + var morpher = new Morpher(TraceManager, Language); + AssertMorphsEqual(morpher.ParseWord("bubu"), "27", "19"); + } + + [Test] + public void MergeRules() + { + var highVowel = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons-") + .Symbol("voc+") + .Symbol("high+") + .Value; + var backRnd = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("back+") + .Symbol("round+") + .Value; + var backRndVowel = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons-") + .Symbol("voc+") + .Symbol("back+") + .Symbol("round+") + .Value; + var t = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons+") + .Symbol("alveolar") + .Symbol("del_rel-") + .Symbol("asp-") + .Symbol("vd-") + .Symbol("strident-") + .Value; + + var rule1 = new RewriteRule + { + Name = "rule1", + Lhs = Pattern.New().Annotation(highVowel).Annotation(highVowel).Value + }; + Allophonic.PhonologicalRules.Add(rule1); + rule1.Subrules.Add( + new RewriteSubrule + { + Rhs = Pattern.New().Annotation(t).Value, + LeftEnvironment = Pattern.New().Annotation(backRndVowel).Value + } + ); + + var morpher = new Morpher(TraceManager, Language); + AssertMorphsEqual(morpher.ParseWord("butbut"), "27"); + } + + [Test] + public void MultipleMergeRules() + { + var highVowel = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons-") + .Symbol("voc+") + .Symbol("high+") + .Value; + var backRnd = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("back+") + .Symbol("round+") + .Value; + var backRndVowel = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons-") + .Symbol("voc+") + .Symbol("back+") + .Symbol("round+") + .Value; + var t = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons+") + .Symbol("alveolar") + .Symbol("del_rel-") + .Symbol("asp-") + .Symbol("vd-") + .Symbol("strident-") + .Value; + + var rule1 = new RewriteRule + { + Name = "rule1", + Lhs = Pattern.New().Annotation(backRndVowel).Annotation(highVowel).Annotation(highVowel).Value + }; + Allophonic.PhonologicalRules.Add(rule1); + rule1.Subrules.Add( + new RewriteSubrule + { + Rhs = Pattern.New().Annotation(t).Annotation(t).Value, + } + ); + + var morpher = new Morpher(TraceManager, Language); + AssertMorphsEqual(morpher.ParseWord("bttbtt"), "27"); + } + [Test] public void BoundaryRules() { From 12d018ad80f3b789e1b325451c251d79420a9a85 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Thu, 15 Jan 2026 09:17:47 -0800 Subject: [PATCH 2/6] Fix formatting --- .../PhonologicalRules/RewriteRuleTests.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs b/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs index e2d3df81a..0e34915c8 100644 --- a/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs +++ b/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs @@ -467,10 +467,7 @@ public void MultipleDeletionRules() }; Allophonic.PhonologicalRules.Add(rule1); rule1.Subrules.Add( - new RewriteSubrule - { - LeftEnvironment = Pattern.New().Annotation(backRndVowel).Value - } + new RewriteSubrule { LeftEnvironment = Pattern.New().Annotation(backRndVowel).Value } ); var morpher = new Morpher(TraceManager, Language); @@ -572,10 +569,7 @@ public void MultipleMergeRules() }; Allophonic.PhonologicalRules.Add(rule1); rule1.Subrules.Add( - new RewriteSubrule - { - Rhs = Pattern.New().Annotation(t).Annotation(t).Value, - } + new RewriteSubrule { Rhs = Pattern.New().Annotation(t).Annotation(t).Value } ); var morpher = new Morpher(TraceManager, Language); From 1cb246b22e4ffe33f2700a56d1764f05ff71f0d2 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Thu, 15 Jan 2026 10:20:13 -0800 Subject: [PATCH 3/6] Fix formatting --- .../PhonologicalRules/RewriteRuleTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs b/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs index 0e34915c8..4dd672955 100644 --- a/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs +++ b/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs @@ -565,7 +565,12 @@ public void MultipleMergeRules() var rule1 = new RewriteRule { Name = "rule1", - Lhs = Pattern.New().Annotation(backRndVowel).Annotation(highVowel).Annotation(highVowel).Value + Lhs = Pattern + .New() + .Annotation(backRndVowel) + .Annotation(highVowel) + .Annotation(highVowel) + .Value }; Allophonic.PhonologicalRules.Add(rule1); rule1.Subrules.Add( From fd619034a8da3b2bd035e82ad54faa5ea6629293 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Mon, 19 Jan 2026 09:37:31 -0800 Subject: [PATCH 4/6] Add code for expanding rules --- .../PhonologicalRules/AnalysisRewriteRule.cs | 8 +++ .../SynthesisRewriteRuleSpec.cs | 14 +++++ .../PhonologicalRules/RewriteRuleTests.cs | 52 +++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/AnalysisRewriteRule.cs b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/AnalysisRewriteRule.cs index d680e23ea..bc867a8e8 100644 --- a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/AnalysisRewriteRule.cs +++ b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/AnalysisRewriteRule.cs @@ -81,6 +81,14 @@ Constraint constraint in sr.Rhs.Children.Cast< if (_rule.ApplicationMode == RewriteApplicationMode.Simultaneous) reapplyType = ReapplyType.SelfOpaquing; } + else + { + // NarrowAnalysisRewriteRuleSpec works for expansion, too. + ruleSpec = new NarrowAnalysisRewriteRuleSpec(settings, _rule.Lhs, sr); + mode = RewriteApplicationMode.Simultaneous; + reapplyType = ReapplyType.Deletion; + + } Debug.Assert(ruleSpec != null); PhonologicalPatternRule patternRule = null; diff --git a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/SynthesisRewriteRuleSpec.cs b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/SynthesisRewriteRuleSpec.cs index 661f2729c..b9eb086eb 100644 --- a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/SynthesisRewriteRuleSpec.cs +++ b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/SynthesisRewriteRuleSpec.cs @@ -65,6 +65,20 @@ IEnumerable subrules new EpenthesisSynthesisRewriteSubruleSpec(matcherSettings, isIterative, subrule, i) ); } + else + { + // NarrowSynthesisRewriteSubruleSpec works for expansion, too. + SubruleSpecs.Add( + new NarrowSynthesisRewriteSubruleSpec( + matcherSettings, + isIterative, + lhs.Children.Count, + subrule, + i + ) + ); + + } i++; } } diff --git a/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs b/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs index 4dd672955..e88c57ad2 100644 --- a/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs +++ b/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs @@ -581,6 +581,58 @@ public void MultipleMergeRules() AssertMorphsEqual(morpher.ParseWord("bttbtt"), "27"); } + [Test] + public void ExpandRules() + { + var highVowel = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons-") + .Symbol("voc+") + .Symbol("high+") + .Value; + var backRnd = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("back+") + .Symbol("round+") + .Value; + var backRndVowel = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons-") + .Symbol("voc+") + .Symbol("back+") + .Symbol("round+") + .Value; + var t = FeatureStruct + .New(Language.PhonologicalFeatureSystem) + .Symbol(HCFeatureSystem.Segment) + .Symbol("cons+") + .Symbol("alveolar") + .Symbol("del_rel-") + .Symbol("asp-") + .Symbol("vd-") + .Symbol("strident-") + .Value; + + var rule1 = new RewriteRule + { + Name = "rule1", + Lhs = Pattern.New().Annotation(backRndVowel).Value, + }; + Allophonic.PhonologicalRules.Add(rule1); + rule1.Subrules.Add( + new RewriteSubrule + { + Rhs = Pattern.New().Annotation(highVowel).Annotation(highVowel).Value + } + ); + + var morpher = new Morpher(TraceManager, Language); + AssertMorphsEqual(morpher.ParseWord("biiiibiiii"), "27"); + } + [Test] public void BoundaryRules() { From 87ac92d6cc00a159f13705241e66949ee8f13c49 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Mon, 19 Jan 2026 10:21:56 -0800 Subject: [PATCH 5/6] Fix formatting --- .../PhonologicalRules/AnalysisRewriteRule.cs | 1 - .../PhonologicalRules/SynthesisRewriteRuleSpec.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/AnalysisRewriteRule.cs b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/AnalysisRewriteRule.cs index bc867a8e8..1915e5ccd 100644 --- a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/AnalysisRewriteRule.cs +++ b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/AnalysisRewriteRule.cs @@ -87,7 +87,6 @@ Constraint constraint in sr.Rhs.Children.Cast< ruleSpec = new NarrowAnalysisRewriteRuleSpec(settings, _rule.Lhs, sr); mode = RewriteApplicationMode.Simultaneous; reapplyType = ReapplyType.Deletion; - } Debug.Assert(ruleSpec != null); diff --git a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/SynthesisRewriteRuleSpec.cs b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/SynthesisRewriteRuleSpec.cs index b9eb086eb..87fa10955 100644 --- a/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/SynthesisRewriteRuleSpec.cs +++ b/src/SIL.Machine.Morphology.HermitCrab/PhonologicalRules/SynthesisRewriteRuleSpec.cs @@ -77,7 +77,6 @@ IEnumerable subrules i ) ); - } i++; } From 0f45197e8c77b58f85d8c73fa9c6587483c3ec95 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Tue, 20 Jan 2026 07:52:11 -0800 Subject: [PATCH 6/6] Remove unused variables --- .../PhonologicalRules/RewriteRuleTests.cs | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs b/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs index e88c57ad2..e79cc0d3a 100644 --- a/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs +++ b/tests/SIL.Machine.Morphology.HermitCrab.Tests/PhonologicalRules/RewriteRuleTests.cs @@ -435,12 +435,6 @@ public void MultipleDeletionRules() .Symbol("voc+") .Symbol("high+") .Value; - var backRnd = FeatureStruct - .New(Language.PhonologicalFeatureSystem) - .Symbol(HCFeatureSystem.Segment) - .Symbol("back+") - .Symbol("round+") - .Value; var backRndVowel = FeatureStruct .New(Language.PhonologicalFeatureSystem) .Symbol(HCFeatureSystem.Segment) @@ -449,16 +443,6 @@ public void MultipleDeletionRules() .Symbol("back+") .Symbol("round+") .Value; - var t = FeatureStruct - .New(Language.PhonologicalFeatureSystem) - .Symbol(HCFeatureSystem.Segment) - .Symbol("cons+") - .Symbol("alveolar") - .Symbol("del_rel-") - .Symbol("asp-") - .Symbol("vd-") - .Symbol("strident-") - .Value; var rule1 = new RewriteRule { @@ -484,12 +468,6 @@ public void MergeRules() .Symbol("voc+") .Symbol("high+") .Value; - var backRnd = FeatureStruct - .New(Language.PhonologicalFeatureSystem) - .Symbol(HCFeatureSystem.Segment) - .Symbol("back+") - .Symbol("round+") - .Value; var backRndVowel = FeatureStruct .New(Language.PhonologicalFeatureSystem) .Symbol(HCFeatureSystem.Segment) @@ -537,12 +515,6 @@ public void MultipleMergeRules() .Symbol("voc+") .Symbol("high+") .Value; - var backRnd = FeatureStruct - .New(Language.PhonologicalFeatureSystem) - .Symbol(HCFeatureSystem.Segment) - .Symbol("back+") - .Symbol("round+") - .Value; var backRndVowel = FeatureStruct .New(Language.PhonologicalFeatureSystem) .Symbol(HCFeatureSystem.Segment) @@ -591,12 +563,6 @@ public void ExpandRules() .Symbol("voc+") .Symbol("high+") .Value; - var backRnd = FeatureStruct - .New(Language.PhonologicalFeatureSystem) - .Symbol(HCFeatureSystem.Segment) - .Symbol("back+") - .Symbol("round+") - .Value; var backRndVowel = FeatureStruct .New(Language.PhonologicalFeatureSystem) .Symbol(HCFeatureSystem.Segment) @@ -605,16 +571,6 @@ public void ExpandRules() .Symbol("back+") .Symbol("round+") .Value; - var t = FeatureStruct - .New(Language.PhonologicalFeatureSystem) - .Symbol(HCFeatureSystem.Segment) - .Symbol("cons+") - .Symbol("alveolar") - .Symbol("del_rel-") - .Symbol("asp-") - .Symbol("vd-") - .Symbol("strident-") - .Value; var rule1 = new RewriteRule {