Beispiel #1
0
        /// <summary>Handles a wildcard node by taking words one by one until a template is found.</summary>
        private Template?WildcardSearch(RequestSentence subRequest, RequestProcess process, string[] inputPath, int inputPathIndex, bool traceSearch, MatchState matchState, int minimumWords)
        {
            int inputPathIndex2;
            var star      = process.GetStar(matchState);
            int starIndex = star.Count;

            // Reserve a space in the star list. If a template is found, this slot will be filled with the matched phrase.
            // This function can call other wildcards recursively. The reservation ensures that the star list will be populated correctly.
            star.Add("");

            for (inputPathIndex2 = inputPathIndex + minimumWords; inputPathIndex2 <= inputPath.Length; ++inputPathIndex2)
            {
                var result = this.Search(subRequest, process, inputPath, inputPathIndex2, traceSearch, matchState);
                if (result != null)
                {
                    star[starIndex] = string.Join(" ", inputPath, inputPathIndex, inputPathIndex2 - inputPathIndex);
                    return(result);
                }

                // Wildcards cannot match these tokens.
                if ((matchState == MatchState.Message && inputPath[inputPathIndex2] == "<that>") ||
                    (matchState == MatchState.That && inputPath[inputPathIndex2] == "<topic>"))
                {
                    break;
                }
            }

            // No match; remove the reserved slot.
            star.RemoveAt(starIndex);
            Debug.Assert(star.Count == starIndex);
            return(null);
        }
Beispiel #2
0
        public Template?Search(RequestSentence sentence, RequestProcess process, string that, bool traceSearch)
        {
            if (process.RecursionDepth > sentence.Bot.Config.RecursionLimit)
            {
                sentence.Bot.Log(LogLevel.Warning, "Recursion limit exceeded. User: "******"; raw input: \"" + sentence.Request.Text + "\"");
                throw new RecursionLimitException();
            }

            // Generate the input path.
            var messageSplit = sentence.Text.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
            var thatSplit    = that.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
            var topicSplit   = sentence.Bot.Normalize(sentence.User.Topic).Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);

            var inputPath = new string[messageSplit.Length + thatSplit.Length + topicSplit.Length + 2];
            int i         = 0;

            messageSplit.CopyTo(inputPath, 0);
            i += messageSplit.Length;
            inputPath[i++] = "<that>";
            thatSplit.CopyTo(inputPath, i);
            i += thatSplit.Length;
            inputPath[i++] = "<topic>";
            topicSplit.CopyTo(inputPath, i);
            if (traceSearch)
            {
                process.Log(LogLevel.Diagnostic, "Normalized path: " + string.Join(" ", inputPath));
            }

            var result = this.Search(sentence, process, inputPath, 0, traceSearch, MatchState.Message);

            return(result);
        }
Beispiel #3
0
 public RequestProcess(RequestSentence sentence, int recursionDepth, bool useTests)
 {
     this.Sentence       = sentence;
     this.RecursionDepth = recursionDepth;
     this.Variables      = new Dictionary <string, string>(sentence.Bot.Config.StringComparer);
     if (useTests)
     {
         this.testResults = new Dictionary <string, TestResult>(sentence.Bot.Config.StringComparer);
         this.TestResults = new ReadOnlyDictionary <string, TestResult>(this.testResults);
     }
 }
Beispiel #4
0
        private Template?Search(RequestSentence sentence, RequestProcess process, string[] inputPath, int inputPathIndex, bool traceSearch, MatchState matchState)
        {
            if (traceSearch)
            {
                sentence.Bot.Log(LogLevel.Diagnostic, "Search: " + process.Path);
            }

            int pathDepth = process.patternPathTokens.Count;

            if (process.CheckTimeout())
            {
                sentence.Bot.Log(LogLevel.Warning, "Request timeout. User: "******"; raw input: \"" + sentence.Request.Text + "\"");
                throw new TimeoutException();
            }

            bool tokensRemaining;

            if (inputPathIndex >= inputPath.Length)
            {
                // No tokens remaining in the input path. If this node has a template, return success.
                if (this.Template != null)
                {
                    return(this.Template);
                }
                // Otherwise, look for zero+ wildcards.
                tokensRemaining = false;
            }
            else
            {
                tokensRemaining = true;

                switch (matchState)
                {
                case MatchState.Message:
                    if (inputPath[inputPathIndex] == "<that>")
                    {
                        matchState = MatchState.That;
                    }
                    break;

                case MatchState.That:
                    if (inputPath[inputPathIndex] == "<topic>")
                    {
                        matchState = MatchState.Topic;
                    }
                    break;
                }
            }

            // Reserve a space in the pattern path list here. This is so that further recursive calls will leave it alone.
            // If we find a template, we replace the empty string with the correct token.
            process.patternPathTokens.Add("?");

            //var star = matchState == MatchState.That ? subRequest.thatstar :
            //	matchState == MatchState.Topic ? subRequest.topicstar :
            //	subRequest.star;

            // Search for child nodes that match the input in priority order.

            // Priority exact match.
            if (tokensRemaining && this.children.TryGetValue("$" + inputPath[inputPathIndex], out var node))
            {
                process.patternPathTokens[pathDepth] = "$" + inputPath[inputPathIndex];
                var result = node.Search(sentence, process, inputPath, inputPathIndex + 1, traceSearch, matchState);
                if (result != null)
                {
                    return(result);
                }
            }

            // Priority zero+ wildcard.
            if (this.children.TryGetValue("#", out node))
            {
                process.patternPathTokens[pathDepth] = "#";
                var result = node.WildcardSearch(sentence, process, inputPath, inputPathIndex, traceSearch, matchState, 0);
                if (result != null)
                {
                    return(result);
                }
            }

            // Priority one+ wildcard.
            if (this.children.TryGetValue("_", out node))
            {
                process.patternPathTokens[pathDepth] = "_";
                var result = node.WildcardSearch(sentence, process, inputPath, inputPathIndex, traceSearch, matchState, 1);
                if (result != null)
                {
                    return(result);
                }
            }

            // Exact match.
            if (tokensRemaining && this.children.TryGetValue(inputPath[inputPathIndex], out node))
            {
                process.patternPathTokens[pathDepth] = inputPath[inputPathIndex];
                var result = node.Search(sentence, process, inputPath, inputPathIndex + 1, traceSearch, matchState);
                if (result != null)
                {
                    return(result);
                }
            }

            // Sets. (The empty string cannot be matched by a set token.)
            if (tokensRemaining)
            {
                foreach (var child in this.setChildren)
                {
                    process.patternPathTokens[pathDepth] = $"<set>{child.SetName}</set>";
                    if (sentence.Bot.Sets.TryGetValue(child.SetName, out var set))
                    {
                        var star      = process.GetStar(matchState);
                        int starIndex = star.Count;
                        star.Add("");                          // Reserving a space; see above.

                        // Similarly to a wildcard search, we take words one by one until either a template is found, or no words remain.
                        // This time, each time we take a word, we must check that the phrase is in the set.
                        var phrase = new StringBuilder(); int wordCount = 0;
                        for (int inputPathIndex2 = inputPathIndex; inputPathIndex2 < inputPath.Length; ++inputPathIndex2)
                        {
                            if ((matchState == MatchState.Message && inputPath[inputPathIndex2] == "<that>") ||
                                (matchState == MatchState.That && inputPath[inputPathIndex2] == "<topic>"))
                            {
                                break;
                            }

                            if (phrase.Length > 0)
                            {
                                phrase.Append(' ');
                            }
                            phrase.Append(inputPath[inputPathIndex2]);
                            ++wordCount;

                            if (set.Contains(phrase.ToString()))
                            {
                                // Phrase found in the set. Now continue with the tree search.
                                var result = child.Node.Search(sentence, process, inputPath, inputPathIndex + wordCount, traceSearch, matchState);
                                if (result != null)
                                {
                                    star[starIndex] = phrase.ToString();
                                    return(result);
                                }
                            }

                            // Each set keeps track of the greatest number of words any element in the set has.
                            // After reaching that number, we can stop searching.
                            if (wordCount >= set.MaxWords)
                            {
                                break;
                            }
                        }

                        // No match; release the reserved space.
                        star.RemoveAt(starIndex);
                        Debug.Assert(star.Count == starIndex);
                    }
                    else
                    {
                        sentence.Request.Bot.Log(LogLevel.Warning, $"Reference to a missing set in pattern path '{string.Join(" ", process.patternPathTokens)}'.");
                    }
                }
            }

            // Zero+ wildcard.
            if (this.children.TryGetValue("^", out node))
            {
                process.patternPathTokens[pathDepth] = "^";
                var result = node.WildcardSearch(sentence, process, inputPath, inputPathIndex, traceSearch, matchState, 0);
                if (result != null)
                {
                    return(result);
                }
            }

            // One+ wildcard.
            if (this.children.TryGetValue("*", out node))
            {
                process.patternPathTokens[pathDepth] = "*";
                var result = node.WildcardSearch(sentence, process, inputPath, inputPathIndex, traceSearch, matchState, 1);
                if (result != null)
                {
                    return(result);
                }
            }

            // No match.
            process.patternPathTokens.RemoveAt(pathDepth);
            Debug.Assert(process.patternPathTokens.Count == pathDepth);
            return(null);
        }