コード例 #1
0
        public void GenerateWords_CanGenerate_ReturnsCorrectWord()
        {
            var any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;

            var siPrefix = new AffixProcessRule
            {
                Id    = "3SG",
                Name  = "si_prefix",
                Gloss = "3SG",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value
            };

            siPrefix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new InsertSegments(Table3, "si+"), new CopyFromInput("1") }
            });
            Morphophonemic.MorphologicalRules.Add(siPrefix);

            var edSuffix = new AffixProcessRule
            {
                Id    = "PAST",
                Name  = "ed_suffix",
                Gloss = "PAST",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value
            };

            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+ɯd") }
            });
            Morphophonemic.MorphologicalRules.Add(edSuffix);

            var morpher = new Morpher(TraceManager, Language);

            var analysis = new WordAnalysis(new IMorpheme[] { siPrefix, Entries["33"], edSuffix }, 1, "V");

            string[] words = morpher.GenerateWords(analysis).ToArray();
            Assert.That(words, Is.EquivalentTo(new[] { "sisasɯd" }));
        }
コード例 #2
0
ファイル: RootAllomorph.cs プロジェクト: ermshiperete/machine
        internal override bool IsWordValid(Morpher morpher, Word word)
        {
            if (!base.IsWordValid(morpher, word))
            {
                return(false);
            }

            if (IsBound && word.Allomorphs.Count == 1)
            {
                if (morpher.TraceManager.IsTracing)
                {
                    morpher.TraceManager.Failed(morpher.Language, word, FailureReason.BoundRoot, this, null);
                }
                return(false);
            }

            if (StemName != null && !StemName.IsRequiredMatch(word.SyntacticFeatureStruct))
            {
                if (morpher.TraceManager.IsTracing)
                {
                    morpher.TraceManager.Failed(morpher.Language, word, FailureReason.RequiredStemName, this, StemName);
                }
                return(false);
            }

            foreach (RootAllomorph otherAllo in ((LexEntry)Morpheme).Allomorphs.Where(a => a != this && a.StemName != null))
            {
                if (!otherAllo.StemName.IsExcludedMatch(word.SyntacticFeatureStruct, StemName))
                {
                    if (morpher.TraceManager.IsTracing)
                    {
                        morpher.TraceManager.Failed(morpher.Language, word, FailureReason.ExcludedStemName, this, otherAllo.StemName);
                    }
                    return(false);
                }
            }

            return(true);
        }
コード例 #3
0
ファイル: LexEntryTests.cs プロジェクト: ermshiperete/machine
        public void FreeFluctuation()
        {
            var any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;
            var d   = FeatureStruct.New(Language.PhonologicalFeatureSystem)
                      .Symbol(HCFeatureSystem.Segment)
                      .Symbol("cons+")
                      .Symbol("strident-")
                      .Symbol("del_rel-")
                      .Symbol("alveolar")
                      .Symbol("nasal-")
                      .Symbol("vd+").Value;

            var edSuffix = new AffixProcessRule
            {
                Name  = "ed_suffix",
                Gloss = "PAST",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value
            };

            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+t") }
            });
            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+"), new InsertSimpleContext(d) }
            });
            Morphophonemic.MorphologicalRules.Add(edSuffix);

            var morpher = new Morpher(TraceManager, Language);

            AssertMorphsEqual(morpher.ParseWord("tazd"), "free PAST");
            AssertMorphsEqual(morpher.ParseWord("tast"), "free PAST");
            AssertMorphsEqual(morpher.ParseWord("tazt"), "free PAST");
            AssertMorphsEqual(morpher.ParseWord("tasd"), "free PAST");
        }
コード例 #4
0
ファイル: LexEntryTests.cs プロジェクト: ermshiperete/machine
        public void BoundRootAllomorph()
        {
            var any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;

            var edSuffix = new AffixProcessRule
            {
                Name  = "ed_suffix",
                Gloss = "PAST",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value
            };

            Morphophonemic.MorphologicalRules.Add(edSuffix);
            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+ɯd") }
            });

            var morpher = new Morpher(TraceManager, Language);

            Assert.That(morpher.ParseWord("dag"), Is.Empty);
            AssertMorphsEqual(morpher.ParseWord("dagɯd"), "bound PAST");
        }
コード例 #5
0
        public void AnalyzeWord_CannotAnalyze_ReturnsEmptyEnumerable()
        {
            var any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;

            var edSuffix = new AffixProcessRule
            {
                Id    = "PAST",
                Name  = "ed_suffix",
                Gloss = "PAST",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value
            };

            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+d") }
            });
            Morphophonemic.MorphologicalRules.Add(edSuffix);

            var morpher = new Morpher(TraceManager, Language);

            Assert.That(morpher.AnalyzeWord("sagt"), Is.Empty);
        }
コード例 #6
0
ファイル: Allomorph.cs プロジェクト: ermshiperete/machine
        internal virtual bool IsWordValid(Morpher morpher, Word word)
        {
            AllomorphEnvironment env = Environments.FirstOrDefault(e => !e.IsWordValid(word));

            if (env != null)
            {
                if (morpher.TraceManager.IsTracing)
                {
                    morpher.TraceManager.Failed(morpher.Language, word, FailureReason.Environments, this, env);
                }
                return(false);
            }

            AllomorphCoOccurrenceRule alloRule = AllomorphCoOccurrenceRules.FirstOrDefault(r => !r.IsWordValid(word));

            if (alloRule != null)
            {
                if (morpher.TraceManager.IsTracing)
                {
                    morpher.TraceManager.Failed(morpher.Language, word, FailureReason.AllomorphCoOccurrenceRules, this, alloRule);
                }
                return(false);
            }

            MorphemeCoOccurrenceRule morphemeRule = Morpheme.MorphemeCoOccurrenceRules.FirstOrDefault(r => !r.IsWordValid(word));

            if (morphemeRule != null)
            {
                if (morpher.TraceManager.IsTracing)
                {
                    morpher.TraceManager.Failed(morpher.Language, word, FailureReason.MorphemeCoOccurrenceRules, this, morphemeRule);
                }
                return(false);
            }

            return(true);
        }
コード例 #7
0
        public SynthesisStratumRule(Morpher morpher, Stratum stratum)
        {
            _templatesRule = new SynthesisAffixTemplatesRule(morpher, stratum);
            _mrulesRule    = null;
            IEnumerable <IRule <Word, ShapeNode> > mrules = stratum.MorphologicalRules
                                                            .Select(mrule => mrule.CompileSynthesisRule(morpher));

            switch (stratum.MorphologicalRuleOrder)
            {
            case MorphologicalRuleOrder.Linear:
                _mrulesRule = new LinearRuleCascade <Word, ShapeNode>(mrules, true,
                                                                      FreezableEqualityComparer <Word> .Default);
                break;

            case MorphologicalRuleOrder.Unordered:
                _mrulesRule = new CombinationRuleCascade <Word, ShapeNode>(mrules, true,
                                                                           FreezableEqualityComparer <Word> .Default);
                break;
            }
            _prulesRule = new LinearRuleCascade <Word, ShapeNode>(
                stratum.PhonologicalRules.Select(prule => prule.CompileSynthesisRule(morpher)));
            _stratum = stratum;
            _morpher = morpher;
        }
コード例 #8
0
 public override IRule <Word, ShapeNode> CompileSynthesisRule(Morpher morpher)
 {
     return(new SynthesisStratumRule(morpher, this));
 }
コード例 #9
0
 public void Compile()
 {
     _morpher = new Morpher(new TraceManager(), _language);
 }
コード例 #10
0
ファイル: LexEntryTests.cs プロジェクト: ermshiperete/machine
        public void StemNames()
        {
            var any = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;

            var edSuffix = new AffixProcessRule
            {
                Name  = "ed_suffix",
                Gloss = "1",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value,
                OutSyntacticFeatureStruct      = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                                 .Feature(Head).EqualTo(head => head
                                                                        .Feature("pers").EqualTo("1")).Value
            };

            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+ɯd") }
            });
            Morphophonemic.MorphologicalRules.Add(edSuffix);

            var tSuffix = new AffixProcessRule
            {
                Name  = "t_suffix",
                Gloss = "2",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value,
                OutSyntacticFeatureStruct      = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                                 .Feature(Head).EqualTo(head => head
                                                                        .Feature("pers").EqualTo("2")).Value
            };

            tSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+t") }
            });
            Morphophonemic.MorphologicalRules.Add(tSuffix);

            var sSuffix = new AffixProcessRule
            {
                Name  = "s_suffix",
                Gloss = "3",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value,
                OutSyntacticFeatureStruct      = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                                 .Feature(Head).EqualTo(head => head
                                                                        .Feature("pers").EqualTo("3")).Value
            };

            sSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "+s") }
            });
            Morphophonemic.MorphologicalRules.Add(sSuffix);

            var morpher = new Morpher(TraceManager, Language);

            AssertMorphsEqual(morpher.ParseWord("sanɯd"));
            AssertMorphsEqual(morpher.ParseWord("sant"));
            AssertMorphsEqual(morpher.ParseWord("sans"));
            AssertMorphsEqual(morpher.ParseWord("san"), "stemname");

            AssertMorphsEqual(morpher.ParseWord("sadɯd"), "stemname 1");
            AssertMorphsEqual(morpher.ParseWord("sadt"), "stemname 2");
            AssertMorphsEqual(morpher.ParseWord("sads"));
            AssertMorphsEqual(morpher.ParseWord("sad"));

            AssertMorphsEqual(morpher.ParseWord("sapɯd"), "stemname 1");
            AssertMorphsEqual(morpher.ParseWord("sapt"));
            AssertMorphsEqual(morpher.ParseWord("saps"), "stemname 3");
            AssertMorphsEqual(morpher.ParseWord("sap"));
        }
コード例 #11
0
 public abstract IRule <Word, ShapeNode> CompileSynthesisRule(Morpher morpher);
コード例 #12
0
 public abstract IRule <Word, ShapeNode> CompileAnalysisRule(Morpher morpher);
コード例 #13
0
        public void RealizationalRule()
        {
            var any     = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;
            var alvStop = FeatureStruct.New(Language.PhonologicalFeatureSystem)
                          .Symbol(HCFeatureSystem.Segment)
                          .Symbol("cons+")
                          .Symbol("strident-")
                          .Symbol("del_rel-")
                          .Symbol("alveolar").Value;
            var voicelessCons = FeatureStruct.New(Language.PhonologicalFeatureSystem)
                                .Symbol(HCFeatureSystem.Segment)
                                .Symbol("cons+")
                                .Symbol("vd-").Value;
            var labiodental = FeatureStruct.New(Language.PhonologicalFeatureSystem)
                              .Symbol(HCFeatureSystem.Segment)
                              .Symbol("cons+")
                              .Symbol("labiodental").Value;
            var voiced = FeatureStruct.New(Language.PhonologicalFeatureSystem)
                         .Symbol(HCFeatureSystem.Segment)
                         .Symbol("vd+").Value;
            var strident = FeatureStruct.New(Language.PhonologicalFeatureSystem)
                           .Symbol(HCFeatureSystem.Segment)
                           .Symbol("cons+")
                           .Symbol("strident+").Value;

            var edSuffix = new RealizationalAffixProcessRule
            {
                Name = "ed_suffix",
                RealizationalFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                             .Feature(Head).EqualTo(head => head
                                                                    .Feature("tense").EqualTo("past")).Value,
                Gloss = "PAST"
            };

            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs =
                {
                    Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value,
                    Pattern <Word, ShapeNode> .New("2").Annotation(alvStop).Value
                },
                Rhs = { new CopyFromInput("1"), new CopyFromInput("2"), new InsertSegments(Table3, "ɯd") }
            });
            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Annotation(voicelessCons).Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "t") }
            });
            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "d") }
            });

            var sSuffix = new RealizationalAffixProcessRule
            {
                Name = "s_suffix",
                RealizationalFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                             .Feature(Head).EqualTo(head => head
                                                                    .Feature("pers").EqualTo("3")
                                                                    .Feature("tense").EqualTo("pres")).Value,
                Gloss = "3SG"
            };

            sSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs =
                {
                    Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value,
                    Pattern <Word, ShapeNode> .New("2").Annotation(labiodental).Value
                },
                Rhs = { new CopyFromInput("1"), new ModifyFromInput("2", voiced), new InsertSegments(Table3, "z") }
            });
            sSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Annotation(strident).Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "ɯz") }
            });
            sSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs =
                {
                    Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value,
                    Pattern <Word, ShapeNode> .New("2").Annotation(voicelessCons).Value
                },
                Rhs = { new CopyFromInput("1"), new CopyFromInput("2"), new InsertSegments(Table3, "s") }
            });
            sSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "z") }
            });

            var evidential = new RealizationalAffixProcessRule
            {
                Name = "evidential",
                RealizationalFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                             .Feature(Head).EqualTo(head => head
                                                                    .Feature("evidential").EqualTo("witnessed")).Value,
                Gloss = "WIT"
            };

            evidential.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "v") }
            });

            var verbTemplate = new AffixTemplate {
                Name = "verb", RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value
            };

            verbTemplate.Slots.Add(new AffixTemplateSlot(sSuffix, edSuffix)
            {
                Optional = true
            });
            verbTemplate.Slots.Add(new AffixTemplateSlot(evidential)
            {
                Optional = true
            });
            Morphophonemic.AffixTemplates.Add(verbTemplate);

            var morpher = new Morpher(TraceManager, Language);

            Word[] output = morpher.ParseWord("sagd").ToArray();
            AssertMorphsEqual(output, "32 PAST");
            AssertSyntacticFeatureStructsEqual(output, FeatureStruct.New(Language.SyntacticFeatureSystem)
                                               .Symbol("V")
                                               .Feature(Head).EqualTo(head => head
                                                                      .Feature("tense").EqualTo("past")).Value);
            output = morpher.ParseWord("sagdv").ToArray();
            AssertMorphsEqual(output, "32 PAST WIT");
            AssertSyntacticFeatureStructsEqual(output, FeatureStruct.New(Language.SyntacticFeatureSystem)
                                               .Symbol("V")
                                               .Feature(Head).EqualTo(head => head
                                                                      .Feature("tense").EqualTo("past")
                                                                      .Feature("evidential").EqualTo("witnessed")).Value);
            Assert.That(morpher.ParseWord("sid"), Is.Empty);
            output = morpher.ParseWord("sau").ToArray();
            AssertMorphsEqual(output, "bl2");
            AssertSyntacticFeatureStructsEqual(output, FeatureStruct.New(Language.SyntacticFeatureSystem)
                                               .Symbol("V")
                                               .Feature(Head).EqualTo(head => head
                                                                      .Feature("tense").EqualTo("past")).Value);

            evidential.RealizationalFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                                    .Feature(Head).EqualTo(head => head
                                                                           .Feature("evidential").EqualTo("witnessed")
                                                                           .Feature("tense").EqualTo("pres")).Value;

            morpher = new Morpher(TraceManager, Language);
            output  = morpher.ParseWord("sagzv").ToArray();
            AssertMorphsEqual(output, "32 3SG WIT");
            AssertSyntacticFeatureStructsEqual(output, FeatureStruct.New(Language.SyntacticFeatureSystem)
                                               .Symbol("V")
                                               .Feature(Head).EqualTo(head => head
                                                                      .Feature("pers").EqualTo("3")
                                                                      .Feature("tense").EqualTo("pres")
                                                                      .Feature("evidential").EqualTo("witnessed")).Value);
        }
コード例 #14
0
 public override IRule <Word, ShapeNode> CompileSynthesisRule(Morpher morpher)
 {
     return(new PipelineRuleCascade <Word, ShapeNode>(
                _strata.Select(stratum => stratum.CompileSynthesisRule(morpher)),
                FreezableEqualityComparer <Word> .Default));
 }
コード例 #15
0
 public override IRule <Word, ShapeNode> CompileAnalysisRule(Morpher morpher)
 {
     return(new AnalysisLanguageRule(morpher, this));
 }
コード例 #16
0
 public AnalysisLanguageRule(Morpher morpher, Language language)
 {
     _morpher = morpher;
     _strata  = language.Strata.Reverse().ToList();
     _rules   = _strata.Select(stratum => stratum.CompileAnalysisRule(morpher)).ToList();
 }
コード例 #17
0
ファイル: AffixTemplate.cs プロジェクト: ermshiperete/machine
 public override IRule <Word, ShapeNode> CompileSynthesisRule(Morpher morpher)
 {
     return(new SynthesisAffixTemplateRule(morpher, this));
 }
コード例 #18
0
        public void NonFinalTemplate()
        {
            var any     = FeatureStruct.New().Symbol(HCFeatureSystem.Segment).Value;
            var alvStop = FeatureStruct.New(Language.PhonologicalFeatureSystem)
                          .Symbol(HCFeatureSystem.Segment)
                          .Symbol("cons+")
                          .Symbol("strident-")
                          .Symbol("del_rel-")
                          .Symbol("alveolar").Value;
            var voicelessCons = FeatureStruct.New(Language.PhonologicalFeatureSystem)
                                .Symbol(HCFeatureSystem.Segment)
                                .Symbol("cons+")
                                .Symbol("vd-").Value;

            var edSuffix = new AffixProcessRule
            {
                Name  = "ed_suffix",
                Gloss = "PAST",
            };

            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs =
                {
                    Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value,
                    Pattern <Word, ShapeNode> .New("2").Annotation(alvStop).Value
                },
                Rhs = { new CopyFromInput("1"), new CopyFromInput("2"), new InsertSegments(Table3, "ɯd") }
            });
            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Annotation(voicelessCons).Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "t") }
            });
            edSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "d") }
            });

            var verbTemplate = new AffixTemplate
            {
                Name = "verb",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value
            };

            verbTemplate.Slots.Add(new AffixTemplateSlot(edSuffix));
            Morphophonemic.AffixTemplates.Add(verbTemplate);

            var nominalizer = new AffixProcessRule
            {
                Name  = "nominalizer",
                Gloss = "NOM",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("V").Value,
                OutSyntacticFeatureStruct      = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("N").Value
            };

            nominalizer.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "v") }
            });
            Morphophonemic.MorphologicalRules.Add(nominalizer);

            var crule = new CompoundingRule
            {
                Name = "rule1",
                HeadRequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                                     .Symbol("V").Value,
                NonHeadRequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem)
                                                        .Symbol("N").Value,
                OutSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("N").Value
            };

            crule.Subrules.Add(new CompoundingSubrule
            {
                HeadLhs    = { Pattern <Word, ShapeNode> .New("head").Annotation(any).OneOrMore.Value },
                NonHeadLhs = { Pattern <Word, ShapeNode> .New("nonHead").Annotation(any).OneOrMore.Value },
                Rhs        = { new CopyFromInput("head"), new InsertSegments(Table3, "+"), new CopyFromInput("nonHead") }
            });
            Morphophonemic.MorphologicalRules.Add(crule);

            var sSuffix = new AffixProcessRule
            {
                Name  = "s_suffix",
                Gloss = "PL",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("N").Value,
            };

            sSuffix.Allomorphs.Add(new AffixProcessAllomorph
            {
                Lhs = { Pattern <Word, ShapeNode> .New("1").Annotation(any).OneOrMore.Value },
                Rhs = { new CopyFromInput("1"), new InsertSegments(Table3, "s") }
            });

            var nounTemplate = new AffixTemplate
            {
                Name = "noun",
                RequiredSyntacticFeatureStruct = FeatureStruct.New(Language.SyntacticFeatureSystem).Symbol("N").Value
            };

            nounTemplate.Slots.Add(new AffixTemplateSlot(sSuffix)
            {
                Optional = true
            });
            Morphophonemic.AffixTemplates.Add(nounTemplate);

            var morpher = new Morpher(TraceManager, Language);

            AssertMorphsEqual(morpher.ParseWord("sagd"), "32 PAST");
            AssertMorphsEqual(morpher.ParseWord("sagdv"));
            AssertMorphsEqual(morpher.ParseWord("sagdvs"));
            AssertMorphsEqual(morpher.ParseWord("sagdmi"));
            AssertMorphsEqual(morpher.ParseWord("sagdmis"));

            verbTemplate.IsFinal = false;
            morpher = new Morpher(TraceManager, Language);
            AssertMorphsEqual(morpher.ParseWord("sagd"));
            AssertMorphsEqual(morpher.ParseWord("sagdv"), "32 PAST NOM");
            AssertMorphsEqual(morpher.ParseWord("sagdvs"), "32 PAST NOM PL");
            AssertMorphsEqual(morpher.ParseWord("sagdmi"), "32 PAST 53");
            AssertMorphsEqual(morpher.ParseWord("sagdmis"), "32 PAST 53 PL");
        }