Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ public IEnumerable<Word> Apply(Word input)
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.BeginUnapplyTemplate(_template, input);

// Do not allow a final template to unapply if we don't need to worry about partials
// Do not allow a final template to unapply if we don't need to worry about partial rules
// and a non-template was last unapplied.
if (
(!_morpher.IsPartial || _morpher.AlwaysEnforceFinalTemplates)
(!_morpher.HasPartialRules || _morpher.AlwaysEnforceFinalTemplates)
&& input.FinalTemplateState == FinalTemplateState.NonTemplate
&& _template.IsFinal
)
Expand Down
9 changes: 4 additions & 5 deletions src/SIL.Machine.Morphology.HermitCrab/Morpher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public Morpher(ITraceManager traceManager, Language lang, int maxDegreeOfParalle
RuleSelector = rule => true;

_morphemes = new ReadOnlyObservableCollection<Morpheme>(morphemes);
IsPartial = GetPartialMorphemes().Count() > 0;
HasPartialRules = GetPartialMorphemes().Any(morpheme => !(morpheme is LexEntry));
}

public IEnumerable<Morpheme> GetPartialMorphemes()
Expand Down Expand Up @@ -98,9 +98,10 @@ public ITraceManager TraceManager
public bool MergeEquivalentAnalyses { get; set; }

/// <summary>
/// A Morpher is partial if any of the elements are partial.
/// True if any morpheme other than a <see cref="LexEntry"/> is partial. Analysis may prune a final
/// template that follows a non-template rule only when this is false.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: F7. The summary says analysis prunes "only when this is false", but it also prunes when this is true and AlwaysEnforceFinalTemplates is set (AnalysisAffixTemplateRule.cs:47, AnalysisAffixProcessRule.cs:83). Consider "only when this is false or AlwaysEnforceFinalTemplates is set".

/// </summary>
public bool IsPartial { get; }
public bool HasPartialRules { get; }
Comment thread
johnml1135 marked this conversation as resolved.

/// <summary>
/// Enforce final templates even if some of the morphemes were partial.
Expand Down Expand Up @@ -491,8 +492,6 @@ private IEnumerable<Word> LexicalGuess(Word input)
SyntacticFeatureStruct = input.SyntacticFeatureStruct,
Stratum = input.Stratum,
};
// Don't update Morpher.IsPartial even if lexEntry.IsPartial is true.
// We don't mind if final templates are filtered during analysis in this case.
lexEntry.Allomorphs.Add(root);
// Point the root allomorph to the lexical pattern in FieldWorks.
if (lexicalPattern.Morpheme != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ out FeatureStruct syntacticFS

outWord.SyntacticFeatureStruct = syntacticFS;
}
if (!_morpher.IsPartial || _morpher.AlwaysEnforceFinalTemplates)
if (!_morpher.HasPartialRules || _morpher.AlwaysEnforceFinalTemplates)
{
if (!_rule.GetIsCliticRule())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ public void EarlyPruningOfFinalTemplate()

// Verify early pruning of final template.
var morpher = new Morpher(TraceManager, Language);
Assert.That(morpher.IsPartial, Is.True);
Assert.That(morpher.GetPartialMorphemes(), Is.Not.Empty);
morpher.AlwaysEnforceFinalTemplates = true;
Comment thread
claude[bot] marked this conversation as resolved.
TraceManager.IsTracing = true;
AssertMorphsEqual(morpher.ParseWord("sagdv", out object trace));
Expand Down Expand Up @@ -501,6 +501,79 @@ public void EarlyPruningOfFinalTemplate()
AssertMorphsEqual(morpher.ParseWord("sagdmis"), "32 PAST 53 PL");
}

[Test]
public void PartialLexicalEntryDoesNotDisableEarlyPruningOfFinalTemplate()
{
var any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;
var verb = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value;

AffixProcessRule CreateSuffix(string name, string gloss, string suffix)
{
var rule = new AffixProcessRule
{
Name = name,
Gloss = gloss,
RequiredSyntacticFeatureStruct = verb.Clone(),
};
rule.Allomorphs.Add(
new AffixProcessAllomorph
{
Lhs = { Pattern<Word, ShapeNode>.New("1").Annotation(any).OneOrMore.Value },
Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, suffix) },
}
);
return rule;
}

var edSuffix = CreateSuffix("ed_suffix", "PAST", "d");
var verbTemplate = new AffixTemplate { Name = "verb", RequiredSyntacticFeatureStruct = verb.Clone() };
verbTemplate.Slots.Add(new AffixTemplateSlot(edSuffix));
Morphophonemic.AffixTemplates.Add(verbTemplate);
var nominalizer = CreateSuffix("nominalizer", "NOM", "v");
Morphophonemic.MorphologicalRules.Add(nominalizer);
var partialRoot = AddEntry("partial_root", verb.Clone(), Morphophonemic, "sab");
partialRoot.IsPartial = true;
try
{
// The test grammar already has a partial entry, so only the rules decide the prune.
var morpher = new Morpher(TraceManager, Language);
Assert.That(morpher.GetPartialMorphemes(), Is.Not.Empty);
Assert.That(morpher.HasPartialRules, Is.False);
Assert.That(morpher.AlwaysEnforceFinalTemplates, Is.False);
TraceManager.IsTracing = true;
AssertMorphsEqual(morpher.ParseWord("sagdv", out object trace));
Assert.That(
GetFailureDepth((Trace)trace, FailureReason.NonPartialRuleProhibitedAfterFinalTemplate),
Is.EqualTo(2)
);
TraceManager.IsTracing = false;
AssertMorphsEqual(morpher.ParseWord("sagd"), "32 PAST");

// No template applies to a partial root, so pruning loses nothing for it.
AssertMorphsEqual(morpher.ParseWord("sab"), "partial_root");
AssertMorphsEqual(morpher.ParseWord("sabd"));
AssertMorphsEqual(morpher.ParseWord("sabdv"));
AssertMorphsEqual(morpher.ParseWord("sabv"), "partial_root NOM");

// A partial rule still turns the prune off, and what it licenses after a final template survives.
nominalizer.IsPartial = true;
morpher = new Morpher(TraceManager, Language);
Assert.That(morpher.HasPartialRules, Is.True);
TraceManager.IsTracing = true;
AssertMorphsEqual(morpher.ParseWord("sagdv", out trace), "32 PAST NOM");
Assert.That(
GetFailureDepth((Trace)trace, FailureReason.NonPartialRuleProhibitedAfterFinalTemplate),
Is.EqualTo(0)
);
TraceManager.IsTracing = false;
}
finally
{
Morphophonemic.Entries.Remove(partialRoot);
Entries.Remove("partial_root");
}
}

private static int GetFailureDepth(Trace trace, FailureReason reason)
{
if (trace == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,11 @@ public void PartialMorphemesParseTheSameWithAndWithoutPruning(bool alwaysEnforce
string[] Parse(bool pruneCopies, string word)
{
Morpher morpher = CreateMorpher(pruneCopies);
Assert.That(morpher.IsPartial, Is.True, "entry 54 has an empty syntactic feature structure");
Assert.That(
morpher.GetPartialMorphemes(),
Is.Not.Empty,
"entry 54 has an empty syntactic feature structure"
);
morpher.AlwaysEnforceFinalTemplates = alwaysEnforceFinalTemplates;
Comment thread
claude[bot] marked this conversation as resolved.
return ParseGlosses(morpher, word);
}
Expand Down
Loading