public override string Evaluate(RequestProcess process) { process.Log(LogLevel.Info, $"In element <test>: running test {this.Name}"); string text = this.Children?.Evaluate(process) ?? ""; process.Log(LogLevel.Diagnostic, "In element <test>: processing text '" + text + "'."); var newRequest = new Aiml.Request(text, process.User, process.Bot); text = process.Bot.ProcessRequest(newRequest, false, false, process.RecursionDepth + 1, out var duration).ToString().Trim(); process.Log(LogLevel.Diagnostic, "In element <test>: the request returned '" + text + "'."); if (process.testResults != null) { var expectedResponse = this.ExpectedResponse.Evaluate(process).Trim(); TestResult result; if (process.Bot.Config.CaseSensitiveStringComparer.Equals(text, expectedResponse)) { result = TestResult.Pass(duration); } else { result = TestResult.Failure($"Expected response: {expectedResponse}\nActual response: {text}", duration); } process.testResults[this.Name] = result; } else { process.Log(LogLevel.Warning, "In element <test>: Tests are not being used."); } return(text); }
public override string Evaluate(RequestProcess process) { StringBuilder builder = new StringBuilder(); li item; int loops = 0; do { ++loops; if (loops > process.Bot.Config.LoopLimit) { process.Log(LogLevel.Warning, "Loop limit exceeded. User: "******"; path: \"" + process.Path + "\""); throw new LoopLimitException(); } item = this.Pick(process); if (item == null) { return(string.Empty); } builder.Append(item.Children?.Evaluate(process)); } while (item.Children != null && item.Children.Loop); return(builder.ToString()); }
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); }
public override string Evaluate(RequestProcess process) { // Does the triple already exist? var clause = new Clause(this.Subject, this.Predicate, this.Object, true); clause.Evaluate(process); if (string.IsNullOrWhiteSpace(clause.subj) || string.IsNullOrWhiteSpace(clause.pred) || string.IsNullOrWhiteSpace(clause.obj)) { process.Log(LogLevel.Diagnostic, $"In element <addtriple>: Could not add triple with missing elements. Subject: {clause.subj} Predicate: {clause.pred} Object: {clause.obj}"); return(process.Bot.Config.DefaultTriple); } var triples = process.Bot.Triples.Match(clause); if (triples.Count != 0) { process.Log(LogLevel.Diagnostic, $"In element <addtriple>: Triple already exists at key {triples.First()}. Subject: {clause.subj} Predicate: {clause.pred} Object: {clause.obj}"); return(triples.First().ToString()); } // Add the triple. int key = process.Bot.Triples.Add(clause.subj, clause.pred, clause.obj); process.Log(LogLevel.Diagnostic, $"In element <addtriple>: Added a new triple with key {key}. Subject: {clause.subj} Predicate: {clause.pred} Object: {clause.obj}"); return(key.ToString()); }
public override string Evaluate(RequestProcess process) { try { if (process.Bot.SraixServices.TryGetValue(this.ServiceName, out var service)) { var text = this.Children?.Evaluate(process) ?? ""; process.Log(LogLevel.Diagnostic, "In element <sraix>: querying service '" + this.ServiceName + "' to process text '" + text + "'."); text = service.Process(text, this.Attributes, process); process.Log(LogLevel.Diagnostic, "In element <sraix>: the request returned '" + text + "'."); return(text); } else { process.User.Predicates["SraixException"] = nameof(KeyNotFoundException); process.User.Predicates["SraixExceptionMessage"] = "No service named '" + this.ServiceName + "' is known."; process.Log(LogLevel.Warning, "In element <sraix>: no service named '" + this.ServiceName + "' is known."); return((this.DefaultReply ?? new TemplateElementCollection(new Srai(new TemplateElementCollection("SRAIXFAILED")))).Evaluate(process)); } } catch (Exception ex) { process.User.Predicates["SraixException"] = ex.GetType().Name; process.User.Predicates["SraixExceptionMessage"] = ex.Message; process.Log(LogLevel.Warning, "In element <sraix>: service '" + this.ServiceName + "' threw " + ex.GetType().Name + ":\n" + ex.ToString()); return((this.DefaultReply ?? new TemplateElementCollection(new Srai(new TemplateElementCollection("SRAIXFAILED")))).Evaluate(process)); } }
public override string Evaluate(RequestProcess process) { var message = this.Children?.Evaluate(process) ?? ""; process.Bot.WriteGossip(process, message); return(message); }
public override string Evaluate(RequestProcess process) { string indices = null; int responseIndex = 1; int sentenceIndex = 1; if (this.Index != null) { indices = this.Index.Evaluate(process); } if (!string.IsNullOrWhiteSpace(indices)) { // Parse the index attribute. string[] fields = indices.Split(','); if (fields.Length > 2) { throw new ArgumentException("index attribute of a that tag evaluated to an invalid value (" + indices + ")."); } responseIndex = int.Parse(fields[0].Trim()); if (fields.Length == 2) { sentenceIndex = int.Parse(fields[1].Trim()); } } return(process.User.GetThat(responseIndex, sentenceIndex)); }
public override string Evaluate(RequestProcess process) { // Evaluate the contents of clauses. foreach (var clause in this.Clauses) { clause.Evaluate(process); } string[] visibleVars; if (this.Variables == null) { visibleVars = new string[0]; } else { visibleVars = this.Variables.Evaluate(process).Split((char[])null, StringSplitOptions.RemoveEmptyEntries); } // Start with an empty tuple. Tuple tuple = new Tuple(new HashSet <string>(visibleVars, process.Bot.Config.StringComparer)); var tuples = this.SelectFromRemainingClauses(process, tuple, 0); process.Log(LogLevel.Diagnostic, $"In element <select>: Found {tuples.Count} matching {(tuples.Count == 1 ? "tuple" : "tuples")}."); if (tuples.Count == 0) { return(process.Bot.Config.DefaultTriple); } return(string.Join(" ", tuples.Select(t => t.Index))); }
public override string Evaluate(RequestProcess process) { var value = new StringBuilder(this.Children?.Evaluate(process) ?? ""); bool firstLetter = true; for (int i = 0; i < value.Length; ++i) { if (char.IsWhiteSpace(value[i])) { firstLetter = true; } else { if (firstLetter) { if (char.IsLower(value[i])) { value[i] = char.ToUpper(value[i]); } firstLetter = false; } else { if (char.IsUpper(value[i])) { value[i] = char.ToLower(value[i]); } } } } return(value.ToString()); }
public override string Evaluate(RequestProcess process) { string value = this.TupleKey?.Evaluate(process); if (!string.IsNullOrWhiteSpace(value)) { // Get a value from a tuple. int index; Tuple tuple; if (int.TryParse(value, out index) && index >= 0 && index < Tuple.Tuples.Count) { tuple = Tuple.Tuples[index]; if (tuple.TryGetValue(this.Key.Evaluate(process), out value)) { return(value); } } return(process.Bot.Config.DefaultPredicate); } // Get a user variable or local variable. if (this.LocalVar) { return(process.GetVariable(this.Key.Evaluate(process))); } return(process.User.GetPredicate(this.Key.Evaluate(process))); }
public override string Evaluate(RequestProcess process) { var value = new StringBuilder(this.Children?.Evaluate(process) ?? ""); int i; for (i = 0; i < value.Length; ++i) { if (char.IsLetterOrDigit(value[i])) { if (char.IsLower(value[i])) { value[i] = char.ToUpper(value[i]); } break; } } for (++i; i < value.Length; ++i) { if (char.IsUpper(value[i])) { value[i] = char.ToLower(value[i]); } } return(value.ToString()); }
/// <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); }
public override string Evaluate(RequestProcess process) { var clause = new Clause(this.Subject, this.Predicate, this.Object, true); clause.Evaluate(process); if (string.IsNullOrWhiteSpace(clause.subj) || string.IsNullOrWhiteSpace(clause.pred) || string.IsNullOrWhiteSpace(clause.obj)) { process.Log(LogLevel.Diagnostic, $"In element <deletetriple>: Could not delete triple with missing elements. Subject: {clause.subj} Predicate: {clause.pred} Object: {clause.obj}"); return(process.Bot.Config.DefaultTriple); } var triples = process.Bot.Triples.Match(clause); if (triples.Count == 0) { process.Log(LogLevel.Diagnostic, $"In element <deletetriple>: No such triple exists. Subject: {clause.subj} Predicate: {clause.pred} Object: {clause.obj}"); return(process.Bot.Config.DefaultTriple); } var index = triples.Single(); process.Bot.Triples.Remove(index); process.Log(LogLevel.Diagnostic, $"In element <deletetriple>: Deleted the triple with key {index}. Subject: {clause.subj} Predicate: {clause.pred} Object: {clause.obj}"); return(index.ToString()); }
public string EvaluateChildren(RequestProcess process) { if (this.Children == null) { return(""); } return(this.Children.Evaluate(process)); }
public override string Evaluate(RequestProcess process) { if (process.Bot.Maps.TryGetValue(this.Name.Evaluate(process), out var map)) { return(map[this.Children?.Evaluate(process) ?? ""] ?? process.Bot.Config.DefaultMap); } return(process.Bot.Config.DefaultMap); }
internal void WriteGossip(RequestProcess process, string message) { var e = new GossipEventArgs(message); this.OnGossip(e); if (e.Handled) { return; } process.Log(LogLevel.Gossip, "Gossip from " + process.User.ID + ": " + message); }
public override string Evaluate(RequestProcess process) { string text = process.star[0]; process.Log(LogLevel.Diagnostic, "In element <sr>: processing text '" + text + "'."); var newRequest = new Aiml.Request(text, process.User, process.Bot); text = process.Bot.ProcessRequest(newRequest, false, false, process.RecursionDepth + 1, out _).ToString(); process.Log(LogLevel.Diagnostic, "In element <sr>: the request returned '" + text + "'."); return(text); }
public override string Evaluate(RequestProcess process) { int index = int.Parse(this.Index.Evaluate(process)); if (process.topicstar.Count < index) { return(process.Bot.Config.DefaultWildcard); } var match = process.topicstar[index - 1]; return(match == "" ? process.Bot.Config.DefaultWildcard : match); }
public override string Evaluate(RequestProcess process) { string?indexText = null; int index = 1; indexText = this.Index?.Evaluate(process); if (!string.IsNullOrWhiteSpace(indexText)) { index = int.Parse(indexText); } return(process.User.GetInput(index)); }
public override string Evaluate(RequestProcess process) { // Evaluate <eval> tags. XmlNode node = this.Node.Clone(); this.ProcessXml(node, process); // Learn the result. process.Log(LogLevel.Diagnostic, $"In element <learn>: learning new category for {process.User.ID}: {node.OuterXml}"); AimlLoader loader = new AimlLoader(process.Bot); loader.ProcessCategory(process.User.Graphmaster, node, null); return(string.Empty); }
public override string Evaluate(RequestProcess process) { string sentence = (this.Children?.Evaluate(process) ?? "").Trim(); if (sentence == "") { return(process.Bot.Config.DefaultListItem); } int delimiter = sentence.IndexOf(' '); if (delimiter == -1) { return(process.Bot.Config.DefaultListItem); } return(sentence.Substring(delimiter + 1).TrimStart()); }
public override string Evaluate(RequestProcess process) { StringBuilder builder = new StringBuilder(); li item; do { item = this.Pick(); if (builder.Length != 0) { builder.Append(" "); } builder.Append(item.Evaluate(process)); } while (item.Children.Loop); return(builder.ToString()); }
public override string Evaluate(RequestProcess process) { string command = this.Command.Evaluate(process); Process process2 = new Process(); if (Environment.OSVersion.Platform < PlatformID.Unix) { // Windows process2.StartInfo = new ProcessStartInfo(Path.Combine(Environment.SystemDirectory, "cmd.exe"), "/Q /D /C \"" + Regex.Replace(command, @"[/\\:*?""<>^]", "^$0") + "\""); // /C string Carries out the command specified by string and then terminates. // /Q Turns echo off. // /D Disable execution of AutoRun commands from registry (see 'CMD /?'). } else if (Environment.OSVersion.Platform == PlatformID.Unix) { // UNIX process2.StartInfo = new ProcessStartInfo(Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "bin", "sh"), command.Replace(@"\", @"\\").Replace("\"", "\\\"")); } process2.StartInfo.UseShellExecute = false; process2.StartInfo.RedirectStandardOutput = true; process2.StartInfo.RedirectStandardError = true; process.Log(LogLevel.Diagnostic, $"In element <system>: executing {process2.StartInfo.FileName} {process2.StartInfo.Arguments}"); process2.Start(); string output = process2.StandardOutput.ReadToEnd(); string output2 = process2.StandardError.ReadToEnd(); process2.WaitForExit((int)process.Bot.Config.Timeout); if (!process2.HasExited) { process.Log(LogLevel.Diagnostic, $"In element <system>: the process timed out."); } else if (process2.ExitCode != 0) { process.Log(LogLevel.Diagnostic, $"In element <system>: the process exited with code {process2.ExitCode}."); } return(output); }
private void ProcessXml(XmlNode node, RequestProcess process) { for (int i = 0; i < node.ChildNodes.Count; ++i) { XmlNode node2 = node.ChildNodes[i]; if (node2.NodeType == XmlNodeType.Element) { if (node2.Name.Equals("eval", StringComparison.InvariantCultureIgnoreCase)) { TemplateElementCollection tags = TemplateElementCollection.FromXml(node2, process.Bot.AimlLoader); node2.ParentNode.ReplaceChild(node.OwnerDocument.CreateTextNode(tags.Evaluate(process)), node2); } else { this.ProcessXml(node2, process); } } } }
public override string Evaluate(RequestProcess process) { string key = this.Key.Evaluate(process); string value = (this.Children?.Evaluate(process) ?? "").Trim(); var dictionary = this.LocalVar ? process.Variables : process.User.Predicates; if (process.Bot.Config.UnbindPredicatesWithDefaultValue && value == (this.LocalVar ? process.Bot.Config.DefaultPredicate : process.Bot.Config.GetDefaultPredicate(key))) { dictionary.Remove(key); process.Log(LogLevel.Diagnostic, "In element <set>: Unbound " + (this.LocalVar ? "local variable" : "predicate") + " '" + key + "' with default value '" + value + "'."); } else { dictionary[key] = value; process.Log(LogLevel.Diagnostic, "In element <set>: Set " + (this.LocalVar ? "local variable" : "predicate") + " '" + key + "' to '" + value + "'."); } return(value); }
/// <summary>Evaluates the contained tags and returns the result.</summary> /// <param name="subRequest">The sub-request for which this tag collection is being evaluated.</param> /// <param name="response">The response being built.</param> /// <param name="thinking">Indicates whether we are inside a <code>think</code> template element.</param> /// <returns>The concatenated results of evaluating all the contained tags.</returns> public string Evaluate(RequestProcess process) { if (this.tags == null || this.tags.Length == 0) { return(string.Empty); } StringBuilder builder = new StringBuilder(); foreach (TemplateNode tag in this.tags) { var output = tag.Evaluate(process); // Condense consecutive spaces. if (builder.Length > 0 && char.IsWhiteSpace(builder[builder.Length - 1])) { output = output.TrimStart(); } builder.Append(output); } return(builder.ToString()); }
public override string Evaluate(RequestProcess process) { // Evaluate <eval> tags. XmlNode node = this.Node.Clone(); this.ProcessXml(node, process); // Learn the result. process.Log(LogLevel.Diagnostic, "In element <learnf>: learning new category: " + node.OuterXml); AimlLoader loader = new AimlLoader(process.Bot); loader.ProcessCategory(process.Bot.Graphmaster, node, null); // Write it to a file. bool newFile = !File.Exists(process.Bot.Config.LearnfFile) || new FileInfo(process.Bot.Config.LearnfFile).Length < 7; StreamWriter writer = new StreamWriter(File.Open("learnf.aiml", FileMode.OpenOrCreate, FileAccess.Write)); if (newFile) { writer.WriteLine("<!-- This file contains AIML categories the bot has learned via <learnf> elements. -->"); writer.WriteLine(); writer.WriteLine("<aiml version=\"2.0\">"); writer.WriteLine(); } else { // Seek to just before the closing </aiml> tag. writer.BaseStream.Seek(-7, SeekOrigin.End); } writer.WriteLine("<!-- Learned from " + process.User.ID + " via category '" + process.Path + "' on " + DateTime.Now + ". -->"); writer.Write(node.InnerXml.Trim('\r', '\n')); writer.WriteLine(); writer.WriteLine(); writer.Write("</aiml>"); writer.Close(); return(string.Empty); }
public override string Evaluate(RequestProcess process) { this.Clause.Evaluate(process); // Find triples that match. var triples = process.Bot.Triples.Match(this.Clause); if (triples.Count == 0) { process.Log(LogLevel.Diagnostic, $"In element <uniq>: No matching triple exists. Subject: {this.Clause.subj} Predicate: {this.Clause.pred} Object: {this.Clause.obj}"); return(process.Bot.Config.DefaultTriple); } else if (triples.Count > 1) { process.Log(LogLevel.Diagnostic, $"In element <uniq>: Found {triples.Count} matching triples. Subject: {this.Clause.subj} Predicate: {this.Clause.pred} Object: {this.Clause.obj}"); } var tripleIndex = triples.First(); var triple = process.Bot.Triples[tripleIndex]; process.Log(LogLevel.Diagnostic, $"In element <uniq>: Found triple {tripleIndex}. Subject: {triple.Subject} Predicate: {triple.Predicate} Object: {triple.Object}"); // Get the result. if (this.Clause.obj.StartsWith("?")) { return(triple.Object); } if (this.Clause.pred.StartsWith("?")) { return(triple.Predicate); } if (this.Clause.subj.StartsWith("?")) { return(triple.Subject); } process.Log(LogLevel.Warning, $"In element <uniq>: The clause contains no variables. Subject: {this.Clause.subj} Predicate: {this.Clause.pred} Object: {this.Clause.obj}"); return(process.Bot.Config.DefaultTriple); }
public override string Evaluate(RequestProcess process) { return(process.User.ID); }
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); }