You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

CommandParser.cs 6.4 kB

8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. using System;
  2. using System.Collections.Immutable;
  3. using System.Text;
  4. using System.Threading.Tasks;
  5. namespace Discord.Commands
  6. {
  7. internal static class CommandParser
  8. {
  9. private enum ParserPart
  10. {
  11. None,
  12. Parameter,
  13. QuotedParameter
  14. }
  15. public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos)
  16. {
  17. ParameterInfo curParam = null;
  18. StringBuilder argBuilder = new StringBuilder(input.Length);
  19. int endPos = input.Length;
  20. var curPart = ParserPart.None;
  21. int lastArgEndPos = int.MinValue;
  22. var argList = ImmutableArray.CreateBuilder<TypeReaderResult>();
  23. var paramList = ImmutableArray.CreateBuilder<TypeReaderResult>();
  24. bool isEscaping = false;
  25. char c;
  26. for (int curPos = startPos; curPos <= endPos; curPos++)
  27. {
  28. if (curPos < endPos)
  29. c = input[curPos];
  30. else
  31. c = '\0';
  32. //If this character is escaped, skip it
  33. if (isEscaping)
  34. {
  35. if (curPos != endPos)
  36. {
  37. argBuilder.Append(c);
  38. isEscaping = false;
  39. continue;
  40. }
  41. }
  42. //Are we escaping the next character?
  43. if (c == '\\' && (curParam == null || !curParam.IsRemainder))
  44. {
  45. isEscaping = true;
  46. continue;
  47. }
  48. //If we're processing an remainder parameter, ignore all other logic
  49. if (curParam != null && curParam.IsRemainder && curPos != endPos)
  50. {
  51. argBuilder.Append(c);
  52. continue;
  53. }
  54. //If we're not currently processing one, are we starting the next argument yet?
  55. if (curPart == ParserPart.None)
  56. {
  57. if (char.IsWhiteSpace(c) || curPos == endPos)
  58. continue; //Skip whitespace between arguments
  59. else if (curPos == lastArgEndPos)
  60. return ParseResult.FromError(CommandError.ParseFailed, "There must be at least one character of whitespace between arguments.");
  61. else
  62. {
  63. if (curParam == null)
  64. curParam = command.Parameters.Count > argList.Count ? command.Parameters[argList.Count] : null;
  65. if (curParam != null && curParam.IsRemainder)
  66. {
  67. argBuilder.Append(c);
  68. continue;
  69. }
  70. if (c == '\"')
  71. {
  72. curPart = ParserPart.QuotedParameter;
  73. continue;
  74. }
  75. curPart = ParserPart.Parameter;
  76. }
  77. }
  78. //Has this parameter ended yet?
  79. string argString = null;
  80. if (curPart == ParserPart.Parameter)
  81. {
  82. if (curPos == endPos || char.IsWhiteSpace(c))
  83. {
  84. argString = argBuilder.ToString();
  85. lastArgEndPos = curPos;
  86. }
  87. else
  88. argBuilder.Append(c);
  89. }
  90. else if (curPart == ParserPart.QuotedParameter)
  91. {
  92. if (c == '\"')
  93. {
  94. argString = argBuilder.ToString(); //Remove quotes
  95. lastArgEndPos = curPos + 1;
  96. }
  97. else
  98. argBuilder.Append(c);
  99. }
  100. if (argString != null)
  101. {
  102. if (curParam == null)
  103. return ParseResult.FromError(CommandError.BadArgCount, "The input text has too many parameters.");
  104. var typeReaderResult = await curParam.ParseAsync(context, argString, services).ConfigureAwait(false);
  105. if (!typeReaderResult.IsSuccess && typeReaderResult.Error != CommandError.MultipleMatches)
  106. return ParseResult.FromError(typeReaderResult);
  107. if (curParam.IsMultiple)
  108. {
  109. paramList.Add(typeReaderResult);
  110. curPart = ParserPart.None;
  111. }
  112. else
  113. {
  114. argList.Add(typeReaderResult);
  115. curParam = null;
  116. curPart = ParserPart.None;
  117. }
  118. argBuilder.Clear();
  119. }
  120. }
  121. if (curParam != null && curParam.IsRemainder)
  122. {
  123. var typeReaderResult = await curParam.ParseAsync(context, argBuilder.ToString(), services).ConfigureAwait(false);
  124. if (!typeReaderResult.IsSuccess)
  125. return ParseResult.FromError(typeReaderResult);
  126. argList.Add(typeReaderResult);
  127. }
  128. if (isEscaping)
  129. return ParseResult.FromError(CommandError.ParseFailed, "Input text may not end on an incomplete escape.");
  130. if (curPart == ParserPart.QuotedParameter)
  131. return ParseResult.FromError(CommandError.ParseFailed, "A quoted parameter is incomplete");
  132. //Add missing optionals
  133. for (int i = argList.Count; i < command.Parameters.Count; i++)
  134. {
  135. var param = command.Parameters[i];
  136. if (param.IsMultiple)
  137. continue;
  138. if (!param.IsOptional)
  139. return ParseResult.FromError(CommandError.BadArgCount, "The input text has too few parameters.");
  140. argList.Add(TypeReaderResult.FromSuccess(param.DefaultValue));
  141. }
  142. return ParseResult.FromSuccess(argList.ToImmutable(), paramList.ToImmutable());
  143. }
  144. }
  145. }