From fa75c1e78d1fe0c90d7b5a2b67d816de9d8c6ceb Mon Sep 17 00:00:00 2001 From: Andy Black Date: Thu, 26 Jun 2025 13:29:22 -0700 Subject: [PATCH 1/2] LT-18767 yellow box crash in HC for bad XAmple-style reduplication Change-Id: I7b4718674ee25850ac760288665c96fdc6bcc71d --- Src/LexText/ParserCore/HCLoader.cs | 43 +++++++++++++++---- Src/LexText/ParserCore/HCParser.cs | 10 +++++ Src/LexText/ParserCore/IHCLoadErrorLogger.cs | 1 + .../ParserCoreTests/HCLoaderTests.cs | 31 ++++++++++++- Src/Transforms/Presentation/FormatCommon.xsl | 15 +++++++ 5 files changed, 91 insertions(+), 9 deletions(-) diff --git a/Src/LexText/ParserCore/HCLoader.cs b/Src/LexText/ParserCore/HCLoader.cs index 9b40107b59..bbf1cc96dc 100644 --- a/Src/LexText/ParserCore/HCLoader.cs +++ b/Src/LexText/ParserCore/HCLoader.cs @@ -1471,16 +1471,17 @@ private AffixProcessAllomorph LoadFormAffixProcessAllomorph(IMoForm allo, IPhEnv case MoMorphTypeTags.kMorphSuffixingInterfix: case MoMorphTypeTags.kMorphEnclitic: hcAllo.Lhs.Add(stemPattern); - hcAllo.Lhs.AddRange(LoadReduplicationPatterns(contexts.Item1)); + var lhsPatterns = LoadReduplicationPatterns(contexts.Item1); + hcAllo.Lhs.AddRange(lhsPatterns); var suffixNull = new Pattern("suffixNull", SuffixNull()); suffixNull.Freeze(); hcAllo.Lhs.Add(suffixNull); hcAllo.Rhs.Add(new CopyFromInput("stem")); - hcAllo.Rhs.AddRange(LoadReduplicationOutputActions(contexts.Item1)); + hcAllo.Rhs.AddRange(LoadReduplicationOutputActions(contexts.Item1, lhsPatterns, allo)); hcAllo.Rhs.Add(new CopyFromInput("suffixNull")); hcAllo.Rhs.Add(new InsertSegments(Segments("+"))); - hcAllo.Rhs.AddRange(LoadReduplicationOutputActions(form)); + hcAllo.Rhs.AddRange(LoadReduplicationOutputActions(form, lhsPatterns, allo)); break; case MoMorphTypeTags.kMorphPrefix: @@ -1489,13 +1490,14 @@ private AffixProcessAllomorph LoadFormAffixProcessAllomorph(IMoForm allo, IPhEnv var prefixNull = new Pattern("prefixNull", PrefixNull()); prefixNull.Freeze(); hcAllo.Lhs.Add(prefixNull); - hcAllo.Lhs.AddRange(LoadReduplicationPatterns(contexts.Item2)); + lhsPatterns = LoadReduplicationPatterns(contexts.Item2); + hcAllo.Lhs.AddRange(lhsPatterns); hcAllo.Lhs.Add(stemPattern); - hcAllo.Rhs.AddRange(LoadReduplicationOutputActions(form)); + hcAllo.Rhs.AddRange(LoadReduplicationOutputActions(form, lhsPatterns, allo)); hcAllo.Rhs.Add(new InsertSegments(Segments("+"))); hcAllo.Rhs.Add(new CopyFromInput("prefixNull")); - hcAllo.Rhs.AddRange(LoadReduplicationOutputActions(contexts.Item2)); + hcAllo.Rhs.AddRange(LoadReduplicationOutputActions(contexts.Item2, lhsPatterns, allo)); hcAllo.Rhs.Add(new CopyFromInput("stem")); break; } @@ -1611,13 +1613,38 @@ private IEnumerable> LoadReduplicationPatterns(string p } } - private IEnumerable LoadReduplicationOutputActions(string patternStr) + private IEnumerable LoadReduplicationOutputActions(string patternStr, IEnumerable> patterns, IMoForm form) { foreach (string token in TokenizeContext(patternStr)) { if (token.StartsWith("[")) { - yield return new CopyFromInput(XmlConvert.EncodeName(token.Substring(1, token.Length - 2).Trim())); + bool isValid = true; + if (token.Contains("^")) + { + // The ^ gets replaced by _x005E_ so we need to do it here to match in patterns + string indexedToken = token.Substring(1, token.Length - 2).Trim().Replace("^", "_x005E_"); + isValid = patterns.Any(p => p.Name == indexedToken); + if (!isValid) + { + // add error message to logger + string envs = ""; + var environments = form.AllomorphEnvironments; + if (environments != null) + { + StringBuilder sb = new StringBuilder(); + foreach (IPhEnvironment env in environments) + { + sb.Append(env.ShortName); + sb.Append(" "); + } + envs = sb.ToString(); + } + m_logger.UnmatchedReduplicationIndexedClass(form, "Ill-formed index:" + token, envs); + } + } + if (isValid) + yield return new CopyFromInput(XmlConvert.EncodeName(token.Substring(1, token.Length - 2).Trim())); } else { diff --git a/Src/LexText/ParserCore/HCParser.cs b/Src/LexText/ParserCore/HCParser.cs index ea092e29e1..ef38885d27 100644 --- a/Src/LexText/ParserCore/HCParser.cs +++ b/Src/LexText/ParserCore/HCParser.cs @@ -669,6 +669,16 @@ public void OutOfScopeSlot(IMoInflAffixSlot slot, IMoInflAffixTemplate template, m_xmlWriter.WriteElementString("Reason", reason); m_xmlWriter.WriteEndElement(); } + public void UnmatchedReduplicationIndexedClass(IMoForm form, string reason, string pattern) + { + m_xmlWriter.WriteStartElement("LoadError"); + m_xmlWriter.WriteAttributeString("type", "unmatched-redup-indexed-class"); + m_xmlWriter.WriteElementString("Form", form.Form.VernacularDefaultWritingSystem.Text); + m_xmlWriter.WriteElementString("Pattern", pattern); + m_xmlWriter.WriteElementString("Reason", reason); + m_xmlWriter.WriteElementString("Hvo", form.Hvo.ToString(CultureInfo.InvariantCulture)); + m_xmlWriter.WriteEndElement(); + } } } } diff --git a/Src/LexText/ParserCore/IHCLoadErrorLogger.cs b/Src/LexText/ParserCore/IHCLoadErrorLogger.cs index 529deff8ef..362a1a49c2 100644 --- a/Src/LexText/ParserCore/IHCLoadErrorLogger.cs +++ b/Src/LexText/ParserCore/IHCLoadErrorLogger.cs @@ -13,5 +13,6 @@ public interface IHCLoadErrorLogger void InvalidRewriteRule(IPhRegularRule prule, string reason); void InvalidStrata(string strata, string reason); void OutOfScopeSlot(IMoInflAffixSlot slot, IMoInflAffixTemplate template, string reason); + void UnmatchedReduplicationIndexedClass(IMoForm form, string reason, string environment); } } diff --git a/Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs b/Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs index ef49d58733..721bbe765f 100644 --- a/Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs +++ b/Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs @@ -43,7 +43,8 @@ private enum LoadErrorType InvalidEnvironment, InvalidRedupForm, InvalidRewriteRule, - InvalidStrata + InvalidStrata, + UnmatchedReduplicationIndexedClass } private class TestHCLoadErrorLogger : IHCLoadErrorLogger @@ -99,6 +100,11 @@ public void OutOfScopeSlot(IMoInflAffixSlot slot, IMoInflAffixTemplate template, { throw new NotImplementedException(); } + public void UnmatchedReduplicationIndexedClass(IMoForm form, string reason, string patterns) + { + m_loadErrors.Add(Tuple.Create(LoadErrorType.UnmatchedReduplicationIndexedClass, (ICmObject)form)); + } + } private readonly List> m_loadErrors = new List>(); @@ -596,6 +602,29 @@ public void InvalidPartialReduplicationEnvironment() LoadLanguage(); Assert.That(m_lang.Strata[0].MorphologicalRules.Count, Is.EqualTo(0)); + + // now check for missing indexed class which is removed but with an logged error message + // case 1: the form has the indexed class, but the environment does not + var env = allo.PhoneEnvRC.ElementAt(0); + allo.PhoneEnvRC.Remove(env); + allo.PhoneEnvRC.Add(AddEnvironment("/_[C^1]")); + LoadLanguage(); + + Assert.That(m_lang.Strata[0].MorphologicalRules.Count, Is.EqualTo(1)); + Assert.That(m_loadErrors.Count == 1); + var err = m_loadErrors.ElementAt(0); + Assert.That(err.Item1 == LoadErrorType.UnmatchedReduplicationIndexedClass); + + // case 2: the environment has the indexed class, but the form does not + env = allo.PhoneEnvRC.ElementAt(0); + allo.PhoneEnvRC.Remove(env); + allo.PhoneEnvRC.Add(AddEnvironment("/_[C^2][V^1]")); + LoadLanguage(); + + Assert.That(m_lang.Strata[0].MorphologicalRules.Count, Is.EqualTo(1)); + Assert.That(m_loadErrors.Count == 1); + err = m_loadErrors.ElementAt(0); + Assert.That(err.Item1 == LoadErrorType.UnmatchedReduplicationIndexedClass); } [Test] diff --git a/Src/Transforms/Presentation/FormatCommon.xsl b/Src/Transforms/Presentation/FormatCommon.xsl index 9326558a78..acae7d6a40 100644 --- a/Src/Transforms/Presentation/FormatCommon.xsl +++ b/Src/Transforms/Presentation/FormatCommon.xsl @@ -127,6 +127,21 @@ + +
  • + The reduplication form " + + " is invalid. Reason: + + + + + + + (Click here to see the entry.) + +
  • +
  • From aa989b550d19c41e3090c1f9ed6db31956711ad2 Mon Sep 17 00:00:00 2001 From: Andy Black Date: Thu, 26 Jun 2025 14:31:38 -0700 Subject: [PATCH 2/2] Fix missing method in console logger Change-Id: I16a6aa014b4f146b70146a9bd70b1ddaf6da922d --- Src/GenerateHCConfig/ConsoleLogger.cs | 5 +++++ Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs | 1 + 2 files changed, 6 insertions(+) diff --git a/Src/GenerateHCConfig/ConsoleLogger.cs b/Src/GenerateHCConfig/ConsoleLogger.cs index 46685db5f7..035b391474 100644 --- a/Src/GenerateHCConfig/ConsoleLogger.cs +++ b/Src/GenerateHCConfig/ConsoleLogger.cs @@ -118,5 +118,10 @@ public void OutOfScopeSlot(IMoInflAffixSlot slot, IMoInflAffixTemplate template, { Console.WriteLine(reason); } + + void IHCLoadErrorLogger.UnmatchedReduplicationIndexedClass(IMoForm form, string reason, string environment) + { + throw new NotImplementedException(); + } } } diff --git a/Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs b/Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs index 721bbe765f..4dfe09b700 100644 --- a/Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs +++ b/Src/LexText/ParserCore/ParserCoreTests/HCLoaderTests.cs @@ -604,6 +604,7 @@ public void InvalidPartialReduplicationEnvironment() Assert.That(m_lang.Strata[0].MorphologicalRules.Count, Is.EqualTo(0)); // now check for missing indexed class which is removed but with an logged error message + // LT-18767 // case 1: the form has the indexed class, but the environment does not var env = allo.PhoneEnvRC.ElementAt(0); allo.PhoneEnvRC.Remove(env);