public void TestMatchArgValuesWithOptionalTokens() { ArgumentToken arg1 = new ArgumentToken <int> .Builder().Name("arg1").IsOptional(false).Parser(int.TryParse).Build(); ArgumentToken arg2 = new ArgumentToken <double> .Builder().Name("arg2").IsOptional(false).Parser(double.TryParse).Build(); var tokens = new ICommandToken[] { new VerbToken(new Name("verb0")), new OptionWithArgumentToken.Builder().Name("-o2", "--option2").WithArgument(arg1).WithArgument(arg2).Build(), }; var builder = new CommandUsage.Builder().Description("test usage"); foreach (var token in tokens) { builder.WithToken(token); } ICommandUsage usage = builder.Build(); TokenMatchCollection matchCollection = CommandParser.Match(usage.Tokens, "verb0 -o2 1 2.34"); int arg1Value; bool arg1Exists = matchCollection.TryGetArgValue(arg1, out arg1Value); double arg2Value; bool arg2Exists = matchCollection.TryGetArgValue(arg2, out arg2Value); Assert.True(arg1Exists); Assert.True(arg2Exists); Assert.Equal(1, arg1Value); Assert.Equal(2.34, arg2Value); }
public void TestOptionWithQuotedStringNotFullMatch(string input) { ICommandArgumentToken <string> .ValueParser trivialStringParser = (string i, out string v) => { v = i; return(true); }; ArgumentToken arg1 = new ArgumentToken <string> .Builder().Name("arg1").Parser(trivialStringParser).Build(); ArgumentToken arg2 = new ArgumentToken <string> .Builder().Name("arg2").Parser(trivialStringParser).Build(); ArgumentToken arg3 = new ArgumentToken <string> .Builder().Name("arg3").Parser(trivialStringParser).Build(); var token = new OptionWithArgumentToken.Builder() .Name("option", "alt1", "alt2") .WithArgument(arg1) .WithArgument(arg2) .WithArgument(arg3) .Build(); ICommandUsage usage = new CommandUsage.Builder().WithToken(token).Build(); TokenMatchCollection matchCollection = CommandParser.Match(usage.Tokens, input); Assert.True(matchCollection.Matches.Count() == 1); ParserTokenMatch match = matchCollection.Matches.First(); Assert.False(match.IsFullMatch, input); }
[InlineData("-o2 1", false)] //partial match public void TestTokenIsFullMatch(string input, bool expIsFullMatch) { ArgumentToken arg1 = new ArgumentToken <int> .Builder().Name("arg1").IsOptional(false).Parser(int.TryParse).Build(); ArgumentToken arg2 = new ArgumentToken <double> .Builder().Name("arg2").IsOptional(false).Parser(double.TryParse).Build(); var tokens = new ICommandToken[] { new OptionWithArgumentToken.Builder().Name("-o2", "--option2").WithArgument(arg1).WithArgument(arg2).Build() }; var builder = new CommandUsage.Builder() .Description("test usage"); foreach (var token in tokens) { builder.WithToken(token); } ICommandUsage usage = builder.Build(); TokenMatchCollection matchCollection = CommandParser.Match(usage.Tokens, input); Assert.Same(usage.Tokens, matchCollection.MatchableTokens); Assert.NotEmpty(matchCollection.Matches); Assert.Equal(1, matchCollection.Matches.Count()); ParserTokenMatch match = matchCollection.Matches.First(); Assert.Equal(expIsFullMatch, match.IsFullMatch); }
public Dictionary <ICommandRoot, float> GetCommandSuggestions(string text) { Dictionary <ICommandRoot, float> suggestions = new Dictionary <ICommandRoot, float>(); foreach (var c in _commands) { TokenMatchCollection match = CommandParser.Match(c.CommonTokens, text); suggestions.Add(c, match.MatchQuality); } return(suggestions); }
public bool TryGetCommand(string text, out ICommandRoot command) { float bestMatchQuality = 0; command = null; foreach (var c in _commands) { TokenMatchCollection match = CommandParser.Match(c.CommonTokens, text); if (match.MatchQuality > bestMatchQuality) { bestMatchQuality = match.MatchQuality; command = c; } } return(bestMatchQuality > 0); }
[InlineData("verb0 verb4 -o1 -o2 1 2.34 -o3", 0, 4)] //All tokens present, wrong order public void TestMatchWithOptionalTokensWithArgs(string input, params int[] expectedMatchingIndexes) { ArgumentToken arg1 = new ArgumentToken <int> .Builder().Name("arg1").IsOptional(false).Parser(int.TryParse).Build(); ArgumentToken arg2 = new ArgumentToken <double> .Builder().Name("arg2").IsOptional(false).Parser(double.TryParse).Build(); var tokens = new ICommandToken[] { new VerbToken(new Name("verb0")), new StandAloneOptionToken(new Name("-o1", "--option1")), new OptionWithArgumentToken.Builder().Name("-o2", "--option2").WithArgument(arg1).WithArgument(arg2).Build(), new StandAloneOptionToken(new Name("-o3", "--option3")), new VerbToken(new Name("verb4")), }; var builder = new CommandUsage.Builder() .Description("test usage"); foreach (var token in tokens) { builder.WithToken(token); } ICommandUsage usage = builder.Build(); TokenMatchCollection matchCollection = CommandParser.Match(usage.Tokens, input); Assert.Same(usage.Tokens, matchCollection.MatchableTokens); if (expectedMatchingIndexes.Length > 0) { Assert.NotEmpty(matchCollection.Matches); Assert.Equal(expectedMatchingIndexes.Length, matchCollection.Matches.Count()); //Ensure all that are expected are there foreach (var expectedMatchingIndex in expectedMatchingIndexes) { Assert.True(matchCollection.Matches.Any(x => x.TokenIdx == expectedMatchingIndex)); } } else { Assert.Empty(matchCollection.Matches); } }
public CommandUsageMatchData Parse(string text) { CommandUsageMatchData bestMatchData = null; float bestMatchQuality = float.NegativeInfinity; foreach (var command in _commands) { Dictionary <ICommandUsage, TokenMatchCollection> matchDict = CommandParser.GetUsageMatchResults(command, text); foreach (var kvp in matchDict) { ICommandUsage usage = kvp.Key; TokenMatchCollection matchCollection = kvp.Value; if (matchCollection.IsFullMatch && matchCollection.MatchQuality > bestMatchQuality) { bestMatchQuality = matchCollection.MatchQuality; bestMatchData = new CommandUsageMatchData(command, usage, matchCollection); } } } return(bestMatchData); }
public static Dictionary <ICommandUsage, TokenMatchCollection> GetUsageMatchResults(ICommandRoot currentRoot, string text) { Dictionary <ICommandUsage, TokenMatchCollection> usageMatchData = new Dictionary <ICommandUsage, TokenMatchCollection>(); if (currentRoot.Usages.Any()) { foreach (var usage in currentRoot.Usages) { ICommandToken[] tokens = currentRoot.CommonTokens.Concat(usage.Tokens).ToArray(); TokenMatchCollection matchCollection = Match(tokens, text); usageMatchData.Add(usage, matchCollection); } } else { ICommandToken[] tokens = currentRoot.CommonTokens.ToArray(); TokenMatchCollection matchCollection = Match(tokens, text); usageMatchData.Add(new CommandRootUsage(currentRoot), matchCollection); } return(usageMatchData); }
[InlineData("1234 verb1 verb2 verb3 verb4", 3)] //Too many tokens public void TestMatch(string input, int?expectedMatchIdx) { var tokens = new ICommandToken[] { new ArgumentToken <int> .Builder() .Name("name") .Parser(int.TryParse) .IsOptional(false) .Build(), new VerbToken(new Name("verb1", "alt1")), new VerbToken(new Name("verb2", "alt2")), new VerbToken(new Name("verb3", "alt3")) }; var builder = new CommandUsage.Builder() .Description("test usage"); foreach (var token in tokens) { builder.WithToken(token); } ICommandUsage usage = builder.Build(); TokenMatchCollection matchCollection = CommandParser.Match(usage.Tokens, input); Assert.Same(usage.Tokens, matchCollection.MatchableTokens); if (expectedMatchIdx.HasValue) { Assert.NotEmpty(matchCollection.Matches); Assert.Same(tokens[expectedMatchIdx.Value], matchCollection.Matches.Last(x => x.MatchOutcome == Enums.MatchOutcome.Full).Token); Assert.Equal(expectedMatchIdx, matchCollection.Matches.Where(x => x.MatchOutcome == Enums.MatchOutcome.Full).Max(x => x.TokenIdx)); } else { Assert.Empty(matchCollection.Matches); } }
public static TokenMatchCollection Match(ICommandToken[] commandTokens, string input) { string[] inputTokens = Tokenize(input); int inputTokenIdx = 0; int commandTokensIdx = 0; List <int> matchableTokenIndexes = new List <int>(); TokenMatchCollection matchCollection = new TokenMatchCollection(input, commandTokens); while (inputTokenIdx < inputTokens.Length) { //Take another token from the usage to consider if (commandTokensIdx < commandTokens.Length) { matchableTokenIndexes.Add(commandTokensIdx); commandTokensIdx++; } bool areAllTokensOptional = true; //Find the next token match. Greedy search prefers tokens with lowest index TokenMatchResult bestMatch = TokenMatchResult.None; int bestMatchIdx = -1; foreach (var tokenIdx in matchableTokenIndexes) { ICommandToken token = commandTokens[tokenIdx]; areAllTokensOptional &= token.IsOptional; TokenMatchResult matchResult = token.Matches(inputTokens, inputTokenIdx); int tokenMatchLength = matchResult.TokensMatched; if (matchResult.IsBetterMatchThan(bestMatch)) { bestMatch = matchResult; bestMatchIdx = tokenIdx; } if (matchResult.MatchOutcome == MatchOutcome.Full) { //match! List <string> matchedTokensStrs = inputTokens.Skip(inputTokenIdx).Take(tokenMatchLength).ToList(); string matchText = string.Join(" ", matchedTokensStrs); matchCollection.With(new ParserTokenMatch(tokenIdx, matchResult)); matchCollection.AddAllArgumentValues(matchResult); matchableTokenIndexes.Remove(tokenIdx); inputTokenIdx += tokenMatchLength; if (!token.IsOptional) { //All optional tokens until this required one are no longer viable matchableTokenIndexes.Clear(); } break; } } if (bestMatch.MatchOutcome < MatchOutcome.Full) { //No match found! if (!areAllTokensOptional || commandTokensIdx == commandTokens.Length) { //We couldn't match a required token OR //considering all tokens, no more matches could be found //...so that's the end of the match if (bestMatch.MatchOutcome != MatchOutcome.None) { //Add a partial match for whatever last failed to match matchCollection.With(new ParserTokenMatch(bestMatchIdx, bestMatch)); } return(matchCollection); } } } return(matchCollection); }