diff --git a/Src/LexText/ParserCore/FwXmlTraceManager.cs b/Src/LexText/ParserCore/FwXmlTraceManager.cs index e71dce1bab..4c8e8d29ec 100644 --- a/Src/LexText/ParserCore/FwXmlTraceManager.cs +++ b/Src/LexText/ParserCore/FwXmlTraceManager.cs @@ -69,7 +69,8 @@ public void BeginUnapplyTemplate(AffixTemplate template, Word input) CreateWordElement("Input", input, true))); } - public void EndUnapplyTemplate(AffixTemplate template, Word output, bool unapplied, FailureReason reason) + public void EndUnapplyTemplate(AffixTemplate template, Word output, bool unapplied, FailureReason reason, + object failureObj) { var trace = new XElement( "TemplateAnalysisTraceOut", @@ -82,6 +83,11 @@ public void EndUnapplyTemplate(AffixTemplate template, Word output, bool unappli new XElement("FailureReason", new XAttribute("type", "nonPartialRuleAfterFinalTemplate")) ); } + else if (reason == FailureReason.UnreachableRequiredSlot) + { + trace.Add(new XElement("FailureReason", new XAttribute("type", "unreachableRequiredSlot"), + new XElement("Slot", (failureObj as AffixTemplateSlot)?.Name))); + } ((XElement)output.CurrentTrace).Add(trace); } diff --git a/Src/LexText/ParserCore/ParserCoreTests/FwXmlTraceManagerTests.cs b/Src/LexText/ParserCore/ParserCoreTests/FwXmlTraceManagerTests.cs index 1f946b493c..6a5cff77e4 100644 --- a/Src/LexText/ParserCore/ParserCoreTests/FwXmlTraceManagerTests.cs +++ b/Src/LexText/ParserCore/ParserCoreTests/FwXmlTraceManagerTests.cs @@ -109,6 +109,21 @@ public void CompoundingFailure_UnknownReasonIsNotGuessedFromOperand() Assert.That((string)reason.Attribute("type"), Is.EqualTo("unknown")); } + /// Records which required slot made a template fail. + [Test] + public void TemplateFailure_RecordsUnreachableRequiredSlot() + { + var template = new AffixTemplate { Name = "synthetic template" }; + var slot = new AffixTemplateSlot(new AffixProcessRule()) { Name = "synthetic slot" }; + + m_manager.EndUnapplyTemplate(template, m_word, false, + FailureReason.UnreachableRequiredSlot, slot); + + var reason = m_trace.Element("TemplateAnalysisTraceOut").Element("FailureReason"); + Assert.That((string)reason.Attribute("type"), Is.EqualTo("unreachableRequiredSlot")); + Assert.That(reason.Element("Slot").Value, Is.EqualTo("synthetic slot")); + } + private RootAllomorph CreateRoot(string headword) { var entry = Cache.ServiceLocator.GetInstance().Create( diff --git a/Src/LexText/ParserCore/ParserCoreTests/HCTraceTransformTests.cs b/Src/LexText/ParserCore/ParserCoreTests/HCTraceTransformTests.cs index eb71cbb2f1..e46f3f4e9c 100644 --- a/Src/LexText/ParserCore/ParserCoreTests/HCTraceTransformTests.cs +++ b/Src/LexText/ParserCore/ParserCoreTests/HCTraceTransformTests.cs @@ -90,6 +90,25 @@ public void CompoundingFailure_UnknownCauseIsExplicit(string type) Assert.That(Text(Transform(CompoundFailure(type))), Does.Contain("Reason: Unknown reason.")); } + /// Names the required slot whose affixes the word cannot supply. + [Test] + public void TemplateFailure_NamesUnreachableRequiredSlot() + { + var node = new XElement("MorphologicalRuleAnalysisTrace", + new XElement("MorphologicalRule", new XAttribute("type", "affix"), "suffix"), + new XElement("Output", "a"), + new XElement("TemplateAnalysisTraceOut", + new XElement("AffixTemplate", new XAttribute("id", 0), "verb template"), + new XElement("Output", "*None*"), + new XElement("FailureReason", new XAttribute("type", "unreachableRequiredSlot"), + new XElement("Slot", "subject slot")))); + + var text = Text(Transform(node)); + + Assert.That(text, Does.Contain("failed.")); + Assert.That(text, Does.Contain("(Reason: The required slot 'subject slot' cannot be filled")); + } + private static XElement Allomorph() { return new XElement("Allomorph", new XAttribute("id", 42), diff --git a/Src/Transforms/Presentation/FormatHCTrace.xsl b/Src/Transforms/Presentation/FormatHCTrace.xsl index 6661e42bd2..da4922e0fc 100644 --- a/Src/Transforms/Presentation/FormatHCTrace.xsl +++ b/Src/Transforms/Presentation/FormatHCTrace.xsl @@ -684,6 +684,16 @@ ShowMsaInfo An affix cannot be applied more than once. + + The required slot ' + + + + + + + ' cannot be filled: no affix in this slot can be found in what remains of the word, in the order and position the template requires. + Unknown reason. diff --git a/Src/Transforms/Presentation/HCSynthByGlossFormatHCTrace.xsl b/Src/Transforms/Presentation/HCSynthByGlossFormatHCTrace.xsl index 58e51073a2..b451f4df02 100644 --- a/Src/Transforms/Presentation/HCSynthByGlossFormatHCTrace.xsl +++ b/Src/Transforms/Presentation/HCSynthByGlossFormatHCTrace.xsl @@ -828,6 +828,16 @@ ShowMsaInfo An affix cannot be applied more than once. + + The required slot ' + + + + + + + ' cannot be filled: no affix in this slot can be found in what remains of the word, in the order and position the template requires. + ) diff --git a/Src/Utilities/HCSynthByGloss/HCSynthByGloss/HCSynthByGlossTests/HCSynthByGlossTests.cs b/Src/Utilities/HCSynthByGloss/HCSynthByGloss/HCSynthByGlossTests/HCSynthByGlossTests.cs index 941646206e..736439a4b4 100644 --- a/Src/Utilities/HCSynthByGloss/HCSynthByGloss/HCSynthByGlossTests/HCSynthByGlossTests.cs +++ b/Src/Utilities/HCSynthByGloss/HCSynthByGloss/HCSynthByGlossTests/HCSynthByGlossTests.cs @@ -12,6 +12,7 @@ using SIL.Machine.FeatureModel; using SIL.Machine.Morphology; using SIL.Machine.Morphology.HermitCrab; +using SIL.Machine.Morphology.HermitCrab.MorphologicalRules; using System.Reflection; using System.IO; using HCSynthByGloss; @@ -145,5 +146,25 @@ public void TraceListsEachFailedEnvironment() Assert.That(reason.Elements("Environment").Select(e => e.Value).ToList(), Has.Count.EqualTo(2).And.All.Not.Empty); } + + /// The trace names the required slot that made a template fail. + [Test] + public void TraceNamesUnreachableRequiredSlot() + { + var traceManager = new HcXmlTraceManager { IsTracing = true }; + var table = new CharacterDefinitionTable(); + table.AddSegment("a"); + var trace = new XElement("WordAnalysisTrace"); + var word = new Word(new Stratum(table), table.Segment("a")) { CurrentTrace = trace }; + var template = new AffixTemplate { Name = "synthetic template" }; + var slot = new AffixTemplateSlot(new AffixProcessRule()) { Name = "synthetic slot" }; + + traceManager.EndUnapplyTemplate(template, word, false, + FailureReason.UnreachableRequiredSlot, slot); + + XElement reason = trace.Element("TemplateAnalysisTraceOut").Element("FailureReason"); + Assert.That((string)reason.Attribute("type"), Is.EqualTo("unreachableRequiredSlot")); + Assert.That(reason.Element("Slot").Value, Is.EqualTo("synthetic slot")); + } } } diff --git a/Src/Utilities/HCSynthByGloss/HCSynthByGlossLib/HcXmlTraceManager.cs b/Src/Utilities/HCSynthByGloss/HCSynthByGlossLib/HcXmlTraceManager.cs index 56a9eee6bb..f442038ffe 100644 --- a/Src/Utilities/HCSynthByGloss/HCSynthByGlossLib/HcXmlTraceManager.cs +++ b/Src/Utilities/HCSynthByGloss/HCSynthByGlossLib/HcXmlTraceManager.cs @@ -97,7 +97,13 @@ public void BeginUnapplyTemplate(AffixTemplate template, Word input) ); } - public void EndUnapplyTemplate(AffixTemplate template, Word output, bool unapplied, FailureReason reason) + public void EndUnapplyTemplate( + AffixTemplate template, + Word output, + bool unapplied, + FailureReason reason, + object failureObj + ) { var trace = new XElement( "TemplateAnalysisTraceOut", @@ -110,6 +116,16 @@ public void EndUnapplyTemplate(AffixTemplate template, Word output, bool unappli new XElement("FailureReason", new XAttribute("type", "nonPartialRuleAfterFinalTemplate")) ); } + else if (reason == FailureReason.UnreachableRequiredSlot) + { + trace.Add( + new XElement( + "FailureReason", + new XAttribute("type", "unreachableRequiredSlot"), + new XElement("Slot", (failureObj as AffixTemplateSlot)?.Name) + ) + ); + } ((XElement)output.CurrentTrace).Add(trace); }