private CommandResult ExecuteLine(string line) { CommandResult next = CommandResult.Normal; if (line.StartsWith("#")) { // Comments start with "#", just ignore them } else if (line.StartsWith("@")) { // A line beginning with an "@" indicates a script to execute. string scriptFile = line.Substring(1); next = ExecuteScript(scriptFile); } else { string[] args = null; DebuggerCommand command = GetDebuggerCommandFromCommandString(line, out args); if (command == null) { // Not a command. Console.WriteLine("Invalid command."); } else { next = InvokeConsoleMethod(command, args); } } return(next); }
public void AddSubNode(List <string> words, MethodInfo method, object instance) { // We should never hit this case. if (words.Count == 0) { throw new InvalidOperationException("Out of words building command node."); } // Check the root to see if a node for the first incoming word has already been added DebuggerCommand subNode = FindSubNodeByName(words[0]); if (subNode == null) { // No, it has not -- create one and add it now. subNode = new DebuggerCommand(instance, words[0], null, null, null); this.SubCommands.Add(subNode); if (words.Count == 1) { // This is the last stop -- set the method and be done with it now. subNode.Methods.Add(method); // early return. return; } } else { // The node already exists, we will be adding a subnode, hopefully. if (words.Count == 1) { // // If we're on the last word at this point then this is an overloaded command. // Check that we don't have any other commands with this number of arguments. // int argCount = method.GetParameters().Length; foreach (MethodInfo info in subNode.Methods) { if (info.GetParameters().Length == argCount) { throw new InvalidOperationException("Duplicate overload for console command"); } } // // We're ok. Add it to the method list. // subNode.Methods.Add(method); // and return early. return; } } // We have more words to go. words.RemoveAt(0); subNode.AddSubNode(words, method, instance); }
private DebuggerCommand GetDebuggerCommandFromCommandString(string command, out string[] args) { args = null; List <string> cmdArgs = SplitArgs(command); DebuggerCommand current = _commandRoot; int commandIndex = 0; while (true) { // If this node has an executor and no subnodes, or if this node has an executor // and there are no further arguments, then we're done. if ((current.Methods.Count > 0 && current.SubCommands.Count == 0) || (current.Methods.Count > 0 && commandIndex > cmdArgs.Count - 1)) { break; } if (commandIndex > cmdArgs.Count - 1) { // Out of args with no match. return(null); } // Otherwise we continue down the tree. DebuggerCommand next = current.FindSubNodeByName(cmdArgs[commandIndex]); commandIndex++; if (next == null) { // // If a matching subcommand was not found, then if we had a previous node with an // executor, use that; otherwise the command is invalid. // if (current.Methods.Count > 0) { break; } else { return(null); } } current = next; } // Now current should point to the command with the executor // and commandIndex should point to the first argument to the command. cmdArgs.RemoveRange(0, commandIndex); args = cmdArgs.ToArray(); return(current); }
public DebuggerCommand FindSubNodeByName(string name) { DebuggerCommand found = null; foreach (DebuggerCommand sub in SubCommands) { if (sub.Name == name) { found = sub; break; } } return(found); }
/// <summary> /// Builds the debugger command tree. /// </summary> private void BuildCommandTree(List <object> commandObjects) { // Build the flat list which will be built into the tree, by walking // the classes that provide the methods _commandList = new List <DebuggerCommand>(); // Add ourself to the list commandObjects.Add(this); foreach (object commandObject in commandObjects) { Type type = commandObject.GetType(); foreach (MethodInfo info in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { object[] attribs = info.GetCustomAttributes(typeof(DebuggerFunction), true); if (attribs.Length > 1) { throw new InvalidOperationException(String.Format("More than one ConsoleFunction attribute set on {0}", info.Name)); } else if (attribs.Length == 1) { // we have a debugger attribute set on this method // this cast should always succeed given that we're filtering for this type above. DebuggerFunction function = (DebuggerFunction)attribs[0]; DebuggerCommand newCommand = new DebuggerCommand(commandObject, function.CommandName, function.Description, function.Usage, info); _commandList.Add(newCommand); } } } // Now actually build the command tree from the above list! _commandRoot = new DebuggerCommand(null, "Root", null, null, null); foreach (DebuggerCommand c in _commandList) { string[] commandWords = c.Name.Split(' '); // This is kind of ugly, we know that at this point every command built above have only // one method. When building the tree, overloaded commands may end up with more than one. _commandRoot.AddSubNode(new List <string>(commandWords), c.Methods[0], c.Instance); } }
private CommandResult InvokeConsoleMethod(DebuggerCommand command, string[] args) { MethodInfo method = null; // // Find the method that matches the arg count we were passed // (i.e. handle overloaded commands). // That this only matches on argument count is somewhat of a kluge... // foreach (MethodInfo m in command.Methods) { ParameterInfo[] paramInfo = m.GetParameters(); if (args == null && paramInfo.Length == 0 || paramInfo.Length == args.Length) { // found a match method = m; break; } } if (method == null) { // invalid argument count. throw new ArgumentException(String.Format("Invalid argument count to command.")); } ParameterInfo[] parameterInfo = method.GetParameters(); object[] invokeParams; if (args == null) { invokeParams = null; } else { invokeParams = new object[parameterInfo.Length]; } int argIndex = 0; for (int paramIndex = 0; paramIndex < parameterInfo.Length; paramIndex++) { ParameterInfo p = parameterInfo[paramIndex]; if (p.ParameterType.IsEnum) { // // This is an enumeration type. // See if we can find an enumerant that matches the argument. // FieldInfo[] fields = p.ParameterType.GetFields(); foreach (FieldInfo f in fields) { if (!f.IsSpecialName && args[argIndex].ToLower() == f.Name.ToLower()) { invokeParams[paramIndex] = f.GetRawConstantValue(); break; } } if (invokeParams[paramIndex] == null) { // no match, provide possible values StringBuilder sb = new StringBuilder(String.Format("Invalid value for parameter {0}. Possible values are:", paramIndex)); foreach (FieldInfo f in fields) { if (!f.IsSpecialName) { sb.AppendFormat("{0} ", f.Name); } } sb.AppendLine(); throw new ArgumentException(sb.ToString()); } argIndex++; } else if (p.ParameterType.IsArray) { // // If a function takes an array type, i should do something here, yeah. // argIndex++; } else { if (p.ParameterType == typeof(bool)) { invokeParams[paramIndex] = bool.Parse(args[argIndex++]); } else if (p.ParameterType == typeof(uint)) { invokeParams[paramIndex] = TryParseUint(args[argIndex++]); } else if (p.ParameterType == typeof(ushort)) { invokeParams[paramIndex] = TryParseUshort(args[argIndex++]); } else if (p.ParameterType == typeof(string)) { invokeParams[paramIndex] = args[argIndex++]; } else if (p.ParameterType == typeof(char)) { invokeParams[paramIndex] = (char)args[argIndex++][0]; } else if (p.ParameterType == typeof(float)) { invokeParams[paramIndex] = float.Parse(args[argIndex++]); } else { throw new ArgumentException(String.Format("Unhandled type for parameter {0}, type {1}", paramIndex, p.ParameterType)); } } } // // If we've made it THIS far, then we were able to parse all the commands into what they should be. // Invoke the method on the object instance associated with the command. // return((CommandResult)method.Invoke(command.Instance, invokeParams)); }
private string FuzzyMatch(DebuggerCommand root, List <string> tokens, bool silent) { if (tokens.Count == 0) { if (!silent) { // If there are no tokens, just show the completion for the root. PrintCompletions(root.SubCommands); } return(String.Empty); } DebuggerCommand match = null; // Search for exact matches. If we find one it's guaranteed to be unique // so we can follow that node. foreach (DebuggerCommand c in root.SubCommands) { if (c.Name.ToLower() == tokens[0].ToLower()) { match = c; break; } } if (match == null) { // No exact match. Try a substring match. // If we have an unambiguous match then we can complete it automatically. // If the match is ambiguous, display possible completions and return String.Empty. List <DebuggerCommand> completions = new List <DebuggerCommand>(); foreach (DebuggerCommand c in root.SubCommands) { if (c.Name.StartsWith(tokens[0], StringComparison.InvariantCultureIgnoreCase)) { completions.Add(c); } } if (completions.Count == 1) { // unambiguous match. use it. match = completions[0]; } else if (completions.Count > 1) { // ambiguous match. display possible completions. if (!silent) { PrintCompletions(completions); } } } if (match == null) { // If we reach this point then no matches are available. return the tokens we have... StringBuilder sb = new StringBuilder(); for (int i = 0; i < tokens.Count; i++) { if (i < tokens.Count - 1) { sb.AppendFormat("{0} ", tokens[i]); } else { sb.AppendFormat("{0}", tokens[i]); } } return(sb.ToString()); } else { // A match was found tokens.RemoveAt(0); string subMatch = String.Empty; if (tokens.Count > 0) { subMatch = FuzzyMatch(match, tokens, silent); } else // if (exactMatch) { if (!silent && match.SubCommands.Count > 1) { // More than one possible completion // Just show the completions for this node. PrintCompletions(match.SubCommands); } else if (!silent && match.SubCommands.Count == 1) { // Just one possible completion; fill it out. DebuggerCommand next = match.SubCommands[0]; StringBuilder sb = new StringBuilder(String.Format("{0} {1}", match.Name, next.Name)); while (next.SubCommands.Count > 0) { if (next.SubCommands.Count > 1) { break; } next = next.SubCommands[0]; sb.AppendFormat(" {0}", next.Name); } return(sb.ToString()); } } if (subMatch == String.Empty) { return(String.Format("{0} ", match.Name)); } else { return(String.Format("{0} {1}", match.Name, subMatch)); } } }
public DebuggerPrompt(DebuggerCommand root) { _commandTree = root; _commandHistory = new List <string>(64); _historyIndex = 0; }