Add basic flag parsing support
This commit is contained in:
parent
2148e29f54
commit
7a1aaf6dbd
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using App.Metrics;
|
using App.Metrics;
|
||||||
@ -61,9 +62,10 @@ namespace PluralKit.Bot
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Match(ref string used, params string[] potentialMatches)
|
public bool Match(ref string used, params string[] potentialMatches)
|
||||||
{
|
{
|
||||||
|
var arg = PeekArgument();
|
||||||
foreach (var match in potentialMatches)
|
foreach (var match in potentialMatches)
|
||||||
{
|
{
|
||||||
if (PeekArgument().Equals(match, StringComparison.InvariantCultureIgnoreCase))
|
if (arg.Equals(match, StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
used = PopArgument();
|
used = PopArgument();
|
||||||
return true;
|
return true;
|
||||||
@ -82,6 +84,15 @@ namespace PluralKit.Bot
|
|||||||
return Match(ref used, potentialMatches);
|
return Match(ref used, potentialMatches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool MatchFlag(params string[] potentialMatches)
|
||||||
|
{
|
||||||
|
// Flags are *ALWAYS PARSED LOWERCASE*. This means we skip out on a "ToLower" call here.
|
||||||
|
// Can assume the caller array only contains lowercase *and* the set below only contains lowercase
|
||||||
|
|
||||||
|
var flags = _parameters.Flags();
|
||||||
|
return potentialMatches.Any(potentialMatch => flags.Contains(potentialMatch));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Execute<T>(Command commandDef, Func<T, Task> handler)
|
public async Task Execute<T>(Command commandDef, Func<T, Task> handler)
|
||||||
{
|
{
|
||||||
_currentCommand = commandDef;
|
_currentCommand = commandDef;
|
||||||
|
@ -12,6 +12,29 @@ namespace PluralKit.Bot
|
|||||||
|
|
||||||
private readonly string _cmd;
|
private readonly string _cmd;
|
||||||
private int _ptr;
|
private int _ptr;
|
||||||
|
private ISet<string> _flags = null; // Only parsed when requested first time
|
||||||
|
|
||||||
|
private struct WordPosition
|
||||||
|
{
|
||||||
|
// Start of the word
|
||||||
|
internal int startPos;
|
||||||
|
|
||||||
|
// End of the word
|
||||||
|
internal int endPos;
|
||||||
|
|
||||||
|
// How much to advance word pointer afterwards to point at the start of the *next* word
|
||||||
|
internal int advanceAfterWord;
|
||||||
|
|
||||||
|
internal bool wasQuoted;
|
||||||
|
|
||||||
|
public WordPosition(int startPos, int endPos, int advanceAfterWord, bool wasQuoted)
|
||||||
|
{
|
||||||
|
this.startPos = startPos;
|
||||||
|
this.endPos = endPos;
|
||||||
|
this.advanceAfterWord = advanceAfterWord;
|
||||||
|
this.wasQuoted = wasQuoted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Parameters(string cmd)
|
public Parameters(string cmd)
|
||||||
{
|
{
|
||||||
@ -21,42 +44,89 @@ namespace PluralKit.Bot
|
|||||||
_ptr = 0;
|
_ptr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ParseFlags()
|
||||||
|
{
|
||||||
|
_flags = new HashSet<string>();
|
||||||
|
|
||||||
|
var ptr = 0;
|
||||||
|
while (NextWordPosition(ptr) is { } wp)
|
||||||
|
{
|
||||||
|
ptr = wp.endPos + wp.advanceAfterWord;
|
||||||
|
|
||||||
|
// Is this word a *flag* (as in, starts with a - AND is not quoted)
|
||||||
|
if (_cmd[wp.startPos] != '-' || wp.wasQuoted) continue; // (if not, carry on w/ next word)
|
||||||
|
|
||||||
|
// Find the *end* of the flag start (technically allowing arbitrary amounts of dashes)
|
||||||
|
var flagNameStart = wp.startPos;
|
||||||
|
while (flagNameStart < _cmd.Length && _cmd[flagNameStart] == '-')
|
||||||
|
flagNameStart++;
|
||||||
|
|
||||||
|
// Then add the word to the flag set
|
||||||
|
var word = _cmd.Substring(flagNameStart, wp.endPos - flagNameStart).Trim();
|
||||||
|
if (word.Length > 0)
|
||||||
|
_flags.Add(word.ToLowerInvariant());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string Pop()
|
public string Pop()
|
||||||
{
|
{
|
||||||
var positions = NextWordPosition();
|
// Loop to ignore and skip past flags
|
||||||
if (positions == null) return "";
|
while (NextWordPosition(_ptr) is { } pos)
|
||||||
|
{
|
||||||
|
_ptr = pos.endPos + pos.advanceAfterWord;
|
||||||
|
if (_cmd[pos.startPos] == '-' && !pos.wasQuoted) continue;
|
||||||
|
return _cmd.Substring(pos.startPos, pos.endPos - pos.startPos).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
var (start, end, advance) = positions.Value;
|
return "";
|
||||||
_ptr = end + advance;
|
|
||||||
return _cmd.Substring(start, end - start).Trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Peek()
|
public string Peek()
|
||||||
{
|
{
|
||||||
var positions = NextWordPosition();
|
// Loop to ignore and skip past flags, temp ptr so we don't move the real ptr
|
||||||
if (positions == null) return "";
|
var ptr = _ptr;
|
||||||
|
while (NextWordPosition(ptr) is { } pos)
|
||||||
var (start, end, _) = positions.Value;
|
{
|
||||||
return _cmd.Substring(start, end - start).Trim();
|
ptr = pos.endPos + pos.advanceAfterWord;
|
||||||
|
if (_cmd[pos.startPos] == '-' && !pos.wasQuoted) continue;
|
||||||
|
return _cmd.Substring(pos.startPos, pos.endPos - pos.startPos).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ISet<string> Flags()
|
||||||
|
{
|
||||||
|
if (_flags == null) ParseFlags();
|
||||||
|
return _flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Remainder()
|
||||||
|
{
|
||||||
|
// Skip all *leading* flags when taking the remainder
|
||||||
|
while (NextWordPosition(_ptr) is {} wp)
|
||||||
|
{
|
||||||
|
if (_cmd[wp.startPos] != '-' || wp.wasQuoted) break;
|
||||||
|
_ptr = wp.endPos + wp.advanceAfterWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
// *Then* get the remainder
|
||||||
|
return _cmd.Substring(Math.Min(_ptr, _cmd.Length)).Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Remainder() => _cmd.Substring(Math.Min(_ptr, _cmd.Length)).Trim();
|
|
||||||
public string FullCommand => _cmd;
|
public string FullCommand => _cmd;
|
||||||
|
|
||||||
// Returns tuple of (startpos, endpos, advanceafter)
|
private WordPosition? NextWordPosition(int position)
|
||||||
// advanceafter is how much to move the pointer afterwards to point it
|
|
||||||
// at the start of the next word
|
|
||||||
private ValueTuple<int, int, int>? NextWordPosition()
|
|
||||||
{
|
{
|
||||||
// Is this the end of the string?
|
// Is this the end of the string?
|
||||||
if (_cmd.Length <= _ptr) return null;
|
if (_cmd.Length <= position) return null;
|
||||||
|
|
||||||
// Is this a quoted word?
|
// Is this a quoted word?
|
||||||
if (_quotePairs.ContainsKey(_cmd[_ptr]))
|
if (_quotePairs.ContainsKey(_cmd[position]))
|
||||||
{
|
{
|
||||||
// This is a quoted word, find corresponding end quote and return span
|
// This is a quoted word, find corresponding end quote and return span
|
||||||
var endQuote = _quotePairs[_cmd[_ptr]];
|
var endQuote = _quotePairs[_cmd[position]];
|
||||||
var endQuotePosition = _cmd.IndexOf(endQuote, _ptr + 1);
|
var endQuotePosition = _cmd.IndexOf(endQuote, position + 1);
|
||||||
|
|
||||||
// Position after the end quote should be a space (or EOL)
|
// Position after the end quote should be a space (or EOL)
|
||||||
// Otherwise treat it as a standard word that's not quoted
|
// Otherwise treat it as a standard word that's not quoted
|
||||||
@ -66,16 +136,19 @@ namespace PluralKit.Bot
|
|||||||
{
|
{
|
||||||
// This is an unterminated quoted word, just return the entire word including the start quote
|
// This is an unterminated quoted word, just return the entire word including the start quote
|
||||||
// TODO: should we do something else here?
|
// TODO: should we do something else here?
|
||||||
return (_ptr, _cmd.Length, 0);
|
return new WordPosition(position, _cmd.Length, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (_ptr + 1, endQuotePosition, 2);
|
return new WordPosition(position + 1, endQuotePosition, 2, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not a quoted word, just find the next space and return as appropriate
|
// Not a quoted word, just find the next space and return if it's the end of the command
|
||||||
var wordEnd = _cmd.IndexOf(' ', _ptr + 1);
|
var wordEnd = _cmd.IndexOf(' ', position + 1);
|
||||||
return wordEnd != -1 ? (_ptr, wordEnd, 1) : (_ptr, _cmd.Length, 0);
|
|
||||||
|
return wordEnd == -1
|
||||||
|
? new WordPosition(position, _cmd.Length, 0, false)
|
||||||
|
: new WordPosition(position, wordEnd, 1, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user