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
19 changes: 19 additions & 0 deletions src/SIL.Machine.Morphology.HermitCrab.Tool/ParseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ private void PrintTrace(Trace trace, int indent, HashSet<int> lineIndices)
analysis ? trace.Output.Shape.ToRegexString(table, true) : trace.Output.Shape.ToString(table, true)
);
}
if (trace.SlotIndex != null)
{
if (!first)
_context.Out.Write(", ");
first = false;
_context.Out.Write("Slot: {0}, Outcome: {1}", trace.SlotIndex, trace.SlotOutcome);
}
if (trace.FailureReason != FailureReason.None)
{
if (!first)
Expand Down Expand Up @@ -140,7 +147,9 @@ private static bool IsAnalysis(TraceType type)
case TraceType.StratumAnalysisOutput:
case TraceType.TemplateAnalysisInput:
case TraceType.TemplateAnalysisOutput:
case TraceType.TemplateSlotAnalysis:
Comment thread
claude[bot] marked this conversation as resolved.
case TraceType.MorphologicalRuleAnalysis:
case TraceType.CompoundingRuleAnalysis:
case TraceType.PhonologicalRuleAnalysis:
return true;

Expand All @@ -152,7 +161,9 @@ private static bool IsAnalysis(TraceType type)
case TraceType.StratumSynthesisOutput:
case TraceType.TemplateSynthesisInput:
case TraceType.TemplateSynthesisOutput:
case TraceType.TemplateSlotSynthesis:
case TraceType.MorphologicalRuleSynthesis:
case TraceType.CompoundingRuleSynthesis:
case TraceType.PhonologicalRuleSynthesis:
return false;
}
Expand Down Expand Up @@ -203,6 +214,14 @@ private static string GetTraceTypeString(TraceType type)
return "Template Synthesis In";
case TraceType.TemplateSynthesisOutput:
return "Template Synthesis Out";
case TraceType.TemplateSlotAnalysis:
return "Template Slot Analysis";
case TraceType.TemplateSlotSynthesis:
return "Template Slot Synthesis";
case TraceType.CompoundingRuleAnalysis:
return "Compounding Rule Analysis";
case TraceType.CompoundingRuleSynthesis:
return "Compounding Rule Synthesis";
case TraceType.MorphologicalRuleAnalysis:
return "Morphological Rule Analysis";
case TraceType.MorphologicalRuleSynthesis:
Expand Down
25 changes: 25 additions & 0 deletions src/SIL.Machine.Morphology.HermitCrab/AnalysisAffixTemplateRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,18 @@ private void ApplySlots(Word inWord, int index, HashSet<Word> output)
{
for (int i = index; i >= 0; i--)
{
bool producedOutput = false;
foreach (Word outWord in _rules[i].Apply(inWord))
{
producedOutput = true;
TraceSlot(inWord, outWord, i, TemplateSlotOutcome.RuleApplied);
ApplySlots(outWord, i - 1, output);
}

if (_template.Slots[i].Optional)
TraceSlot(inWord, null, i, TemplateSlotOutcome.OptionalSkipped);
else if (!producedOutput)
TraceSlot(inWord, null, i, TemplateSlotOutcome.RequiredUnfilled);

if (!_template.Slots[i].Optional)
{
Expand Down Expand Up @@ -116,7 +126,16 @@ private void ParallelApplySlots(Word inWord, HashSet<Word> output)
.Select(res => Tuple.Create(res, i - 1))
.ToArray();
if (workItems.Length > 0)
{
foreach (Tuple<Word, int> item in workItems)
TraceSlot(work.Item1, item.Item1, i, TemplateSlotOutcome.RuleApplied);
to.PushRange(workItems);
}

if (_template.Slots[i].Optional)
TraceSlot(work.Item1, null, i, TemplateSlotOutcome.OptionalSkipped);
else if (workItems.Length == 0)
TraceSlot(work.Item1, null, i, TemplateSlotOutcome.RequiredUnfilled);

if (!_template.Slots[i].Optional)
{
Expand Down Expand Up @@ -152,5 +171,11 @@ private void ParallelApplySlots(Word inWord, HashSet<Word> output)

output.UnionWith(outStack);
}

private void TraceSlot(Word input, Word output, int index, TemplateSlotOutcome outcome)
{
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.TemplateSlotProcessed(_template, index, input, output, true, outcome);
}
}
}
67 changes: 67 additions & 0 deletions src/SIL.Machine.Morphology.HermitCrab/IDetailedTraceManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
namespace SIL.Machine.Morphology.HermitCrab
{
public enum TemplateSlotOutcome
{
RuleApplied,
OptionalSkipped,
RequiredUnfilled,
}

public enum PartialParseCause
{
RealizationalFeatureMismatch,
UnappliedMorphologicalRules,
NonFinalTemplateAppliedLast,
ApplicableTemplatesNotApplied,
}

public class PartialParseFailure
{
public PartialParseFailure(PartialParseCause cause, IMorphologicalRule rule = null)
{
Cause = cause;
Rule = rule;
}

public PartialParseCause Cause { get; }

/// <summary>
/// The next rule that was not applied, or null when it is unknown. It is also null when the next
/// step is a compounding non-head, whose rule is not recorded.
/// </summary>
public IMorphologicalRule Rule { get; }
}

/// <summary>
/// Optional tracing capability. ITraceManager implementations receive the existing callbacks without opting in.
/// </summary>
public interface IDetailedTraceManager : ITraceManager
{
void MorphologicalRuleNotUnapplied(
IMorphologicalRule rule,
int subruleIndex,
Word input,
FailureReason reason,
object failureObj
);

/// <summary>
/// Reports fully enumerated lookup results as yielded Word candidates, before surface validation.
/// Interrupted lookups do not receive this callback. Each input has at most one active lookup.
/// </summary>
void LexicalLookupCompleted(Stratum stratum, Word input, int candidateCount, bool guessed);

/// <summary>
/// Reports a slot branch. OptionalSkipped can accompany RuleApplied for the same input.
/// RequiredUnfilled means the slot produced no output; no linguistic cause is implied.
/// </summary>
void TemplateSlotProcessed(
AffixTemplate template,
int slotIndex,
Word input,
Word output,
bool analysis,
TemplateSlotOutcome outcome
);
}
}
4 changes: 4 additions & 0 deletions src/SIL.Machine.Morphology.HermitCrab/ITraceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public enum FailureReason
NonPartialRuleProhibitedAfterFinalTemplate,
NonPartialRuleRequiredAfterNonFinalTemplate,
MaxApplicationCount,
OutputSyntacticFeatureStruct,
Comment thread
claude[bot] marked this conversation as resolved.
RealizationalFeatureStruct,
MaxStemCount,
NonHeadLexicalLookup,
}

public interface ITraceManager
Expand Down
32 changes: 27 additions & 5 deletions src/SIL.Machine.Morphology.HermitCrab/Morpher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ internal IEnumerable<RootAllomorph> SearchRootAllomorphs(Stratum stratum, Shape

private IEnumerable<Word> LexicalLookup(Word input)
{
int candidateCount = 0;
if (_traceManager.IsTracing)
_traceManager.LexicalLookup(input.Stratum, input);
foreach (
Expand All @@ -464,16 +465,20 @@ LexEntry entry in SearchRootAllomorphs(input.Stratum, input.Shape)
if (_traceManager.IsTracing)
_traceManager.SynthesizeWord(_lang, newWord);
newWord.Freeze();
candidateCount++;
yield return newWord;
}
}
if (_traceManager.IsTracing)
_traceManager.LexicalLookupCompleted(input.Stratum, input, candidateCount, false);
}

/// <summary>
/// Match the input against lexical patterns and return matches.
/// </summary>
private IEnumerable<Word> LexicalGuess(Word input)
{
int candidateCount = 0;
if (_traceManager.IsTracing)
_traceManager.LexicalLookup(input.Stratum, input);
CharacterDefinitionTable table = input.Stratum.CharacterDefinitionTable;
Expand Down Expand Up @@ -540,10 +545,13 @@ private IEnumerable<Word> LexicalGuess(Word input)
if (_traceManager.IsTracing)
_traceManager.SynthesizeWord(_lang, newWord);
newWord.Freeze();
candidateCount++;
yield return newWord;
}
}
}
if (_traceManager.IsTracing)
_traceManager.LexicalLookupCompleted(input.Stratum, input, candidateCount, true);
}

/// <summary>
Expand Down Expand Up @@ -657,13 +665,27 @@ private IEnumerable<string> GetShapeStrings(IList<ShapeNode> nodes, CharacterDef

private bool IsWordValid(Word word)
{
if (
!word.RealizationalFeatureStruct.IsUnifiable(word.SyntacticFeatureStruct)
|| !word.IsAllMorphologicalRulesApplied
)
if (!word.RealizationalFeatureStruct.IsUnifiable(word.SyntacticFeatureStruct))
{
if (_traceManager.IsTracing)
_traceManager.PartialParseFailed(
_lang,
word,
new PartialParseFailure(PartialParseCause.RealizationalFeatureMismatch)
);
return false;
}
if (!word.IsAllMorphologicalRulesApplied)
{
if (_traceManager.IsTracing)
_traceManager.Failed(_lang, word, FailureReason.PartialParse, null, null);
_traceManager.PartialParseFailed(
_lang,
word,
new PartialParseFailure(
Comment thread
claude[bot] marked this conversation as resolved.
PartialParseCause.UnappliedMorphologicalRules,
word.NextMorphologicalRule
)
);
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,39 @@ public IEnumerable<Word> Apply(Word input)
if (!_morpher.RuleSelector(_rule))
return Enumerable.Empty<Word>();

if (
input.GetUnapplicationCount(_rule) >= _rule.MaxApplicationCount
|| !_outputFS.IsUnifiable(input.SyntacticFeatureStruct)
)
if (input.GetUnapplicationCount(_rule) >= _rule.MaxApplicationCount)
Comment thread
claude[bot] marked this conversation as resolved.
{
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.MorphologicalRuleNotUnapplied(
_rule,
-1,
input,
FailureReason.MaxApplicationCount,
_rule.MaxApplicationCount
);
return Enumerable.Empty<Word>();
}
if (!_outputFS.IsUnifiable(input.SyntacticFeatureStruct))
{
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.MorphologicalRuleNotUnapplied(
_rule,
-1,
input,
FailureReason.OutputSyntacticFeatureStruct,
_outputFS
);
return Enumerable.Empty<Word>();
}

var output = new List<Word>();
for (int i = 0; i < _rules.Count; i++)
{
bool matched = false;
bool unapplied = false;
foreach (Word outWord in _rules[i].Apply(input).RemoveDuplicates())
{
matched = true;
// the paths Out writes describe this rule's output, not the stem it applied to, so they are
// stripped rather than carried backwards; what is left must unify with Required
outWord.SyntacticFeatureStruct.Restrict(_rule.OutSyntacticFeatureStruct);
Expand All @@ -75,6 +94,14 @@ out FeatureStruct syntacticFS
)
)
{
if (_morpher.TraceManager.IsTracing)
_morpher.TraceManager.MorphologicalRuleNotUnapplied(
_rule,
i,
outWord,
FailureReason.RequiredSyntacticFeatureStruct,
Comment thread
claude[bot] marked this conversation as resolved.
_rule.RequiredSyntacticFeatureStruct
);
continue;
}

Expand All @@ -97,8 +124,25 @@ out FeatureStruct syntacticFS
unapplied = true;
}

if (_morpher.TraceManager.IsTracing && !unapplied)
_morpher.TraceManager.MorphologicalRuleNotUnapplied(_rule, i, input);
if (_morpher.TraceManager.IsTracing)
{
// ITraceManager implementations are told only that no word came out of the subrule
if (_morpher.TraceManager is IDetailedTraceManager)
{
if (!matched)
_morpher.TraceManager.MorphologicalRuleNotUnapplied(
_rule,
i,
input,
FailureReason.Pattern,
null
);
}
else if (!unapplied)
{
_morpher.TraceManager.MorphologicalRuleNotUnapplied(_rule, i, input);
}
}
}
return output;
}
Expand Down
Loading
Loading