true
if this string starts with 11, 18, or 8,
+ * excluding strings that start with 180 or 110
+ */
+
+ public static bool requiresAn(string stringa)
+ {
+ var req = false;
+
+ var lowercaseInput = stringa.toLowerCase();
+
+ if (lowercaseInput.matches(AN_AGREEMENT) && !isAnException(lowercaseInput))
+ {
+ req = true;
+
+ }
+ else
+ {
+ var numPref = getNumericPrefix(lowercaseInput);
+
+ if (numPref != null && numPref.length() > 0
+ && numPref.matches(@"^(8|11|18).*$"))
+ {
+ var num = int.Parse(numPref);
+ req = checkNum(num);
+ }
+ }
+
+ return req;
+ }
+
+ /*
+ * check whether a string beginning with a vowel is an exception and doesn't
+ * take "an" (e.g. "a one percent change")
+ *
+ * @return
+ */
+
+ private static bool isAnException(string stringa)
+ {
+ foreach (var ex in AN_EXCEPTIONS)
+ {
+ if (stringa.matches("^" + ex + ".*"))
+ {
+ // if (string.equalsIgnoreCase(ex)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ * Returns true
if the number starts with 8, 11 or 18 and is
+ * either less than 100 or greater than 1000, but excluding 180,000 etc.
+ */
+
+ private static bool checkNum(int num)
+ {
+ var needsAn = false;
+
+ // eight, eleven, eighty and eighteen
+ if (num == 11 || num == 18 || num == 8 || (num >= 80 && num < 90))
+ {
+ needsAn = true;
+
+ }
+ else if (num > 1000)
+ {
+ // num = Math.Round(num / 1000);
+ num = num/1000;
+ needsAn = checkNum(num);
+ }
+
+ return needsAn;
+ }
+
+ /*
+ * Retrieve the numeral prefix of a string.
+ */
+
+ private static string getNumericPrefix(string stringa)
+ {
+ var numeric = new StringBuilder();
+
+ if (stringa != null)
+ {
+ stringa = stringa.Trim();
+
+ if (stringa.length() > 0)
+ {
+
+ var buffer = new StringBuilder(stringa);
+ var first = buffer.charAt(0);
+
+ if (first.isDigit())
+ {
+ numeric.append(first);
+
+ for (var i = 1; i < buffer.length(); i++)
+ {
+ var next = buffer.charAt(i);
+
+ if (next.isDigit())
+ {
+ numeric.append(next);
+
+ // skip commas within numbers
+ }
+ else if (next.Equals(','))
+ {
+ continue;
+
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return numeric.length() == 0 ? null : numeric.ToString();
+ }
+
+
+ /**
+ * Check to see if a string ends with the indefinite article "a" and it agrees with {@code np}.
+ * @param text
+ * @param np
+ * @return an altered version of {@code text} to use "an" if it agrees with {@code np}, the original string otherwise.
+ */
+
+ public static string checkEndsWithIndefiniteArticle(string text, string np)
+ {
+
+ var tokens = text.Split(' ');
+
+ var lastToken = tokens[tokens.Length - 1];
+
+ if (lastToken.equalsIgnoreCase("a") && DeterminerAgrHelper.requiresAn(np))
+ {
+
+ tokens[tokens.Length - 1] = "an";
+
+ return stringArrayToString(tokens);
+
+ }
+
+ return text;
+
+ }
+
+ // Turns ["a","b","c"] into "a b c"
+ private static string stringArrayToString(string[] sArray)
+ {
+
+ var buf = new StringBuilder();
+
+ for (var i = 0; i < sArray.Length; i++)
+ {
+
+ buf.Append(sArray[i]);
+
+ if (i != sArray.Length - 1)
+ {
+
+ buf.Append(" ");
+
+ }
+
+ }
+
+ return buf.ToString();
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/SharpSimpleNLGTests/morphology/MorphologyProcessor.cs b/SharpSimpleNLGTests/morphology/MorphologyProcessor.cs
new file mode 100644
index 0000000..2ccfeda
--- /dev/null
+++ b/SharpSimpleNLGTests/morphology/MorphologyProcessor.cs
@@ -0,0 +1,302 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is "SharpSimpleNLG".
+ *
+ * The Initial Developer of the Original Code is Ehud Reiter, Albert Gatt and Dave Westwater.
+ * Portions created by Ehud Reiter, Albert Gatt and Dave Westwater are Copyright (C) 2010-11 The University of Aberdeen. All Rights Reserved.
+ *
+ * Contributor(s): Ehud Reiter, Albert Gatt, Dave Wewstwater, Roman Kutlak, Margaret Mitchell, Saad Mahamood, Nick Hodge
+ */
+
+/* Additional Notes:
+ * - Original Java source is SimpleNLG from 12-Jun-2016 https://github.com/simplenlg/simplenlg
+ * - This is a port of the Java version to C# with no additional features
+ * - I have left the "Initial Developer" section to reflect this fact
+ * - Any questions, comments, feedback on this port can be sent to Nick Hodge + * This is the processor for handling morphology within the SimpleNLG. The + * processor inflects words form the base form depending on the features applied + * to the word. For example, kiss is inflected to kissed for + * past tense, dog is inflected to dogs for pluralisation. + *
+ * + *+ * As a matter of course, the processor will first use any user-defined + * inflection for the world. If no inflection is provided then the lexicon, if + * it exists, will be examined for the correct inflection. Failing this a set of + * very basic rules will be examined to inflect the word. + *
+ * + *
+ * All processing modules perform realisation on a tree of
+ * NLGElement
s. The modules can alter the tree in whichever way
+ * they wish. For example, the syntax processor replaces phrase elements with
+ * list elements consisting of inflected words while the morphology processor
+ * replaces inflected words with string elements.
+ *
+ * N.B. the use of module, processing module and + * processor is interchangeable. They all mean an instance of this + * class. + *
+ * + * + * @author D. Westwater, University of Aberdeen. + * @version 4.0 + */ + + public class MorphologyProcessor : NLGModule + { + + + public override void initialise() + { + // Do nothing + } + + + public override INLGElement realise(INLGElement element) + { + INLGElement realisedElement = null; + + if (element is InflectedWordElement) + { + realisedElement = doMorphology((InflectedWordElement) element); + + } + else if (element is StringElement) + { + realisedElement = element; + + } + else if (element is WordElement) + { + // AG: now retrieves the default spelling variant, not the baseform + // string baseForm = ((WordElement) element).getBaseForm(); + var defaultSpell = ((WordElement) element).getDefaultSpellingVariant(); + + if (defaultSpell != null) + { + realisedElement = new StringElement(defaultSpell); + } + + } + else if (element is DocumentElement) + { + ListMorphologyRules.
+ *
+ * @param element
+ * the InflectedWordElement
+ * @return an NLGElement
reflecting the correct inflection for
+ * the word.
+ */
+
+ private INLGElement doMorphology(InflectedWordElement element)
+ {
+ INLGElement realisedElement = null;
+ if (element.getFeatureAsBoolean(InternalFeature.NON_MORPH.ToString()))
+ {
+ realisedElement = new StringElement(element.getBaseForm());
+ realisedElement.setFeature(InternalFeature.DISCOURSE_FUNCTION.ToString(),
+ element.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString()));
+
+ }
+ else
+ {
+ INLGElement baseWord = element.getFeatureAsElement(InternalFeature.BASE_WORD.ToString());
+
+ if (baseWord == null && this.lexicon != null)
+ {
+ baseWord = this.lexicon.lookupWord(element.getBaseForm());
+ }
+
+ var category = element.getCategory();
+
+ if (category is ILexicalCategory)
+ {
+ switch ((LexicalCategoryEnum)category.enumType)
+ {
+ case LexicalCategoryEnum.PRONOUN:
+ realisedElement = MorphologyRules.doPronounMorphology(element);
+ break;
+
+ case LexicalCategoryEnum.NOUN:
+ realisedElement = MorphologyRules.doNounMorphology(element, (WordElement) baseWord);
+ break;
+
+ case LexicalCategoryEnum.VERB:
+ realisedElement = MorphologyRules.doVerbMorphology(element, (WordElement) baseWord);
+ break;
+
+ case LexicalCategoryEnum.ADJECTIVE:
+ realisedElement = MorphologyRules.doAdjectiveMorphology(element, (WordElement) baseWord);
+ break;
+
+ case LexicalCategoryEnum.ADVERB:
+ realisedElement = MorphologyRules.doAdverbMorphology(element, (WordElement) baseWord);
+ break;
+
+ default:
+ realisedElement = new StringElement(element.getBaseForm());
+ realisedElement.setFeature(InternalFeature.DISCOURSE_FUNCTION.ToString(),
+ element.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString()));
+ break;
+ }
+ }
+ }
+ return realisedElement;
+ }
+
+
+ public override List realise(List elements)
+ {
+ var realisedElements = new List();
+ INLGElement currentElement = null;
+ INLGElement determiner = null;
+ INLGElement prevElement = null;
+
+ if (elements != null)
+ {
+ foreach (var eachElement in elements)
+ {
+ currentElement = realise(eachElement);
+
+ if (currentElement != null)
+ {
+ //pass the discourse function and appositive features -- important for orth processor
+ currentElement.setFeature(Feature.APPOSITIVE.ToString(),
+ eachElement.getFeature(Feature.APPOSITIVE.ToString()));
+ var function = eachElement.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString());
+
+ if (function != null)
+ {
+ currentElement.setFeature(InternalFeature.DISCOURSE_FUNCTION.ToString(), function);
+ }
+
+ if (prevElement != null && prevElement is StringElement
+ && eachElement is InflectedWordElement
+ && (eachElement.getCategory().enumType == (int)LexicalCategoryEnum.NOUN))
+ {
+
+ var prevString = prevElement.getRealisation();
+
+ //realisedElements.get(realisedElements.size() - 1)
+
+ prevElement.setRealisation(DeterminerAgrHelper.checkEndsWithIndefiniteArticle(prevString,
+ currentElement.getRealisation()));
+
+ }
+
+ // realisedElements.add(realise(currentElement));
+ realisedElements.add(currentElement);
+
+ if (determiner == null && DiscourseFunction.SPECIFIER.Equals(currentElement.getFeature(
+ InternalFeature.DISCOURSE_FUNCTION.ToString())))
+ {
+ determiner = currentElement;
+ determiner.setFeature(Feature.NUMBER.ToString(),
+ eachElement.getFeature(Feature.NUMBER.ToString()));
+ // MorphologyRules.doDeterminerMorphology(determiner,
+ // currentElement.getRealisation());
+
+ }
+ else if (determiner != null)
+ {
+
+ if (currentElement is ListElement)
+ {
+ // list elements: ensure det matches first element
+ INLGElement firstChild = ((ListElement) currentElement).getChildren().get(0);
+
+ if (firstChild != null)
+ {
+ //AG: need to check if child is a coordinate
+ if (firstChild is CoordinatedPhraseElement)
+ {
+ MorphologyRules.doDeterminerMorphology(determiner,
+ firstChild.getChildren().get(0).getRealisation());
+ }
+ else
+ {
+ MorphologyRules.doDeterminerMorphology(determiner, firstChild.getRealisation());
+ }
+ }
+
+ }
+ else
+ {
+ // everything else: ensure det matches realisation
+ MorphologyRules.doDeterminerMorphology(determiner, currentElement.getRealisation());
+ }
+
+ determiner = null;
+ }
+ }
+ prevElement = eachElement;
+ }
+ }
+
+ return realisedElements;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/SharpSimpleNLGTests/morphology/MorphologyRules.cs b/SharpSimpleNLGTests/morphology/MorphologyRules.cs
new file mode 100644
index 0000000..70f67fc
--- /dev/null
+++ b/SharpSimpleNLGTests/morphology/MorphologyRules.cs
@@ -0,0 +1,1148 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is "SharpSimpleNLG".
+ *
+ * The Initial Developer of the Original Code is Ehud Reiter, Albert Gatt and Dave Westwater.
+ * Portions created by Ehud Reiter, Albert Gatt and Dave Westwater are Copyright (C) 2010-11 The University of Aberdeen. All Rights Reserved.
+ *
+ * Contributor(s): Ehud Reiter, Albert Gatt, Dave Wewstwater, Roman Kutlak, Margaret Mitchell, Saad Mahamood, Nick Hodge
+ */
+
+/* Additional Notes:
+ * - Original Java source is SimpleNLG from 12-Jun-2016 https://github.com/simplenlg/simplenlg
+ * - This is a port of the Java version to C# with no additional features
+ * - I have left the "Initial Developer" section to reflect this fact
+ * - Any questions, comments, feedback on this port can be sent to Nick Hodge
+ */
+
+ using System.Text;
+ using SharpNLG.Extensions;
+ using SimpleNLG.Extensions;
+
+namespace SimpleNLG
+{
+ /**
+ *
+ * This abstract class contains a number of rules for doing simple inflection.
+ *
+ *
+ *
+ * As a matter of course, the processor will first use any user-defined
+ * inflection for the world. If no inflection is provided then the lexicon, if
+ * it exists, will be examined for the correct inflection. Failing this a set of
+ * very basic rules will be examined to inflect the word.
+ *
+ *
+ *
+ * All processing modules perform realisation on a tree of
+ * NLGElement
s. The modules can alter the tree in whichever way
+ * they wish. For example, the syntax processor replaces phrase elements with
+ * list elements consisting of inflected words while the morphology processor
+ * replaces inflected words with string elements.
+ *
+ *
+ *
+ * N.B. the use of module, processing module and
+ * processor is interchangeable. They all mean an instance of this
+ * class.
+ *
+ *
+ *
+ * @author D. Westwater, University of Aberdeen.
+ * @version 4.0 16-Mar-2011 modified to use correct base form (ER)
+ */
+
+ public abstract class MorphologyRules : NLGModule
+ {
+
+ /**
+ * A triple array of Pronouns organised by singular/plural,
+ * possessive/reflexive/subjective/objective and by gender/person.
+ */
+
+ private static string[][][] PRONOUNS = new string[][][]
+ { new string[][]
+ {
+ new string[] {"I", "you", "he", "she", "it"},
+ new string[]{"me", "you", "him", "her", "it"},
+ new string[]{"myself", "yourself", "himself", "herself", "itself"},
+ new string[]{"mine", "yours", "his", "hers", "its"},
+ new string[] {"my", "your", "his", "her", "its"}
+ },
+ new string[][]{
+ new string[]{"we", "you", "they", "they", "they"},
+ new string[]{"us", "you", "them", "them", "them"},
+ new string[]{
+ "ourselves",
+ "yourselves",
+ "themselves",
+ "themselves",
+ "themselves"
+ },
+ new string[]{"ours", "yours", "theirs", "theirs", "theirs"},
+ new string[] {"our", "your", "their", "their", "their"}
+ }
+ };
+
+ private static string[] WH_PRONOUNS = {"who", "what", "which", "where", "why", "how", "how many"};
+
+ /**
+ * This method performs the morphology for nouns.
+ *
+ * @param element
+ * the InflectedWordElement
.
+ * @param baseWord
+ * the WordElement
as created from the lexicon
+ * entry.
+ * @return a StringElement
representing the word after
+ * inflection.
+ */
+
+ public static StringElement doNounMorphology(InflectedWordElement element, WordElement baseWord)
+ {
+ var realised = new StringBuilder();
+
+ // base form from baseWord if it exists, otherwise from element
+ var baseForm = getBaseForm(element, baseWord);
+
+ if (element.isPlural() && !element.getFeatureAsBoolean(LexicalFeature.PROPER))
+ {
+
+ string pluralForm = null;
+
+ // AG changed: now check if default infl is uncount
+ // if (element.getFeatureAsBoolean(LexicalFeature.NON_COUNT)
+ // ) {
+ // pluralForm = baseForm;
+ var elementDefaultInfl = element.getFeature(LexicalFeature.DEFAULT_INFL);
+
+ if (elementDefaultInfl != null && Inflection.UNCOUNT.Equals(elementDefaultInfl))
+ {
+ pluralForm = baseForm;
+
+ }
+ else
+ {
+ pluralForm = element.getFeatureAsString(LexicalFeature.PLURAL);
+ }
+
+ if (pluralForm == null && baseWord != null)
+ {
+ // AG changed: now check if default infl is uncount
+ // if (baseWord.getFeatureAsBoolean(LexicalFeature.NON_COUNT)
+ // ) {
+ // pluralForm = baseForm;
+ var baseDefaultInfl = baseWord.getFeatureAsString(LexicalFeature.DEFAULT_INFL);
+ if (baseDefaultInfl != null && baseDefaultInfl.Equals("uncount"))
+ {
+ pluralForm = baseForm;
+ }
+ else
+ {
+ pluralForm = baseWord.getFeatureAsString(LexicalFeature.PLURAL);
+ }
+ }
+
+ if (pluralForm == null)
+ {
+ var pattern = element.getFeature(LexicalFeature.DEFAULT_INFL);
+ if (Inflection.GRECO_LATIN_REGULAR.Equals(pattern))
+ {
+ pluralForm = buildGrecoLatinPluralNoun(baseForm);
+ }
+ else
+ {
+ pluralForm = buildRegularPluralNoun(baseForm);
+ }
+ }
+ realised.append(pluralForm);
+
+ }
+ else
+ {
+ realised.append(baseForm);
+ }
+
+ checkPossessive(element, realised);
+ var realisedElement = new StringElement(realised.ToString());
+ realisedElement.setFeature(InternalFeature.DISCOURSE_FUNCTION.ToString(),
+ element.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString()));
+ return realisedElement;
+ }
+
+ /**
+ * Builds a plural for regular nouns. The rules are performed in this order:
+ *
+ * - For nouns ending -Cy, where C is any consonant, the ending
+ * becomes -ies. For example, fly becomes flies.
+ * - For nouns ending -ch, -s, -sh, -x
+ * or -z the ending becomes -es. For example, box
+ * becomes boxes.
+ * - All other nouns have -s appended the other end. For example,
+ * dog becomes dogs.
+ *
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildRegularPluralNoun(string baseForm)
+ {
+ string plural = null;
+ if (baseForm != null)
+ {
+ // .NET doesnt support character class subtraction the same was as Java
+ // was .*[b-z-[eiou]]y\\b
+ if (baseForm.matches(@".*[b-z-[eiou]]y\b"))
+ {
+
+ plural = baseForm.replaceAll(@"y\b", @"ies");
+
+ //AG: changed regex from ".*[szx(ch)(sh)]\\b" (tip of the hat to Ian Tabolt)
+ }
+ else if (baseForm.matches(@".*([szx]|[cs]h)\b"))
+ {
+
+ plural = baseForm + "es";
+
+ }
+ else
+ {
+ plural = baseForm + "s";
+ }
+ }
+ return plural;
+ }
+
+ /**
+ * Builds a plural for Greco-Latin regular nouns. The rules are performed in
+ * this order:
+ *
+ * - For nouns ending -us the ending becomes -i. For
+ * example, focus becomes foci.
+ * - For nouns ending -ma the ending becomes -mata. For
+ * example, trauma becomes traumata.
+ * - For nouns ending -a the ending becomes -ae. For
+ * example, larva becomes larvae.
+ * - For nouns ending -um or -on the ending becomes
+ * -a. For example, taxon becomes taxa.
+ * - For nouns ending -sis the ending becomes -ses. For
+ * example, analysis becomes analyses.
+ * - For nouns ending -is the ending becomes -ides. For
+ * example, cystis becomes cystides.
+ * - For nouns ending -men the ending becomes -mina. For
+ * example, foramen becomes foramina.
+ * - For nouns ending -ex the ending becomes -ices. For
+ * example, index becomes indices.
+ * - For nouns ending -x the ending becomes -ces. For
+ * example, matrix becomes matrices.
+ *
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildGrecoLatinPluralNoun(string baseForm)
+ {
+ string plural = null;
+ if (baseForm != null)
+ {
+ if (baseForm.endsWith("us"))
+ {
+
+ plural = baseForm.replaceAll(@"us\b", "i");
+ }
+ else if (baseForm.endsWith("ma"))
+ {
+
+ plural = baseForm + "ta";
+ }
+ else if (baseForm.endsWith("a"))
+ {
+
+ plural = baseForm + "e";
+ }
+ else if (baseForm.matches(@".*[(um)(on)]\\b"))
+ {
+
+ plural = baseForm.replaceAll(@"[(um)(on)]\\b", "a");
+ }
+ else if (baseForm.endsWith("sis"))
+ {
+
+ plural = baseForm.replaceAll(@"sis\b", "ses");
+ }
+ else if (baseForm.endsWith("is"))
+ {
+
+ plural = baseForm.replaceAll(@"is\b", "ides");
+ }
+ else if (baseForm.endsWith("men"))
+ {
+
+ plural = baseForm.replaceAll(@"men\b", "mina");
+ }
+ else if (baseForm.endsWith("ex"))
+ {
+
+ plural = baseForm.replaceAll(@"ex\b", "ices");
+ }
+ else if (baseForm.endsWith("x"))
+ {
+
+ plural = baseForm.replaceAll(@"x\b", "ces");
+ }
+ else
+ {
+ plural = baseForm;
+ }
+ }
+ return plural;
+ }
+
+ /**
+ * This method performs the morphology for verbs.
+ *
+ * @param element
+ * the InflectedWordElement
.
+ * @param baseWord
+ * the WordElement
as created from the lexicon
+ * entry.
+ * @return a StringElement
representing the word after
+ * inflection.
+ */
+
+ public static INLGElement doVerbMorphology(InflectedWordElement element, WordElement baseWord)
+ {
+
+ string realised = null;
+ var numberValue = element.getFeature(Feature.NUMBER.ToString());
+ var personValue = element.getFeature(Feature.PERSON.ToString());
+ var tenseValue = element.getFeatureTense(Feature.TENSE.ToString());
+
+
+ var formValue = element.getFeature(Feature.FORM.ToString());
+ var patternValue = element.getFeature(LexicalFeature.DEFAULT_INFL);
+
+ // base form from baseWord if it exists, otherwise from element
+ var baseForm = getBaseForm(element, baseWord);
+
+ if (element.getFeatureAsBoolean(Feature.NEGATED.ToString()) || Form.BARE_INFINITIVE.Equals(formValue))
+ {
+ realised = baseForm;
+
+ }
+ else if (Form.PRESENT_PARTICIPLE.Equals(formValue))
+ {
+ realised = element.getFeatureAsString(LexicalFeature.PRESENT_PARTICIPLE);
+
+ if (realised == null && baseWord != null)
+ {
+ realised = baseWord.getFeatureAsString(LexicalFeature.PRESENT_PARTICIPLE);
+ }
+
+ if (realised == null)
+ {
+ if (Inflection.REGULAR_DOUBLE.Equals(patternValue))
+ {
+ realised = buildDoublePresPartVerb(baseForm);
+ }
+ else
+ {
+ realised = buildRegularPresPartVerb(baseForm);
+ }
+ }
+
+ }
+ else if (Tense.PAST.Equals(tenseValue) || Form.PAST_PARTICIPLE.Equals(formValue))
+ {
+
+ if (Form.PAST_PARTICIPLE.Equals(formValue))
+ {
+ realised = element.getFeatureAsString(LexicalFeature.PAST_PARTICIPLE);
+
+ if (realised == null && baseWord != null)
+ {
+ realised = baseWord.getFeatureAsString(LexicalFeature.PAST_PARTICIPLE);
+ }
+
+ if (realised == null)
+ {
+ if ("be".equalsIgnoreCase(baseForm))
+ {
+
+ realised = "been";
+ }
+ else if (Inflection.REGULAR_DOUBLE.Equals(patternValue))
+ {
+ realised = buildDoublePastVerb(baseForm);
+ }
+ else
+ {
+ realised = buildRegularPastVerb(baseForm, numberValue, personValue);
+ }
+ }
+
+ }
+ else
+ {
+ realised = element.getFeatureAsString(LexicalFeature.PAST);
+
+ if (realised == null && baseWord != null)
+ {
+ realised = baseWord.getFeatureAsString(LexicalFeature.PAST);
+ }
+
+ if (realised == null)
+ {
+ if (Inflection.REGULAR_DOUBLE.Equals(patternValue))
+ {
+ realised = buildDoublePastVerb(baseForm);
+ }
+ else
+ {
+ realised = buildRegularPastVerb(baseForm, numberValue, personValue);
+ }
+ }
+ }
+
+ }
+ else if ((numberValue == null || NumberAgreement.SINGULAR.Equals(numberValue)) && (personValue == null
+ || Person.THIRD.Equals(
+ personValue)) &&
+ (Tense.PRESENT.Equals(tenseValue)))
+ {
+
+ realised = element.getFeatureAsString(LexicalFeature.PRESENT3S);
+
+ if (realised == null && baseWord != null && !"be".equalsIgnoreCase(baseForm))
+ {
+
+ realised = baseWord.getFeatureAsString(LexicalFeature.PRESENT3S);
+ }
+ if (realised == null)
+ {
+ realised = buildPresent3SVerb(baseForm);
+ }
+
+ }
+ else
+ {
+ if ("be".equalsIgnoreCase(baseForm))
+ {
+
+ if (Person.FIRST.Equals(personValue) && (NumberAgreement.SINGULAR.Equals(numberValue)
+ || numberValue == null))
+ {
+ realised = "am";
+ }
+ else
+ {
+ realised = "are";
+ }
+ }
+ else
+ {
+ realised = baseForm;
+ }
+ }
+ var realisedElement = new StringElement(realised);
+ realisedElement.setFeature(InternalFeature.DISCOURSE_FUNCTION.ToString(),
+ element.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString()));
+ return realisedElement;
+ }
+
+ /**
+ * return the base form of a word
+ *
+ * @param element
+ * @param baseWord
+ * @return
+ */
+
+ private static string getBaseForm(InflectedWordElement element, WordElement baseWord)
+ {
+ // unclear what the right behaviour should be
+ // for now, prefer baseWord.getBaseForm() to element.getBaseForm() for
+ // verbs (ie, "is" mapped to "be")
+ // but prefer element.getBaseForm() to baseWord.getBaseForm() for other
+ // words (ie, "children" not mapped to "child")
+
+ // AG: changed this to get the default spelling variant
+ // needed to preserve spelling changes in the VP
+
+ if ((int)LexicalCategoryEnum.VERB == element.getCategory().enumType)
+ {
+ if (baseWord != null && baseWord.getDefaultSpellingVariant() != null)
+ return baseWord.getDefaultSpellingVariant();
+ else
+ return element.getBaseForm();
+ }
+ else
+ {
+ if (element.getBaseForm() != null)
+ return element.getBaseForm();
+ else if (baseWord == null)
+ return null;
+ else
+ return baseWord.getDefaultSpellingVariant();
+ }
+
+ // if (LexicalCategory.VERB == element.getCategory()) {
+ // if (baseWord != null && baseWord.getBaseForm() != null)
+ // return baseWord.getBaseForm();
+ // else
+ // return element.getBaseForm();
+ // } else {
+ // if (element.getBaseForm() != null)
+ // return element.getBaseForm();
+ // else if (baseWord == null)
+ // return null;
+ // else
+ // return baseWord.getBaseForm();
+ // }
+ }
+
+ /**
+ * Checks to see if the noun is possessive. If it is then nouns in ending in
+ * -s become -s' while every other noun has -'s appended to
+ * the end.
+ *
+ * @param element
+ * the InflectedWordElement
+ * @param realised
+ * the realisation of the word.
+ */
+
+ private static void checkPossessive(InflectedWordElement element, StringBuilder realised)
+ {
+
+ if (element.getFeatureAsBoolean(Feature.POSSESSIVE.ToString()))
+ {
+ if (realised.charAt(realised.length() - 1) == 's')
+ {
+ realised.append("\'");
+
+ }
+ else
+ {
+ realised.append("'s");
+ }
+ }
+ }
+
+ /**
+ * Builds the third-person singular form for regular verbs. The rules are
+ * performed in this order:
+ *
+ * - If the verb is be the realised form is is.
+ * - For verbs ending -ch, -s, -sh, -x
+ * or -z the ending becomes -es. For example,
+ * preach becomes preaches.
+ * - For verbs ending -y the ending becomes -ies. For
+ * example, fly becomes flies.
+ * - For every other verb, -s is added to the end of the word.
+ *
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildPresent3SVerb(string baseForm)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ if (baseForm.equalsIgnoreCase("be"))
+ {
+
+ morphology = "is";
+ }
+ else if (baseForm.matches(@".*[szx(ch)(sh)]\\b"))
+ {
+
+ morphology = baseForm + "es";
+ }
+ else if (baseForm.matches(@".*[b-z-[eiou]]y\\b"))
+ {
+
+ morphology = baseForm.replaceAll(@"y\b", "ies");
+ }
+ else
+ {
+ morphology = baseForm + "s";
+ }
+ }
+ return morphology;
+ }
+
+ /**
+ * Builds the past-tense form for regular verbs. The rules are performed in
+ * this order:
+ *
+ * - If the verb is be and the number agreement is plural then
+ * the realised form is were.
+ * - If the verb is be and the number agreement is singular then
+ * the realised form is was, unless the person is second, in which
+ * case it's were.
+ * - For verbs ending -e the ending becomes -ed. For
+ * example, chased becomes chased.
+ * - For verbs ending -Cy, where C is any consonant, the ending
+ * becomes -ied. For example, dry becomes dried.
+ * - For every other verb, -ed is added to the end of the word.
+ *
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @param number
+ * the number agreement for the word.
+ * @param person
+ * the person
+ * @return the inflected word.
+ */
+
+ private static string buildRegularPastVerb(string baseForm, object number, object person)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ if (baseForm.equalsIgnoreCase("be"))
+ {
+
+ if (NumberAgreement.PLURAL.Equals(number))
+ {
+ morphology = "were";
+
+ // AG - bug fix to handle second person past (courtesy of
+ // Minh Le)
+ }
+ else if (Person.SECOND.Equals(person))
+ {
+ morphology = "were";
+ }
+ else
+ {
+ morphology = "was";
+ }
+ }
+ else if (baseForm.endsWith("e"))
+ {
+
+ morphology = baseForm + "d";
+ }
+ // NOTE .NET does not support character class subtraction
+ // ref: http://www.rexegg.com/regex-class-operations.html#intersection_workaround
+ // Java previous was: .*[b-z&&[^eiou]]y\\b
+ else if (baseForm.matches(".*[b-z-[eiou]]y\\b"))
+ {
+
+ morphology = baseForm.replaceAll(@"y\b", "ied");
+ }
+ else
+ {
+ morphology = baseForm + "ed";
+ }
+ }
+ return morphology;
+ }
+
+ /**
+ * Builds the past-tense form for verbs that follow the doubling form of the
+ * last consonant. -ed is added to the end after the last consonant
+ * is doubled. For example, tug becomes tugged.
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildDoublePastVerb(string baseForm)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ morphology = baseForm + baseForm.charAt(baseForm.length() - 1) + "ed";
+ }
+ return morphology;
+ }
+
+ /**
+ * Builds the present participle form for regular verbs. The rules are
+ * performed in this order:
+ *
+ * - If the verb is be then the realised form is being.
+ * - For verbs ending -ie the ending becomes -ying. For
+ * example, tie becomes tying.
+ * - For verbs ending -ee, -oe or -ye then
+ * -ing is added to the end. For example, canoe becomes
+ * canoeing.
+ * - For other verbs ending in -e the ending becomes
+ * -ing. For example, chase becomes chasing.
+ * - For all other verbs, -ing is added to the end. For example,
+ * dry becomes drying.
+ *
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildRegularPresPartVerb(string baseForm)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ if (baseForm.equalsIgnoreCase("be"))
+ {
+
+ morphology = "being";
+ }
+ else if (baseForm.endsWith("ie"))
+ {
+
+ morphology = baseForm.replaceAll(@"ie\b", "ying");
+ }
+ else if (baseForm.matches(".*[^iyeo]e\\b"))
+ {
+
+ morphology = baseForm.replaceAll(@"e\b", "ing");
+ }
+ else
+ {
+ morphology = baseForm + "ing";
+ }
+ }
+ return morphology;
+ }
+
+ /**
+ * Builds the present participle form for verbs that follow the doubling
+ * form of the last consonant. -ing is added to the end after the
+ * last consonant is doubled. For example, tug becomes
+ * tugging.
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildDoublePresPartVerb(string baseForm)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ morphology = baseForm + baseForm.charAt(baseForm.length() - 1) + "ing";
+ }
+ return morphology;
+ }
+
+ /**
+ * This method performs the morphology for adjectives.
+ *
+ * @param element
+ * the InflectedWordElement
.
+ * @param baseWord
+ * the WordElement
as created from the lexicon
+ * entry.
+ * @return a StringElement
representing the word after
+ * inflection.
+ */
+
+ public static INLGElement doAdjectiveMorphology(InflectedWordElement element, WordElement baseWord)
+ {
+
+ string realised = null;
+ var patternValue = element.getFeature(LexicalFeature.DEFAULT_INFL);
+
+ // base form from baseWord if it exists, otherwise from element
+ var baseForm = getBaseForm(element, baseWord);
+
+ if (element.getFeatureAsBoolean(Feature.IS_COMPARATIVE.ToString()))
+ {
+ realised = element.getFeatureAsString(LexicalFeature.COMPARATIVE);
+
+ if (realised == null && baseWord != null)
+ {
+ realised = baseWord.getFeatureAsString(LexicalFeature.COMPARATIVE);
+ }
+ if (realised == null)
+ {
+ if (Inflection.REGULAR_DOUBLE.Equals(patternValue))
+ {
+ realised = buildDoubleCompAdjective(baseForm);
+ }
+ else
+ {
+ realised = buildRegularComparative(baseForm);
+ }
+ }
+ }
+ else if (element.getFeatureAsBoolean(Feature.IS_SUPERLATIVE.ToString()))
+ {
+
+ realised = element.getFeatureAsString(LexicalFeature.SUPERLATIVE);
+
+ if (realised == null && baseWord != null)
+ {
+ realised = baseWord.getFeatureAsString(LexicalFeature.SUPERLATIVE);
+ }
+ if (realised == null)
+ {
+ if (Inflection.REGULAR_DOUBLE.Equals(patternValue))
+ {
+ realised = buildDoubleSuperAdjective(baseForm);
+ }
+ else
+ {
+ realised = buildRegularSuperlative(baseForm);
+ }
+ }
+ }
+ else
+ {
+ realised = baseForm;
+ }
+ var realisedElement = new StringElement(realised);
+ realisedElement.setFeature(InternalFeature.DISCOURSE_FUNCTION.ToString(),
+ element.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString()));
+ return realisedElement;
+ }
+
+ /**
+ * Builds the comparative form for adjectives that follow the doubling form
+ * of the last consonant. -er is added to the end after the last
+ * consonant is doubled. For example, fat becomes fatter.
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildDoubleCompAdjective(string baseForm)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ morphology = baseForm + baseForm.charAt(baseForm.length() - 1) + "er";
+ }
+ return morphology;
+ }
+
+ /**
+ * Builds the comparative form for regular adjectives. The rules are
+ * performed in this order:
+ *
+ * - For adjectives ending -Cy, where C is any consonant, the
+ * ending becomes -ier. For example, brainy becomes
+ * brainier.
+ * - For adjectives ending -e the ending becomes -er.
+ * For example, fine becomes finer.
+ * - For all other adjectives, -er is added to the end. For
+ * example, clear becomes clearer.
+ *
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildRegularComparative(string baseForm)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ if (baseForm.matches(@".*[b-z-[eiou]]y\\b"))
+ {
+
+ morphology = baseForm.replaceAll(@"y\b", "ier");
+ }
+ else if (baseForm.endsWith("e"))
+ {
+
+ morphology = baseForm + "r";
+ }
+ else
+ {
+ morphology = baseForm + "er";
+ }
+ }
+ return morphology;
+ }
+
+ /**
+ * Builds the superlative form for adjectives that follow the doubling form
+ * of the last consonant. -est is added to the end after the last
+ * consonant is doubled. For example, fat becomes fattest.
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildDoubleSuperAdjective(string baseForm)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ morphology = baseForm + baseForm.charAt(baseForm.length() - 1) + "est";
+ }
+ return morphology;
+ }
+
+ /**
+ * Builds the superlative form for regular adjectives. The rules are
+ * performed in this order:
+ *
+ * - For verbs ending -Cy, where C is any consonant, the ending
+ * becomes -iest. For example, brainy becomes
+ * brainiest.
+ * - For verbs ending -e the ending becomes -est. For
+ * example, fine becomes finest.
+ * - For all other verbs, -est is added to the end. For example,
+ * clear becomes clearest.
+ *
+ *
+ * @param baseForm
+ * the base form of the word.
+ * @return the inflected word.
+ */
+
+ private static string buildRegularSuperlative(string baseForm)
+ {
+ string morphology = null;
+ if (baseForm != null)
+ {
+ if (baseForm.matches(@".*[b-z-[eiou]]y\\b"))
+ {
+
+ morphology = baseForm.replaceAll(@"y\b", "iest");
+ }
+ else if (baseForm.endsWith("e"))
+ {
+
+ morphology = baseForm + "st";
+ }
+ else
+ {
+ morphology = baseForm + "est";
+ }
+ }
+ return morphology;
+ }
+
+ /**
+ * This method performs the morphology for adverbs.
+ *
+ * @param element
+ * the InflectedWordElement
.
+ * @param baseWord
+ * the WordElement
as created from the lexicon
+ * entry.
+ * @return a StringElement
representing the word after
+ * inflection.
+ */
+
+ public static INLGElement doAdverbMorphology(InflectedWordElement element, WordElement baseWord)
+ {
+
+ string realised = null;
+
+ // base form from baseWord if it exists, otherwise from element
+ var baseForm = getBaseForm(element, baseWord);
+
+ if (element.getFeatureAsBoolean(Feature.IS_COMPARATIVE.ToString()))
+ {
+ realised = element.getFeatureAsString(LexicalFeature.COMPARATIVE);
+
+ if (realised == null && baseWord != null)
+ {
+ realised = baseWord.getFeatureAsString(LexicalFeature.COMPARATIVE);
+ }
+ if (realised == null)
+ {
+ realised = buildRegularComparative(baseForm);
+ }
+ }
+ else if (element.getFeatureAsBoolean(Feature.IS_SUPERLATIVE.ToString()))
+ {
+
+ realised = element.getFeatureAsString(LexicalFeature.SUPERLATIVE);
+
+ if (realised == null && baseWord != null)
+ {
+ realised = baseWord.getFeatureAsString(LexicalFeature.SUPERLATIVE);
+ }
+ if (realised == null)
+ {
+ realised = buildRegularSuperlative(baseForm);
+ }
+ }
+ else
+ {
+ realised = baseForm;
+ }
+ var realisedElement = new StringElement(realised);
+ realisedElement.setFeature(InternalFeature.DISCOURSE_FUNCTION.ToString(),
+ element.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString()));
+ return realisedElement;
+ }
+
+ /**
+ * This method performs the morphology for pronouns.
+ *
+ * @param element
+ * the InflectedWordElement
.
+ * @return a StringElement
representing the word after
+ * inflection.
+ */
+
+ public static INLGElement doPronounMorphology(InflectedWordElement element)
+ {
+ string realised = null;
+
+ if (!element.getFeatureAsBoolean(InternalFeature.NON_MORPH.ToString()) && !isWHPronoun(element))
+ {
+ var genderValue = element.getFeature(LexicalFeature.GENDER);
+ var personValue = element.getFeature(Feature.PERSON.ToString());
+ var discourseValue = element.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString());
+
+ var numberIndex = element.isPlural() ? 1 : 0;
+ var genderIndex = (genderValue is Gender) ? ((Gender) genderValue).ordinal() : 2;
+
+ var personIndex = (personValue is Person) ? ((Person) personValue).ordinal() : 2;
+
+ if (personIndex == 2)
+ {
+ personIndex += genderIndex;
+ }
+
+ var positionIndex = 0;
+
+ if (element.getFeatureAsBoolean(LexicalFeature.REFLEXIVE))
+ {
+ positionIndex = 2;
+ }
+ else if (element.getFeatureAsBoolean(Feature.POSSESSIVE.ToString()))
+ {
+ positionIndex = 3;
+ if (DiscourseFunction.SPECIFIER.Equals(discourseValue))
+ {
+ positionIndex++;
+ }
+ }
+ else
+ {
+ positionIndex = (DiscourseFunction.SUBJECT.Equals(discourseValue) && !element.getFeatureAsBoolean(
+ Feature.PASSIVE.ToString())) ||
+ (DiscourseFunction.OBJECT.Equals(discourseValue) &&
+ element.getFeatureAsBoolean(Feature.PASSIVE.ToString()))
+ || DiscourseFunction.SPECIFIER.Equals(discourseValue) || (
+ DiscourseFunction.COMPLEMENT.Equals(discourseValue)
+ && element.getFeatureAsBoolean(Feature.PASSIVE.ToString()))
+ ? 0
+ : 1;
+ }
+ realised = PRONOUNS[numberIndex][positionIndex][personIndex];
+ }
+ else
+ {
+ realised = element.getBaseForm();
+ }
+ var realisedElement = new StringElement(realised);
+ realisedElement.setFeature(InternalFeature.DISCOURSE_FUNCTION.ToString(),
+ element.getFeature(InternalFeature.DISCOURSE_FUNCTION.ToString()));
+
+ return realisedElement;
+ }
+
+ private static bool isWHPronoun(InflectedWordElement word)
+ {
+ var bases = word.getBaseForm();
+ var wh = false;
+
+ if (bases != null)
+ {
+ for (var i = 0; i < WH_PRONOUNS.Length && !wh; i++)
+ {
+ wh = WH_PRONOUNS[i].Equals(bases);
+ }
+ }
+
+ return wh;
+
+ }
+
+ /**
+ * This method performs the morphology for determiners.
+ *
+ * @param determiner
+ * the InflectedWordElement
.
+ * @param realisation
+ * the current realisation of the determiner.
+ */
+
+ public static void doDeterminerMorphology(INLGElement determiner, string realisation)
+ {
+
+ if (realisation != null)
+ {
+
+ if (!(determiner.getRealisation().Equals("a")))
+ {
+ if (determiner.isPlural())
+ {
+ // Use default inflection rules:
+ if ("that".Equals(determiner.getRealisation()))
+ {
+ determiner.setRealisation("those");
+ }
+ else if ("this".Equals(determiner.getRealisation()))
+ {
+ determiner.setRealisation("these");
+ }
+ }
+ else if (!determiner.isPlural())
+ {
+ // Use default push back to base form rules:
+ if ("those".Equals(determiner.getRealisation()))
+ {
+ determiner.setRealisation("that");
+ }
+ else if ("these".Equals(determiner.getRealisation()))
+ {
+ determiner.setRealisation("this");
+ }
+
+ }
+ }
+
+ // Special "a" determiner and perform a/an agreement:
+ if (determiner.getRealisation().Equals("a"))
+ {
+
+ if (determiner.isPlural())
+ {
+ determiner.setRealisation("some");
+ }
+ else if (DeterminerAgrHelper.requiresAn(realisation))
+ {
+ determiner.setRealisation("an");
+ }
+ }
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpSimpleNLGTests/syntax/ClauseAggregationTest.cs b/SharpSimpleNLGTests/syntax/ClauseAggregationTest.cs
index 14e87ff..98e8388 100644
--- a/SharpSimpleNLGTests/syntax/ClauseAggregationTest.cs
+++ b/SharpSimpleNLGTests/syntax/ClauseAggregationTest.cs
@@ -42,7 +42,7 @@ public class ClauseAggregationTest : SimpleNLG4TestBase
{
// set up a few more fixtures
/** The s4. */
- public SPhraseSpec s1, s2, s3, s4, s5, s6;
+ public SPhraseSpec s5, s6;
public Aggregator aggregator = new Aggregator();
public ClauseCoordinationRule coord = new ClauseCoordinationRule();
public ForwardConjunctionReductionRule fcr = new ForwardConjunctionReductionRule();
@@ -56,7 +56,7 @@ public class ClauseAggregationTest : SimpleNLG4TestBase
*/
[SetUp]
- protected void setUp()
+ protected override void setUp()
{
aggregator.initialise();
diff --git a/SharpSimpleNLGTests/syntax/DocumentElementTest.cs b/SharpSimpleNLGTests/syntax/DocumentElementTest.cs
index 03e2945..6d526f5 100644
--- a/SharpSimpleNLGTests/syntax/DocumentElementTest.cs
+++ b/SharpSimpleNLGTests/syntax/DocumentElementTest.cs
@@ -50,7 +50,7 @@ public class DocumentElementTest : SimpleNLG4TestBase
*/
[SetUp]
- protected void setUp()
+ protected override void setUp()
{
p1 = this.phraseFactory.createClause("you", "be", "happy");
p2 = this.phraseFactory.createClause("I", "be", "sad");
diff --git a/SharpSimpleNLGTests/syntax/InterrogativeTest.cs b/SharpSimpleNLGTests/syntax/InterrogativeTest.cs
index 1fcdeb4..38190b2 100644
--- a/SharpSimpleNLGTests/syntax/InterrogativeTest.cs
+++ b/SharpSimpleNLGTests/syntax/InterrogativeTest.cs
@@ -40,11 +40,10 @@ public class InterrogativeTest : SimpleNLG4TestBase
// set up a few more fixtures
/** The s5. */
- SPhraseSpec s1, s2, s3, s4;
[SetUp]
- protected void setUp()
+ protected override void setUp()
{
// // the man gives the woman John's flower
diff --git a/SharpSimpleNLGTests/syntax/OrthographyFormatTest.cs b/SharpSimpleNLGTests/syntax/OrthographyFormatTest.cs
index 1b2a8cb..29df4c5 100644
--- a/SharpSimpleNLGTests/syntax/OrthographyFormatTest.cs
+++ b/SharpSimpleNLGTests/syntax/OrthographyFormatTest.cs
@@ -42,7 +42,7 @@ public class OrthographyFormatTest : SimpleNLG4TestBase
public string list2Realisation;
[SetUp]
- public void setUp()
+ public new void setUp()
{
list2Realisation = new StringBuilder("* in the room").append("\n* ").append(list1Realisation).append("\n").ToString();
diff --git a/SharpSimpleNLGTests/syntax/SimpleNLG4Test.cs b/SharpSimpleNLGTests/syntax/SimpleNLG4Test.cs
index f6cdee7..99f8453 100644
--- a/SharpSimpleNLGTests/syntax/SimpleNLG4Test.cs
+++ b/SharpSimpleNLGTests/syntax/SimpleNLG4Test.cs
@@ -68,7 +68,7 @@ public class SimpleNLG4TestBase
*/
[SetUp]
- protected void setUp()
+ protected virtual void setUp()
{
this.lexicon = Lexicon.getDefaultLexicon();
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..9e7d7a9
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,15 @@
+version: 1.0.{build}
+image: Visual Studio 2017
+before_build:
+- cmd: >-
+ dotnet restore
+
+ nuget restore
+build:
+ publish_nuget: true
+ publish_nuget_symbols: true
+ include_nuget_references: true
+ verbosity: minimal
+before_package:
+- cmd: nuget spec
+test: off
\ No newline at end of file