Skip to content
Draft
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 numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,14 @@ public IEnumerable<Word> Apply(Word input)
_morpher.TraceManager.BeginUnapplyTemplate(_template, input);

Word inWord = input.Clone();
if (
(!_morpher.IsPartial || _morpher.AlwaysEnforceFinalTemplates)
&& inWord.FinalTemplateState == FinalTemplateState.NonTemplate
&& _template.IsFinal
)
{
inWord.FinalTemplateState = FinalTemplateState.FinalTemplateAfterNonTemplate;
}
inWord.Freeze();

var output = new HashSet<Word>(FreezableEqualityComparer<Word>.Default);
Expand Down
11 changes: 9 additions & 2 deletions src/SIL.Machine.Morphology.HermitCrab/AnalysisStateKey.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,9 +12,11 @@ namespace SIL.Machine.Morphology.HermitCrab
/// re-run whenever an <c>Analysis*.cs</c> rule changes:
/// <list type="bullet">
/// <item><see cref="MorphologicalRules.AnalysisAffixProcessRule"/>: Shape (FST pattern match),
/// <see cref="Word.SyntacticFeatureStruct"/> (unifiability gate), per-rule unapplication count.</item>
/// <see cref="Word.SyntacticFeatureStruct"/> (unifiability gate), per-rule unapplication count
/// and FinalTemplateState.</item>
/// <item><see cref="MorphologicalRules.AnalysisCompoundingRule"/>: adds <see cref="Word.NonHeadCount"/>
/// (<c>MaxStemCount</c> gate) -- never the non-heads' own content, only the count.</item>
/// (<c>MaxStemCount</c> gate) -- never the non-heads' own content, only the count
/// and FinalTemplateState.</item>
/// <item><see cref="MorphologicalRules.AnalysisRealizationalAffixProcessRule"/>: adds
/// <see cref="Word.RealizationalFeatureStruct"/>.</item>
/// </list>
Expand All@@ -31,6 +33,7 @@ namespace SIL.Machine.Morphology.HermitCrab
private readonly FeatureStruct _realizationalFS;
private readonly int _nonHeadCount;
private readonly IReadOnlyDictionary<IMorphologicalRule, int> _ruleCounts;
private readonly FinalTemplateState _finalTemplateState;
private readonly int _hashCode;

/// <summary>
Expand DownExpand Up@@ -62,6 +65,7 @@ private AnalysisStateKey(Word word)
_realizationalFS = word.RealizationalFeatureStruct;
_nonHeadCount = word.NonHeadCount;
_ruleCounts = word.UnappliedRuleCounts;
_finalTemplateState = word.FinalTemplateState;

// See PinAndKey for why the key pins these rather than just reading them.
_shape.Freeze();
Expand All@@ -74,6 +78,7 @@ private AnalysisStateKey(Word word)
hash = hash * 31 + _syntacticFS.GetFrozenHashCode();
hash = hash * 31 + _realizationalFS.GetFrozenHashCode();
hash = hash * 31 + _nonHeadCount;
hash = hash * 31 + _finalTemplateState.GetHashCode();
if (_ruleCounts != null)
{
// XOR rather than the usual *31 rolling combine: the multiset is unordered, so entries
Expand All@@ -100,6 +105,8 @@ public bool Equals(AnalysisStateKey other)
return false;
if (!_syntacticFS.ValueEquals(other._syntacticFS) || !_realizationalFS.ValueEquals(other._realizationalFS))
return false;
if (_finalTemplateState != other._finalTemplateState)
return false;
return RuleCountsEqual(_ruleCounts, other._ruleCounts);
}

Expand Down
12 changes: 10 additions & 2 deletions src/SIL.Machine.Morphology.HermitCrab/AnalysisStratumRule.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -161,9 +161,17 @@ internal IEnumerable<Word> Apply(Word input, ref int alternativeCount)
}
shapeWord[shape] = mruleOutWord;
}
output.Add(mruleOutWord);
Word newMruleOutWord = mruleOutWord;
if (mruleOutWord.FinalTemplateState != FinalTemplateState.None)
{
// Clear FinalTemplateState to allow clitics.
newMruleOutWord = mruleOutWord.Clone();
newMruleOutWord.FinalTemplateState = FinalTemplateState.None;
newMruleOutWord.Freeze();
}
output.Add(newMruleOutWord);
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.EndUnapplyStratum(_stratum, mruleOutWord);
_morpher.TraceManager.EndUnapplyStratum(_stratum, newMruleOutWord);
}
return output;
}
Expand Down
9 changes: 9 additions & 0 deletions src/SIL.Machine.Morphology.HermitCrab/FinalTemplateState.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
namespace SIL.Machine.Morphology.HermitCrab
{
enum FinalTemplateState : byte
{
None,
NonTemplate,
FinalTemplateAfterNonTemplate,
}
}
7 changes: 7 additions & 0 deletions src/SIL.Machine.Morphology.HermitCrab/ITraceManager.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,6 +47,13 @@ public interface ITraceManager

void MorphologicalRuleUnapplied(IMorphologicalRule rule, int subruleIndex, Word input, Word output);
void MorphologicalRuleNotUnapplied(IMorphologicalRule rule, int subruleIndex, Word input);
void MorphologicalRuleNotUnapplied(
IMorphologicalRule rule,
int subruleIndex,
Word input,
FailureReason reason,
object failureObj
);

void CompoundingRuleNotUnapplied(
IMorphologicalRule rule,
Expand Down
22 changes: 22 additions & 0 deletions src/SIL.Machine.Morphology.HermitCrab/Morpher.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,6 +65,18 @@ public Morpher(ITraceManager traceManager, Language lang, int maxDegreeOfParalle
RuleSelector = rule => true;

_morphemes = new ReadOnlyObservableCollection<Morpheme>(morphemes);
IsPartial = GetPartialMorphemes().Count() > 0;
}

public IEnumerable<Morpheme> GetPartialMorphemes()
{
var morphemes = new HashSet<Morpheme>();
foreach (Morpheme morpheme in _morphemes)
{
if (morpheme.IsPartial)
morphemes.Add(morpheme);
}
return morphemes;
}

public ITraceManager TraceManager
Expand All@@ -88,6 +100,16 @@ public ITraceManager TraceManager
/// </summary>
public bool MergeEquivalentAnalyses { get; set; }

/// <summary>
/// A Morpher is partial if any of the elements are partial.
/// </summary>
public bool IsPartial { get; set; }

/// <summary>
/// Enforce final templates even if some of the morphemes were partial.
/// </summary>
public bool AlwaysEnforceFinalTemplates { get; set; }

/// <summary>
/// Caps the concurrency used within a single parse or generation -- analysis cascade,
/// affix-template unapplication and synthesis alike. A value of 1 runs the work fully
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,6 +49,26 @@ public IEnumerable<Word> Apply(Word input)
return Enumerable.Empty<Word>();
}

// Do not allow a final template to unapply if the grammar is not partial
// and a non-template was last unapplied.
if (
(!_morpher.IsPartial || _morpher.AlwaysEnforceFinalTemplates)
&& input.FinalTemplateState == FinalTemplateState.FinalTemplateAfterNonTemplate
)
{
if (_morpher.TraceManager.IsTracing)
{
_morpher.TraceManager.MorphologicalRuleNotUnapplied(
_rule,
-1,
input,
FailureReason.NonPartialRuleProhibitedAfterFinalTemplate,
null
);
}
return Enumerable.Empty<Word>();
}

var output = new List<Word>();
for (int i = 0; i < _rules.Count; i++)
{
Expand All@@ -59,6 +79,9 @@ public IEnumerable<Word> Apply(Word input)
outWord.SyntacticFeatureStruct.Add(_rule.RequiredSyntacticFeatureStruct);
else if (_rule.OutSyntacticFeatureStruct.IsEmpty)
outWord.SyntacticFeatureStruct.Clear();
outWord.FinalTemplateState = !_rule.IsTemplateRule
? FinalTemplateState.NonTemplate
: FinalTemplateState.None;
outWord.MorphologicalRuleUnapplied(_rule);
outWord.Freeze();
if (_morpher.TraceManager.IsTracing)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -130,6 +130,7 @@ RootAllomorph allo in _morpher.SearchRootAllomorphs(_rule.Stratum, outWord.Curre
outWord.SyntacticFeatureStruct.Add(_rule.HeadRequiredSyntacticFeatureStruct);
else if (_rule.OutSyntacticFeatureStruct.IsEmpty)
outWord.SyntacticFeatureStruct.Clear();
outWord.FinalTemplateState = FinalTemplateState.NonTemplate;
outWord.MorphologicalRuleUnapplied(_rule);

outWord.Freeze();
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,6 +46,26 @@ public IEnumerable<Word> Apply(Word input)
if (!_rule.RealizationalFeatureStruct.Unify(input.RealizationalFeatureStruct, out realFS))
return Enumerable.Empty<Word>();

// Do not allow a final template to unapply if the grammar is not partial
// and a non-template was last unapplied.
if (
(!_morpher.IsPartial || _morpher.AlwaysEnforceFinalTemplates)
&& input.FinalTemplateState == FinalTemplateState.FinalTemplateAfterNonTemplate
)
{
if (_morpher.TraceManager.IsTracing)
{
_morpher.TraceManager.MorphologicalRuleNotUnapplied(
_rule,
-1,
input,
FailureReason.NonPartialRuleProhibitedAfterFinalTemplate,
null
);
}
return Enumerable.Empty<Word>();
}

var output = new List<Word>();
for (int i = 0; i < _rules.Count; i++)
{
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,8 +63,7 @@ public IEnumerable<Word> Apply(Word input)
if (
!_rule.IsTemplateRule
&& (input.IsLastAppliedRuleFinal ?? false)
&& !input.IsPartial
&& !_rule.IsPartial
&& ((!input.IsPartial && !_rule.IsPartial) || _morpher.AlwaysEnforceFinalTemplates)
)
{
if (_morpher.TraceManager.IsTracing)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ public IEnumerable<Word> Apply(Word input)
return Enumerable.Empty<Word>();
}

if ((input.IsLastAppliedRuleFinal ?? false) && !input.IsPartial)
if ((input.IsLastAppliedRuleFinal ?? false) && (!input.IsPartial || _morpher.AlwaysEnforceFinalTemplates))
{
if (_morpher.TraceManager.IsTracing)
{
Expand Down
18 changes: 18 additions & 0 deletions src/SIL.Machine.Morphology.HermitCrab/TraceManager.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -84,6 +84,24 @@ public void MorphologicalRuleNotUnapplied(IMorphologicalRule rule, int subruleIn
);
}

public void MorphologicalRuleNotUnapplied(
IMorphologicalRule rule,
int subruleIndex,
Word input,
FailureReason reason,
object failureObj
)
{
((Trace)input.CurrentTrace).Children.Add(
new Trace(TraceType.MorphologicalRuleAnalysis, rule)
{
SubruleIndex = subruleIndex,
Input = input,
FailureReason = reason,
}
);
}

public void CompoundingRuleNotUnapplied(
IMorphologicalRule rule,
int subruleIndex,
Expand Down
18 changes: 17 additions & 1 deletion src/SIL.Machine.Morphology.HermitCrab/Word.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@ public class Word : Freezable<Word>, IAnnotatedData<ShapeNode>, ICloneable<Word>
private FeatureStruct _realizationalFS;
private Stratum _stratum;
private bool? _isLastAppliedRuleFinal;
private FinalTemplateState _finalTemplateState;
private bool _isPartial;
private readonly Dictionary<string, HashSet<int>> _disjunctiveAllomorphIndices;
private int _mruleAppCount = 0;
Expand All@@ -47,6 +48,7 @@ public Word(RootAllomorph rootAllomorph, FeatureStruct realizationalFS)
_nonHeadApps = new List<Word>();
_obligatorySyntacticFeatures = new IDBearerSet<Feature>();
_isLastAppliedRuleFinal = null;
_finalTemplateState = FinalTemplateState.None;
_disjunctiveAllomorphIndices = new Dictionary<string, HashSet<int>>();
}

Expand All@@ -65,6 +67,7 @@ public Word(Stratum stratum, Shape shape)
_nonHeadApps = new List<Word>();
_obligatorySyntacticFeatures = new IDBearerSet<Feature>();
_isLastAppliedRuleFinal = null;
_finalTemplateState = FinalTemplateState.None;
_isPartial = false;
_disjunctiveAllomorphIndices = new Dictionary<string, HashSet<int>>();
}
Expand DownExpand Up@@ -93,6 +96,7 @@ private Word(Word word, bool cloneNonHeadApps)
_nonHeadAppIndex = word._nonHeadAppIndex;
_obligatorySyntacticFeatures = new IDBearerSet<Feature>(word._obligatorySyntacticFeatures);
_isLastAppliedRuleFinal = word._isLastAppliedRuleFinal;
_finalTemplateState = word._finalTemplateState;
_isPartial = word._isPartial;
CurrentTrace = word.CurrentTrace;
AnalysisScope = word.AnalysisScope;
Expand DownExpand Up@@ -392,6 +396,16 @@ internal bool? IsLastAppliedRuleFinal
}
}

internal FinalTemplateState FinalTemplateState
{
get { return _finalTemplateState; }
set
{
CheckFrozen();
_finalTemplateState = value;
}
}

/// <summary>
/// Gets the number of times the specified morphological rule has been applied.
/// </summary>
Expand DownExpand Up@@ -621,6 +635,7 @@ protected override int FreezeImpl()
code = code * 31 + _mruleApps.GetSequenceHashCode();
code = code * 31 + _mruleAppIndex.GetHashCode();
code = code * 31 + _isLastAppliedRuleFinal.GetHashCode();
code = code * 31 + _finalTemplateState.GetHashCode();
return code;
}

Expand All@@ -640,7 +655,8 @@ public override bool ValueEquals(Word other)
&& _rootAllomorph == other._rootAllomorph
&& _mruleApps.SequenceEqual(other._mruleApps)
&& _mruleAppIndex == other._mruleAppIndex
&& _isLastAppliedRuleFinal == other._isLastAppliedRuleFinal;
&& _isLastAppliedRuleFinal == other._isLastAppliedRuleFinal
&& _finalTemplateState == other._finalTemplateState;
}

public Word Clone()
Expand Down
Loading
Loading