public static StringView SimplifyExpression(StringView outerText, bool trimLastWhiteSpaces = true)
        {
            // First we look for the closers that could be trimmed
            Stack <int> nestedLevelsEnd   = new Stack <int>();
            Stack <int> nestedLevelsStart = new Stack <int>();

            for (int i = outerText.length - 1; i >= 0; --i)
            {
                if (char.IsWhiteSpace(outerText[i]))
                {
                    continue;
                }
                if (outerText[i] == '}')
                {
                    nestedLevelsEnd.Push(i);
                    continue;
                }
                break;
            }
            // Then we parse the string to check how many we can trim
            bool hasTrimmedText = false;

            if (nestedLevelsEnd.Any())
            {
                bool inNonTrimmableText  = false;
                int  nonTrimmableOpeners = 0;
                bool isInString          = false;
                for (int i = 0; i < outerText.length; ++i)
                {
                    if (char.IsWhiteSpace(outerText[i]))
                    {
                        continue;
                    }
                    if (k_Quotes.Contains(outerText[i]))
                    {
                        isInString = !isInString;
                    }
                    if (isInString)
                    {
                        continue;
                    }
                    if (outerText[i] == '{')
                    {
                        nestedLevelsStart.Push(i);
                        // if part can't be trimmed we must not keep the { so we keep track of how many
                        if (inNonTrimmableText)
                        {
                            ++nonTrimmableOpeners;
                        }
                        continue;
                    }
                    if (!nestedLevelsStart.Any() || !nestedLevelsEnd.Any())
                    {
                        break;
                    }
                    if (outerText[i] == '}')
                    {
                        if (nonTrimmableOpeners == 0 && i == nestedLevelsEnd.Peek() && nestedLevelsStart.Count == nestedLevelsEnd.Count)
                        {
                            hasTrimmedText = true;
                            break;
                        }
                        nestedLevelsStart.Pop();
                        if (i == nestedLevelsEnd.Peek())
                        {
                            nestedLevelsEnd.Pop();
                        }
                        // In that case that means there was one expression that was closed and no more remaining so we should exit
                        if (!nestedLevelsStart.Any())
                        {
                            break;
                        }
                        if (inNonTrimmableText && nonTrimmableOpeners > 0)
                        {
                            --nonTrimmableOpeners;
                        }
                    }
                    // if we find other characters that means that part can't be trimmed, in that case we must not keep the next { we might find
                    inNonTrimmableText = true;
                }
            }
            var result = outerText;

            if (hasTrimmedText)
            {
                int start  = nestedLevelsStart.Peek() + 1;
                int length = nestedLevelsEnd.Peek() - start;
                result = outerText.Substring(start, length);
            }
            return(trimLastWhiteSpaces ? result.Trim() : result);
        }
        public static StringView[] GetExpressionsStartAndLength(StringView text, out bool rootHasParameters)
        {
            rootHasParameters = false;
            var openersStack            = new Stack <char>();
            var expressions             = new List <StringView>();
            int firstOpenerIndex        = -1;
            int currentStringTokenIndex = -1;

            for (int i = 0; i < text.length; ++i)
            {
                if (text[i] == ' ')
                {
                    continue;
                }

                // In case of a string, we must find the end of the string before checking any nested levels or ,
                if (k_Quotes.Any(c => c == text[i]))
                {
                    if (currentStringTokenIndex == -1)
                    {
                        currentStringTokenIndex = i;
                    }
                    else if (text[currentStringTokenIndex] != text[i])
                    {
                        throw new SearchExpressionParseException($"Nested strings are not allowed, \"{text[i]}\" found instead of \"{text[currentStringTokenIndex]}\" in \"{text.Substring(currentStringTokenIndex, i + 1 - currentStringTokenIndex)}\"", text.startIndex + currentStringTokenIndex, i - currentStringTokenIndex + 1);
                    }
                    else
                    {
                        currentStringTokenIndex = -1;
                    }
                }
                if (currentStringTokenIndex != -1) // is in string
                {
                    continue;
                }

                if (k_Openers.Any(c => c == text[i]))
                {
                    if (openersStack.Count == 0)
                    {
                        firstOpenerIndex = i;
                    }
                    openersStack.Push(text[i]);
                    continue;
                }

                if (k_Closers.Any(c => c == text[i]))
                {
                    if (openersStack.Count == 0)
                    {
                        throw new SearchExpressionParseException($"Extra \"{text[i]}\" found", text.startIndex + i, 1);
                    }
                    if (CharMatchOpener(openersStack.Peek(), text[i]))
                    {
                        openersStack.Pop();
                        // We found the final closer, that means we found the end of the expression
                        if (openersStack.Count == 0)
                        {
                            expressions.Add(text.Substring(firstOpenerIndex, i - firstOpenerIndex + 1));
                        }
                        continue;
                    }
                    else
                    {
                        throw new SearchExpressionParseException($"Missing \"{GetCorrespondingCloser(openersStack.Peek())}\" in \"{text.Substring(0, i + 1)}\"", text.startIndex + firstOpenerIndex, i - firstOpenerIndex + 1);
                    }
                }

                if (openersStack.Count == 0 && text[i] == ',')
                {
                    rootHasParameters = true;
                }
            }
            if (currentStringTokenIndex != -1)
            {
                throw new SearchExpressionParseException($"The string \"{text.Substring(currentStringTokenIndex)}\" is not closed correctly", text.startIndex + currentStringTokenIndex, text.length - currentStringTokenIndex);
            }
            if (openersStack.Any())
            {
                throw new SearchExpressionParseException($"Missing \"{GetCorrespondingCloser(openersStack.Peek())}\" in \"{text}\"", text.startIndex + firstOpenerIndex, text.length - firstOpenerIndex);
            }
            return(expressions.ToArray());
        }
Esempio n. 3
0
        public static bool TryParse(StringView text, out QueryMarker marker)
        {
            marker = none;
            if (!IsQueryMarker(text))
            {
                return(false);
            }

            var innerText = text.Substring(k_StartToken.Length, text.Length - k_StartToken.Length - k_EndToken.Length);

            var indexOfColon = innerText.IndexOf(':');

            if (indexOfColon < 0)
            {
                return(false);
            }

            var controlType     = innerText.Substring(0, indexOfColon).Trim().ToString();
            var args            = new List <StringView>();
            var level           = 0;
            var currentArgStart = indexOfColon + 1;

            for (var i = currentArgStart; i < innerText.Length; ++i)
            {
                if (ParserUtils.IsOpener(innerText[i]))
                {
                    ++level;
                }
                if (ParserUtils.IsCloser(innerText[i]))
                {
                    --level;
                }
                if (innerText[i] != ',' || level != 0)
                {
                    continue;
                }
                if (i + 1 == innerText.Length)
                {
                    return(false);
                }

                args.Add(innerText.Substring(currentArgStart, i - currentArgStart).Trim());
                currentArgStart = i + 1;
            }

            if (currentArgStart == innerText.Length)
            {
                return(false); // No arguments
            }
            // Extract the final argument, since there is no comma after the last argument
            args.Add(innerText.Substring(currentArgStart, innerText.Length - currentArgStart).Trim());

            var queryMarkerArguments = new List <QueryMarkerArgument>();

            using (var context = SearchService.CreateContext(""))
            {
                foreach (var arg in args)
                {
                    var parserArgs = new SearchExpressionParserArgs(arg, context, SearchExpressionParserFlags.ImplicitLiterals);
                    SearchExpression expression = null;
                    try
                    {
                        expression = SearchExpression.Parse(parserArgs);
                    }
                    catch (SearchExpressionParseException)
                    { }

                    if (expression == null || !expression.types.HasAny(SearchExpressionType.Literal))
                    {
                        queryMarkerArguments.Add(new QueryMarkerArgument {
                            rawText = arg, expression = expression, value = expression == null ? arg.ToString() : null
                        });
                        continue;
                    }
                    var results             = expression.Execute(context);
                    var resolvedValue       = results.FirstOrDefault(item => item != null);
                    var resolvedValueObject = resolvedValue?.value;
                    queryMarkerArguments.Add(new QueryMarkerArgument {
                        rawText = arg, expression = expression, value = resolvedValueObject
                    });
                }
            }

            marker = new QueryMarker {
                type = controlType, text = text, args = queryMarkerArguments.ToArray()
            };
            return(true);
        }
        public static StringView[] ExtractArguments(StringView text, string errorPrefix = "")
        {
            var          paramsBlock      = text;
            var          expressionParams = new List <StringView>();
            var          paramStartIndex  = -1;
            var          lastCommaIndex   = -1;
            Stack <char> openersStack     = new Stack <char>();

            openersStack.Push(paramsBlock[0]);
            int currentStringTokenIndex = -1;
            var i = 1;

            for (; i < paramsBlock.length; ++i)
            {
                if (paramsBlock[i] == ' ')
                {
                    continue;
                }
                if (paramStartIndex == -1)
                {
                    paramStartIndex = i;
                }

                // In case of a string, we must find the end of the string before checking any nested levels or ,
                if (k_Quotes.Any(c => c == paramsBlock[i]))
                {
                    if (currentStringTokenIndex == -1)
                    {
                        currentStringTokenIndex = i;
                    }
                    else
                    {
                        currentStringTokenIndex = -1;
                    }
                }
                if (currentStringTokenIndex != -1) // is in string
                {
                    continue;
                }

                if (k_Openers.Any(c => c == paramsBlock[i]))
                {
                    openersStack.Push(paramsBlock[i]);
                    continue;
                }

                if (k_Closers.Any(c => c == paramsBlock[i]))
                {
                    if (CharMatchOpener(openersStack.Peek(), paramsBlock[i]))
                    {
                        openersStack.Pop();
                        // We found the final closer, that means we found the end of the expression
                        if (openersStack.Count == 0)
                        {
                            var startIndex = GetExpressionStartIndex(paramStartIndex, lastCommaIndex);
                            if (i - startIndex != 0)
                            {
                                expressionParams.Add(paramsBlock.Substring(startIndex, i - startIndex).Trim());
                            }
                            else if (lastCommaIndex != -1)
                            {
                                throw new SearchExpressionParseException($"Last argument missing in \"{errorPrefix + text.Substring(0, i + 1)}\"", text.startIndex + lastCommaIndex, i - lastCommaIndex + 1);
                            }
                            ++i;
                            break;
                        }

                        continue;
                    }
                }
                if (paramsBlock[i] == ',' && openersStack.Count == 1)
                {
                    var startIndex = GetExpressionStartIndex(paramStartIndex, lastCommaIndex);
                    if (i - startIndex == 0)
                    {
                        var position = lastCommaIndex == -1 ? 0 : lastCommaIndex;
                        throw new SearchExpressionParseException($"The argument is not defined before \",\" in \"{errorPrefix + text.Substring(0, i)}\"", text.startIndex + position, i - position + 1);
                    }
                    lastCommaIndex  = i;
                    paramStartIndex = -1;
                    expressionParams.Add(paramsBlock.Substring(startIndex, lastCommaIndex - startIndex).Trim());
                }
            }

            return(expressionParams.ToArray());
        }
Esempio n. 5
0
        public static QueryMarker[] ParseAllMarkers(StringView text)
        {
            if (text.Length <= k_StartToken.Length + k_EndToken.Length)
            {
                return(null);
            }

            List <QueryMarker> markers = new List <QueryMarker>();
            var  startIndex            = -1;
            var  endIndex    = -1;
            var  nestedLevel = 0;
            bool inQuotes    = false;

            for (var i = 0; i < text.Length; ++i)
            {
                if (i + k_StartToken.Length > text.Length || i + k_EndToken.Length > text.Length)
                {
                    break;
                }

                if (ParserUtils.IsOpener(text[i]) && !inQuotes)
                {
                    ++nestedLevel;
                }
                if (ParserUtils.IsCloser(text[i]) && !inQuotes)
                {
                    --nestedLevel;
                }
                if (text[i] == '"' || text[i] == '\'')
                {
                    inQuotes = !inQuotes;
                }

                if (startIndex == -1 && nestedLevel == 0 && !inQuotes)
                {
                    var openerSv = text.Substring(i, k_StartToken.Length);
                    if (openerSv == k_StartToken)
                    {
                        startIndex = i;
                    }
                }

                if (endIndex == -1 && nestedLevel == 0 && !inQuotes)
                {
                    var closerSv = text.Substring(i, k_EndToken.Length);
                    if (closerSv == k_EndToken)
                    {
                        endIndex = i + k_EndToken.Length;
                    }
                }

                if (startIndex != -1 && endIndex != -1)
                {
                    var markerSv = text.Substring(startIndex, endIndex - startIndex);
                    if (TryParse(markerSv, out var marker))
                    {
                        markers.Add(marker);
                    }
                    startIndex = -1;
                    endIndex   = -1;
                }
            }

            return(markers.ToArray());
        }