// Interaction Interface: Return whether or not a given sentence matches given pattern: If a match is found a PatternInstance will be returned other wise null // Notice that patterns provide EXACT matches, this gives designers flexibility in defining general or specific matches per need/context; For subpatterns the length cannot be decided though so an option if given // <Debug> Pending thorough logic check // @sentence: input sentence must be well formed: no auxiliary blanks, no punctuation (only words are allowed for now). public PatternInstance IsMatchPattern(string sentence, Pattern pattern, bool bExactMatch = true) { // Prepare return value PatternInstance instance = new PatternInstance(); instance.Type = pattern; // Split sentence into words string[] words = sentence.Split(new char[] { ' ' }); // <Debug><Improvement> Current we are not dealing with sentences that might contain commas (and we should deal with it here), // or multiple sentences types in one piece (and this one should be handled and seperated by higher level input engine because a pattern is designed to handle one setence only (with commas)). int currentWord = 0; // ID of the word we are matching against // Stage 1: Iterate through pattern elements: if all elements match then it is likely a good match foreach (PatternElement element in pattern.Elements) { // Make sure we still have more words to match against, otherwise input string is too short for this pattern if (words.Length <= currentWord) { return(null); } // Predefine so later it won't cause local variable name conflict Phrase phrase; bool result; PatternInstance temp; WordAttribute attribute; string tempString; int overlap; // Match element type and vlaue switch (element.Type) { case PatternElementType.SpecificWord: // Prepare two strings tempString = words[currentWord]; for (int i = currentWord + 1; i < words.Length; i++) { tempString = tempString + ' ' + words[i]; } overlap = GetStringOverlapLowerCase(tempString, element.Key); // If the following words don't match and it's not optional then this pattern doesn't match if (overlap == -1 && element.bOptional == false) { return(null); } else { // Generate Pattern Element Instance and Proceed to next word instance.ComponentElements.Add(new PatternElementInstance(PatternElementType.SpecificWord, tempString.Substring(0, overlap))); currentWord += GetNumOfWords(element.Key); } break; case PatternElementType.VarietyWord: result = Vocabulary.IsWordVaryingFormOrSynonym(words[currentWord], element.Key); if (result == false && element.bOptional == false) { return(null); } else { // Generate Pattern Element Instance and Proceed to next word instance.ComponentElements.Add(new PatternElementInstance(PatternElementType.VarietyWord, words[currentWord])); currentWord++; } break; case PatternElementType.WordAttribute: // <Debug> This is currently incomplete implementation, i.e. + for multiple constriants (e.g. for verbs) attribute = (WordAttribute)Enum.Parse(typeof(WordAttribute), element.Key); // foundWord = Vocabulary.ProbeWord(words[currentWord], attribute); // Notice for WordAttribute we are not matching against word, but phrase tempString = words[currentWord]; for (int i = currentWord + 1; i < words.Length; i++) { tempString = tempString + ' ' + words[i]; } phrase = Vocabulary.GetPhrase(tempString, attribute); if (phrase == null && element.bOptional == false) { return(null); } else { // Generate Pattern Element Instance and Proceed to next word instance.ComponentElements.Add(new PatternElementInstance(PatternElementType.WordAttribute, phrase.Key)); currentWord += phrase.WordCount; } break; case PatternElementType.SubPattern: tempString = words[currentWord]; for (int i = currentWord + 1; i < words.Length; i++) { tempString = tempString + ' ' + words[i]; } temp = IsMatchPattern(tempString, element.SubPattern, false); // Do not do an exact match in this case if (temp == null && element.bOptional == false) { return(null); } else { // Generate Pattern Element Instance and Proceed to next word instance.ComponentElements.Add(new PatternElementInstance(PatternElementType.SubPattern, temp)); currentWord += temp.WordCount; } break; case PatternElementType.Choice: // Return the first matched selection bool bLoopBreak = false; foreach (PatternElement choiceElement in element.Choices) { // break loop, not just the switch if (bLoopBreak) { break; } // Match CHOICE element type and vlaue // Notice that CHOICE elements are always optional, but at least one must be selected switch (choiceElement.Type) { case PatternElementType.SpecificWord: // Prepare two strings tempString = words[currentWord]; for (int i = currentWord + 1; i < words.Length; i++) { tempString = tempString + ' ' + words[i]; } overlap = GetStringOverlapLowerCase(tempString, choiceElement.Key); // If next word doesn't match then continue; If match then no more loop if (overlap == -1) { continue; } else { // Generate Pattern Element Instance and Proceed to next word instance.ComponentElements.Add(new PatternElementInstance(PatternElementType.SpecificWord, tempString.Substring(0, overlap))); currentWord += GetNumOfWords(choiceElement.Key); bLoopBreak = true; } break; case PatternElementType.VarietyWord: // If word doesn't match then continue; If match then no more loop result = Vocabulary.IsWordVaryingFormOrSynonym(words[currentWord], choiceElement.Key); if (result == false) { continue; } else { // Generate Pattern Element Instance and Proceed to next word instance.ComponentElements.Add(new PatternElementInstance(PatternElementType.VarietyWord, words[currentWord])); currentWord++; bLoopBreak = true; } break; case PatternElementType.WordAttribute: attribute = (WordAttribute)Enum.Parse(typeof(WordAttribute), choiceElement.Key); // foundWord = Vocabulary.ProbeWord(words[currentWord], attribute); // Notice for WordAttribute we are not matching against word, but phrase tempString = words[currentWord]; for (int i = currentWord + 1; i < words.Length; i++) { tempString = tempString + ' ' + words[i]; } phrase = Vocabulary.GetPhrase(tempString, attribute); // If trailing string doesn't match then continue; If match then no more loop if (phrase == null && choiceElement.bOptional == false) { continue; } else { // Generate Pattern Element Instance and Proceed to next word instance.ComponentElements.Add(new PatternElementInstance(PatternElementType.WordAttribute, phrase.Key)); currentWord += phrase.WordCount; bLoopBreak = true; } break; case PatternElementType.SubPattern: tempString = words[currentWord]; for (int i = currentWord + 1; i < words.Length; i++) { tempString = tempString + ' ' + words[i]; } // If trailing string doesn't match then continue; If match then no more loop temp = IsMatchPattern(tempString, choiceElement.SubPattern); if (temp == null) { continue; } else { // Generate Pattern Element Instance and Proceed to next word instance.ComponentElements.Add(new PatternElementInstance(PatternElementType.SubPattern, temp)); currentWord += temp.WordCount; bLoopBreak = true; } break; default: break; } } // If no match was found then this pattern doesn't match, otherwise continue if (bLoopBreak == false) { return(null); } break; default: break; } } // Stage 2: If we are doing an exact match then make sure there is no auxiliary words if (bExactMatch == true && words.Length != currentWord) { return(null); } return(instance); }