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

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