internal static NuGetLicenseExpression Parse()

in src/NuGet.Core/NuGet.Packaging/Licenses/NuGetLicenseExpressionParser.cs [30:151]


        internal static NuGetLicenseExpression Parse(string expression)
        {
            try
            {
                var tokens = GetTokens(expression);
                var operatorStack = new Stack<LicenseExpressionToken>();
                // The operand stack can contain both unprocessed value and complex expressions such as MIT OR Apache-2.0.
                // Complex expressions are valid operands for the logical operators. The first value represents whether it's value or an expression.
                // true => LicenseExpressionToken, false => NuGetLicenseExpression
                var operandStack = new Stack<Tuple<bool, object>>();

                var lastTokenType = LicenseTokenType.IDENTIFIER;
                var firstPass = true;

                foreach (var token in tokens)
                {
                    var currentTokenType = token.TokenType;
                    switch (token.TokenType)
                    {
                        case LicenseTokenType.IDENTIFIER:
                            if (!firstPass && !token.TokenType.IsValidPrecedingToken(lastTokenType))
                            {
                                throw new NuGetLicenseExpressionParsingException(string.Format(CultureInfo.CurrentCulture, Strings.NuGetLicenseExpression_InvalidToken, token.Value));
                            }
                            // Add it to the operandstack. Only add it to the expression when you meet an operator
                            operandStack.Push(new Tuple<bool, object>(true, token));
                            break;

                        case LicenseTokenType.OPENING_BRACKET:
                            if (!firstPass && !token.TokenType.IsValidPrecedingToken(lastTokenType))
                            {
                                throw new NuGetLicenseExpressionParsingException(string.Format(CultureInfo.CurrentCulture, Strings.NuGetLicenseExpression_InvalidToken, token.Value));
                            }
                            operatorStack.Push(token);
                            break;

                        case LicenseTokenType.CLOSING_BRACKET:
                            if (firstPass || !token.TokenType.IsValidPrecedingToken(lastTokenType))
                            {
                                throw new NuGetLicenseExpressionParsingException(string.Format(CultureInfo.CurrentCulture, Strings.NuGetLicenseExpression_InvalidToken, token.Value));
                            }

                            // pop until we hit the opening bracket
                            while (operatorStack.Count > 0 && operatorStack.Peek().TokenType != LicenseTokenType.OPENING_BRACKET)
                            {
                                ProcessOperators(operatorStack, operandStack);
                            }

                            if (operatorStack.Count > 0)
                            {
                                // pop the bracket
                                operatorStack.Pop();
                            }
                            else
                            {
                                throw new NuGetLicenseExpressionParsingException(string.Format(CultureInfo.CurrentCulture, Strings.NuGetLicenseExpression_MismatchedParentheses));
                            }
                            break;

                        case LicenseTokenType.WITH:
                        case LicenseTokenType.AND:
                        case LicenseTokenType.OR:
                            if (firstPass && !token.TokenType.IsValidPrecedingToken(lastTokenType))
                            {
                                throw new NuGetLicenseExpressionParsingException(string.Format(CultureInfo.CurrentCulture, Strings.NuGetLicenseExpression_InvalidToken, token.Value));
                            }
                            if (operatorStack.Count == 0 || // The operator stack is empty
                                operatorStack.Peek().TokenType == LicenseTokenType.OPENING_BRACKET || // The last token is an opening bracket (treat it the same as empty
                                token.TokenType < operatorStack.Peek().TokenType) // An operator that has higher priority than the operator on the stack
                            {
                                operatorStack.Push(token);
                            }
                            // An operator that has lower/same priority than the operator on the stack
                            else if (token.TokenType >= operatorStack.Peek().TokenType)
                            {
                                ProcessOperators(operatorStack, operandStack);
                                operatorStack.Push(token);
                            }
                            break;

                        default:
                            throw new NuGetLicenseExpressionParsingException("Should not happen. File a bug with repro steps on NuGet/Home if seen.");
                    }
                    lastTokenType = currentTokenType;
                    firstPass = false;
                }

                while (operatorStack.Count > 0)
                {
                    if (operatorStack.Peek().TokenType != LicenseTokenType.OPENING_BRACKET)
                    {
                        ProcessOperators(operatorStack, operandStack);
                    }
                    else
                    {
                        throw new NuGetLicenseExpressionParsingException(string.Format(CultureInfo.CurrentCulture, Strings.NuGetLicenseExpression_MismatchedParentheses));
                    }
                }

                // This handles the no operators scenario. This check could be simpler, but it's dangerous to assume all scenarios have been handled by the above logic.
                // As written and as tested, you would never have more than 1 operand on the stack

                if (operandStack.Count != 1)
                {
                    throw new NuGetLicenseExpressionParsingException(string.Format(CultureInfo.CurrentCulture, Strings.NuGetLicenseExpression_InvalidExpression));
                }
                else
                {
                    var value = operandStack.Pop();

                    return value.Item1 ? NuGetLicense.ParseIdentifier(((LicenseExpressionToken)value.Item2).Value, allowUnlicensed: true) : (NuGetLicenseExpression)value.Item2;
                }
            }
            catch (NuGetLicenseExpressionParsingException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new NuGetLicenseExpressionParsingException(e.Message, e);
            }
        }