Beispiel #1
0
    /// <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);
    }
Beispiel #2
0
    /// <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);
    }