/// <summary> /// Attempts to bind the command shape to the given named parameters, matching values to its parameters. /// </summary> /// <param name="namedParameters">The named parameters.</param> /// <param name="boundCommandShape">The resulting shape, if any.</param> /// <param name="searchOptions">A set of search options.</param> /// <returns>true if the shape matches; otherwise, false.</returns> public bool TryBind ( IReadOnlyDictionary <string, IReadOnlyList <string> > namedParameters, [NotNullWhen(true)] out BoundCommandNode?boundCommandShape, TreeSearchOptions?searchOptions = null ) { searchOptions ??= new TreeSearchOptions(); boundCommandShape = null; using var enumerator = namedParameters.GetEnumerator(); var parametersToCheck = new List <IParameterShape>(this.Shape.Parameters); var boundParameters = new List <BoundParameterShape>(); while (parametersToCheck.Count > 0) { // The return value of MoveNext is ignored, because empty collections are allowed _ = enumerator.MoveNext(); var matchedParameters = new List <IParameterShape>(); foreach (var parameterToCheck in parametersToCheck) { // Because the current enumerator might be invalid or ended, we'll fix up the key-value pair here var current = enumerator.Current; if (current.Equals(default(KeyValuePair <string, IReadOnlyList <string> >))) { current = new KeyValuePair <string, IReadOnlyList <string> >(string.Empty, Array.Empty <string>()); } if (!parameterToCheck.Matches(current, out var isFatal, searchOptions)) { if (isFatal) { return(false); } continue; } matchedParameters.Add(parameterToCheck); boundParameters.Add(new BoundParameterShape(parameterToCheck, current.Value)); } if (matchedParameters.Count == 0) { // Check if all remaining parameters are optional if (!parametersToCheck.All(p => p.IsOmissible(searchOptions))) { return(false); } boundCommandShape = new BoundCommandNode(this, boundParameters); return(true); } foreach (var matchedParameter in matchedParameters) { parametersToCheck.Remove(matchedParameter); } } // if there are more tokens to come, we don't match if (enumerator.MoveNext()) { return(false); } boundCommandShape = new BoundCommandNode(this, boundParameters); return(true); }
/// <summary> /// Attempts to bind the command shape to the given tokenizer, materializing values for its parameters. /// </summary> /// <param name="tokenizer">The token sequence.</param> /// <param name="boundCommandShape">The resulting shape, if any.</param> /// <param name="searchOptions">A set of search options.</param> /// <returns>true if the shape matches; otherwise, false.</returns> public bool TryBind ( TokenizingEnumerator tokenizer, [NotNullWhen(true)] out BoundCommandNode?boundCommandShape, TreeSearchOptions?searchOptions = null ) { searchOptions ??= new TreeSearchOptions(); boundCommandShape = null; if (!tokenizer.MoveNext()) { return(false); } var parametersToCheck = new List <IParameterShape>(this.Shape.Parameters); var boundParameters = new List <BoundParameterShape>(); while (parametersToCheck.Count > 0) { var matchedParameters = new List <IParameterShape>(); foreach (var parameterToCheck in parametersToCheck) { if (!parameterToCheck.Matches(tokenizer, out var consumedTokens, searchOptions)) { continue; } // gobble up the tokens matchedParameters.Add(parameterToCheck); var boundTokens = new List <string>(); for (ulong i = 0; i < consumedTokens; ++i) { if (!tokenizer.MoveNext()) { return(false); } var type = tokenizer.Current.Type; if (type != TokenType.Value) { // skip names, we've already checked them continue; } var value = tokenizer.Current.Value.ToString(); boundTokens.Add(value); } boundParameters.Add(new BoundParameterShape(parameterToCheck, boundTokens)); } if (matchedParameters.Count == 0) { // Check if all remaining parameters are optional if (!parametersToCheck.All(p => p.IsOmissible(searchOptions))) { return(false); } boundCommandShape = new BoundCommandNode(this, boundParameters); return(true); } foreach (var matchedParameter in matchedParameters) { parametersToCheck.Remove(matchedParameter); } } // if there are more tokens to come, we don't match if (tokenizer.MoveNext()) { return(false); } boundCommandShape = new BoundCommandNode(this, boundParameters); return(true); }