예제 #1
0
 internal ParsingErrorEventArgs(ParsingErrors errors, bool throwsException)
 {
     Errors          = errors;
     ThrowsException = throwsException;
 }
예제 #2
0
        public Format ParseFormat(string format, string[] formatterExtensionNames)
        {
            var         result                          = new Format(Settings, format);
            var         current                         = result;
            Placeholder currentPlaceholder              = null;
            var         namedFormatterStartIndex        = -1;
            var         namedFormatterOptionsStartIndex = -1;
            var         namedFormatterOptionsEndIndex   = -1;

            // Store parsing errors until the end:
            var parsingErrors    = new ParsingErrors(result);
            var parsingErrorText = new ParsingErrorText();

            // Cache properties:
            var openingBrace = _openingBrace;
            var closingBrace = _closingBrace;


            var nestedDepth   = 0;
            var lastI         = 0;
            var operatorIndex = 0;
            var selectorIndex = 0;

            for (int i = 0, length = format.Length; i < length; i++)
            {
                var c = format[i];
                if (currentPlaceholder == null)
                {
                    if (c == openingBrace)
                    {
                        // Finish the last text item:
                        if (i != lastI)
                        {
                            current.Items.Add(new LiteralText(Settings, current, lastI)
                            {
                                endIndex = i
                            });
                        }
                        lastI = i + 1;

                        // See if this brace should be escaped:
                        if (!_alternativeEscaping)
                        {
                            var nextI = lastI;
                            if (nextI < length && format[nextI] == openingBrace)
                            {
                                i++;
                                continue;
                            }
                        }

                        // New placeholder:
                        nestedDepth++;
                        currentPlaceholder = new Placeholder(Settings, current, i, nestedDepth);
                        current.Items.Add(currentPlaceholder);
                        current.HasNested        = true;
                        operatorIndex            = i + 1;
                        selectorIndex            = 0;
                        namedFormatterStartIndex = -1;
                    }
                    else if (c == closingBrace)
                    {
                        // Finish the last text item:
                        if (i != lastI)
                        {
                            current.Items.Add(new LiteralText(Settings, current, lastI)
                            {
                                endIndex = i
                            });
                        }
                        lastI = i + 1;

                        // See if this brace should be escaped:
                        if (!_alternativeEscaping)
                        {
                            var nextI = lastI;
                            if (nextI < length && format[nextI] == closingBrace)
                            {
                                i++;
                                continue;
                            }
                        }

                        // Make sure that this is a nested placeholder before we un-nest it:
                        if (current.parent == null)
                        {
                            parsingErrors.AddIssue(current, parsingErrorText[ParsingError.TooManyClosingBraces], i, i + 1);
                            OnParsingFailure?.Invoke(this, new ParsingErrorEventArgs(current.RawText, i, i + 1, ParsingError.TooManyClosingBraces, Settings.ParseErrorAction != ErrorAction.ThrowError));
                            continue;
                        }
                        // End of the placeholder's Format:
                        nestedDepth--;
                        current.endIndex        = i;
                        current.parent.endIndex = i + 1;
                        current = current.parent.parent;
                        namedFormatterStartIndex = -1;
                    }
                    else if ((c == CharLiteralEscapeChar && Settings.ConvertCharacterStringLiterals) || (_alternativeEscaping && c == _alternativeEscapeChar))
                    {
                        namedFormatterStartIndex = -1;

                        // See that is the next character
                        var nextI = i + 1;

                        // **** Alternative brace escaping with { or } following the escape character ****
                        if (nextI < length && (format[nextI] == openingBrace || format[nextI] == closingBrace))
                        {
                            // Finish the last text item:
                            if (i != lastI)
                            {
                                current.Items.Add(new LiteralText(Settings, current, lastI)
                                {
                                    endIndex = i
                                });
                            }
                            lastI = i + 1;

                            i++;
                            continue;
                        }
                        else
                        {
                            // **** Escaping of charater literals like \t, \n, \v etc. ****

                            // Finish the last text item:
                            if (i != lastI)
                            {
                                current.Items.Add(new LiteralText(Settings, current, lastI)
                                {
                                    endIndex = i
                                });
                            }
                            lastI = i + 2;
                            if (lastI > length)
                            {
                                lastI = length;
                            }

                            // Next add the character literal INCLUDING the escape character, which LiteralText will expect
                            current.Items.Add(new LiteralText(Settings, current, i)
                            {
                                endIndex = lastI
                            });

                            i++;
                            continue;
                        }
                    }
                    else if (namedFormatterStartIndex != -1)
                    {
                        if (c == '(')
                        {
                            var emptyName = (namedFormatterStartIndex == i);
                            if (emptyName)
                            {
                                namedFormatterStartIndex = -1;
                                continue;
                            }
                            namedFormatterOptionsStartIndex = i;
                        }
                        else if (c == ')' || c == ':')
                        {
                            if (c == ')')
                            {
                                var hasOpeningParenthesis = (namedFormatterOptionsStartIndex != -1);

                                // ensure no trailing chars past ')'
                                var nextI           = i + 1;
                                var nextCharIsValid = (nextI < format.Length && (format[nextI] == ':' || format[nextI] == closingBrace));

                                if (!hasOpeningParenthesis || !nextCharIsValid)
                                {
                                    namedFormatterStartIndex = -1;
                                    continue;
                                }

                                namedFormatterOptionsEndIndex = i;

                                if (format[nextI] == ':')
                                {
                                    i++; // Consume the ':'
                                }
                            }

                            var nameIsEmpty = (namedFormatterStartIndex == i);
                            var missingClosingParenthesis = (namedFormatterOptionsStartIndex != -1 && namedFormatterOptionsEndIndex == -1);
                            if (nameIsEmpty || missingClosingParenthesis)
                            {
                                namedFormatterStartIndex = -1;
                                continue;
                            }


                            lastI = i + 1;

                            var parentPlaceholder = current.parent;

                            if (namedFormatterOptionsStartIndex == -1)
                            {
                                var formatterName = format.Substring(namedFormatterStartIndex, i - namedFormatterStartIndex);

                                if (FormatterNameExists(formatterName, formatterExtensionNames))
                                {
                                    parentPlaceholder.FormatterName = formatterName;
                                }
                                else
                                {
                                    lastI = current.startIndex;
                                }
                            }
                            else
                            {
                                var formatterName = format.Substring(namedFormatterStartIndex, namedFormatterOptionsStartIndex - namedFormatterStartIndex);

                                if (FormatterNameExists(formatterName, formatterExtensionNames))
                                {
                                    parentPlaceholder.FormatterName    = formatterName;
                                    parentPlaceholder.FormatterOptions = format.Substring(namedFormatterOptionsStartIndex + 1, namedFormatterOptionsEndIndex - (namedFormatterOptionsStartIndex + 1));
                                }
                                else
                                {
                                    lastI = current.startIndex;
                                }
                            }
                            current.startIndex = lastI;

                            namedFormatterStartIndex = -1;
                        }
                    }
                }
                else
                {
                    // Placeholder is NOT null, so that means
                    // we're parsing the selectors:
                    if (_operators.IndexOf(c) != -1)
                    {
                        // Add the selector:
                        if (i != lastI)
                        {
                            currentPlaceholder.Selectors.Add(new Selector(Settings, format, lastI, i, operatorIndex, selectorIndex));
                            selectorIndex++;
                            operatorIndex = i;
                        }

                        lastI = i + 1;
                    }
                    else if (c == ':')
                    {
                        // Add the selector:
                        if (i != lastI)
                        {
                            currentPlaceholder.Selectors.Add(new Selector(Settings, format, lastI, i, operatorIndex, selectorIndex));
                        }
                        else if (operatorIndex != i)
                        {
                            // There are trailing operators. For now, this is an error.
                            parsingErrors.AddIssue(current, parsingErrorText[ParsingError.TrailingOperatorsInSelector], operatorIndex, i);
                            OnParsingFailure?.Invoke(this, new ParsingErrorEventArgs(current.RawText, operatorIndex, i + 1, ParsingError.TrailingOperatorsInSelector, Settings.ParseErrorAction != ErrorAction.ThrowError));
                        }
                        lastI = i + 1;

                        // Start the format:
                        currentPlaceholder.Format = new Format(Settings, currentPlaceholder, i + 1);
                        current                         = currentPlaceholder.Format;
                        currentPlaceholder              = null;
                        namedFormatterStartIndex        = lastI;
                        namedFormatterOptionsStartIndex = -1;
                        namedFormatterOptionsEndIndex   = -1;
                    }
                    else if (c == closingBrace)
                    {
                        // Add the selector:
                        if (i != lastI)
                        {
                            currentPlaceholder.Selectors.Add(new Selector(Settings, format, lastI, i, operatorIndex, selectorIndex));
                        }
                        else if (operatorIndex != i)
                        {
                            // There are trailing operators.  For now, this is an error.
                            parsingErrors.AddIssue(current, parsingErrorText[ParsingError.TrailingOperatorsInSelector], operatorIndex, i);
                            OnParsingFailure?.Invoke(this, new ParsingErrorEventArgs(current.RawText, operatorIndex, i, ParsingError.TrailingOperatorsInSelector, Settings.ParseErrorAction != ErrorAction.ThrowError));
                        }
                        lastI = i + 1;

                        // End the placeholder with no format:
                        nestedDepth--;
                        currentPlaceholder.endIndex = i + 1;
                        current            = currentPlaceholder.parent;
                        currentPlaceholder = null;
                    }
                    else
                    {
                        // Let's make sure the selector characters are valid:
                        // Make sure it's alphanumeric:
                        var isValidSelectorChar =
                            ('0' <= c && c <= '9') ||
                            (_alphanumericSelectors && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z')) ||
                            (_allowedSelectorChars.IndexOf(c) != -1);
                        if (!isValidSelectorChar)
                        {
                            // Invalid character in the selector.
                            parsingErrors.AddIssue(current, parsingErrorText[ParsingError.InvalidCharactersInSelector], i, i + 1);
                            OnParsingFailure?.Invoke(this, new ParsingErrorEventArgs(current.RawText, i, i + 1, ParsingError.InvalidCharactersInSelector, Settings.ParseErrorAction != ErrorAction.ThrowError));
                        }
                    }
                }
            }

            // finish the last text item:
            if (lastI != format.Length)
            {
                current.Items.Add(new LiteralText(Settings, current, lastI)
                {
                    endIndex = format.Length
                });
            }

            // Check that the format is finished:
            if (current.parent != null || currentPlaceholder != null)
            {
                parsingErrors.AddIssue(current, parsingErrorText[ParsingError.MissingClosingBrace], format.Length, format.Length);
                OnParsingFailure?.Invoke(this, new ParsingErrorEventArgs(current.RawText, format.Length, format.Length, ParsingError.MissingClosingBrace, Settings.ParseErrorAction != ErrorAction.ThrowError));
                current.endIndex = format.Length;
                while (current.parent != null)
                {
                    current          = current.parent.parent;
                    current.endIndex = format.Length;
                }
            }

            // Check if there were any parsing errors:
            if (parsingErrors.HasIssues && Settings.ParseErrorAction == ErrorAction.ThrowError)
            {
                throw parsingErrors;
            }

            return(result);
        }
예제 #3
0
        public Format ParseFormat(string format)
        {
            var         result                          = new Format(format);
            var         current                         = result;
            Placeholder currentPlaceholder              = null;
            var         namedFormatterStartIndex        = -1;
            var         namedFormatterOptionsStartIndex = -1;
            var         namedFormatterOptionsEndIndex   = -1;

            // Store parsing errors until the end:
            var parsingErrors = new ParsingErrors(result);

            // Cache properties:
            var openingBrace = this.openingBrace;
            var closingBrace = this.closingBrace;


            int nestedDepth   = 0;
            int lastI         = 0;
            int operatorIndex = 0;
            int selectorIndex = 0;

            for (int i = 0, length = format.Length; i < length; i++)
            {
                var c = format[i];
                if (currentPlaceholder == null)
                {
                    if (c == openingBrace)
                    {
                        // Finish the last text item:
                        if (i != lastI)
                        {
                            current.Items.Add(new LiteralText(current, lastI)
                            {
                                endIndex = i
                            });
                        }
                        lastI = i + 1;

                        // See if this brace should be escaped:
                        if (!this.alternativeEscaping)
                        {
                            var nextI = lastI;
                            if (nextI < length && format[nextI] == openingBrace)
                            {
                                i++;
                                continue;
                            }
                        }

                        // New placeholder:
                        nestedDepth++;
                        currentPlaceholder = new Placeholder(current, i, nestedDepth);
                        current.Items.Add(currentPlaceholder);
                        current.HasNested        = true;
                        operatorIndex            = i + 1;
                        selectorIndex            = 0;
                        namedFormatterStartIndex = -1;
                    }
                    else if (c == closingBrace)
                    {
                        // Finish the last text item:
                        if (i != lastI)
                        {
                            current.Items.Add(new LiteralText(current, lastI)
                            {
                                endIndex = i
                            });
                        }
                        lastI = i + 1;

                        // See if this brace should be escaped:
                        if (!this.alternativeEscaping)
                        {
                            var nextI = lastI;
                            if (nextI < length && format[nextI] == closingBrace)
                            {
                                i++;
                                continue;
                            }
                        }

                        // Make sure that this is a nested placeholder before we un-nest it:
                        if (current.parent == null)
                        {
                            parsingErrors.AddIssue(current, "Format string has too many closing braces", i, i + 1);
                            continue;
                        }
                        // End of the placeholder's Format:
                        nestedDepth--;
                        current.endIndex        = i;
                        current.parent.endIndex = i + 1;
                        current = current.parent.parent;
                        namedFormatterStartIndex = -1;
                    }
                    else if (this.alternativeEscaping && c == this.alternativeEscapeChar)
                    {
                        namedFormatterStartIndex = -1;
                        // See if the next char is a brace that should be escaped:
                        var nextI = i + 1;
                        if (nextI < length && (format[nextI] == openingBrace || format[nextI] == closingBrace))
                        {
                            // Finish the last text item:
                            if (i != lastI)
                            {
                                current.Items.Add(new LiteralText(current, lastI)
                                {
                                    endIndex = i
                                });
                            }
                            lastI = i + 1;

                            i++;
                            continue;
                        }
                    }
                    else if (namedFormatterStartIndex != -1)
                    {
                        if (c == '(')
                        {
                            var emptyName = (namedFormatterStartIndex == i);
                            if (emptyName)
                            {
                                namedFormatterStartIndex = -1;
                                continue;
                            }
                            namedFormatterOptionsStartIndex = i;
                        }
                        else if (c == ')' || c == ':')
                        {
                            if (c == ')')
                            {
                                var hasOpeningParenthesis = (namedFormatterOptionsStartIndex != -1);

                                // ensure no trailing chars past ')'
                                var nextI           = i + 1;
                                var nextCharIsValid = (nextI < format.Length && (format[nextI] == ':' || format[nextI] == closingBrace));

                                if (!hasOpeningParenthesis || !nextCharIsValid)
                                {
                                    namedFormatterStartIndex = -1;
                                    continue;
                                }

                                namedFormatterOptionsEndIndex = i;

                                if (format[nextI] == ':')
                                {
                                    i++;                                     // Consume the ':'
                                }
                            }

                            var nameIsEmpty = (namedFormatterStartIndex == i);
                            var missingClosingParenthesis = (namedFormatterOptionsStartIndex != -1 && namedFormatterOptionsEndIndex == -1);
                            if (nameIsEmpty || missingClosingParenthesis)
                            {
                                namedFormatterStartIndex = -1;
                                continue;
                            }


                            lastI = i + 1;

                            var parentPlaceholder = current.parent;
                            if (namedFormatterOptionsStartIndex == -1)
                            {
                                parentPlaceholder.FormatterName = format.Substring(namedFormatterStartIndex, i - namedFormatterStartIndex);
                            }
                            else
                            {
                                parentPlaceholder.FormatterName    = format.Substring(namedFormatterStartIndex, namedFormatterOptionsStartIndex - namedFormatterStartIndex);
                                parentPlaceholder.FormatterOptions = format.Substring(namedFormatterOptionsStartIndex + 1, namedFormatterOptionsEndIndex - (namedFormatterOptionsStartIndex + 1));
                            }
                            current.startIndex = lastI;

                            namedFormatterStartIndex = -1;
                        }
                    }
                }
                else
                {
                    // Placeholder is NOT null, so that means
                    // we're parsing the selectors:
                    if (operators.IndexOf(c) != -1)
                    {
                        // Add the selector:
                        if (i != lastI)
                        {
                            currentPlaceholder.Selectors.Add(new Selector(format, lastI, i, operatorIndex, selectorIndex));
                            selectorIndex++;
                            operatorIndex = i;
                        }

                        lastI = i + 1;
                    }
                    else if (c == ':')
                    {
                        // Add the selector:
                        if (i != lastI)
                        {
                            currentPlaceholder.Selectors.Add(new Selector(format, lastI, i, operatorIndex, selectorIndex));
                        }
                        else if (operatorIndex != i)
                        {
                            // There are trailing operators.  For now, this is an error.
                            parsingErrors.AddIssue(current, "There are trailing operators in the selector", operatorIndex, i);
                        }
                        lastI = i + 1;

                        // Start the format:
                        currentPlaceholder.Format = new Format(currentPlaceholder, i + 1);
                        current                         = currentPlaceholder.Format;
                        currentPlaceholder              = null;
                        namedFormatterStartIndex        = lastI;
                        namedFormatterOptionsStartIndex = -1;
                        namedFormatterOptionsEndIndex   = -1;
                    }
                    else if (c == closingBrace)
                    {
                        // Add the selector:
                        if (i != lastI)
                        {
                            currentPlaceholder.Selectors.Add(new Selector(format, lastI, i, operatorIndex, selectorIndex));
                        }
                        else if (operatorIndex != i)
                        {
                            // There are trailing operators.  For now, this is an error.
                            parsingErrors.AddIssue(current, "There are trailing operators in the selector", operatorIndex, i);
                        }
                        lastI = i + 1;

                        // End the placeholder with no format:
                        nestedDepth--;
                        currentPlaceholder.endIndex = i + 1;
                        current            = currentPlaceholder.parent;
                        currentPlaceholder = null;
                    }
                    else
                    {
                        // Let's make sure the selector characters are valid:
                        // Make sure it's alphanumeric:
                        var isValidSelectorChar =
                            ('0' <= c && c <= '9') ||
                            (alphanumericSelectors && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z')) ||
                            (allowedSelectorChars.IndexOf(c) != -1);
                        if (!isValidSelectorChar)
                        {
                            // Invalid character in the selector.
                            parsingErrors.AddIssue(current, "Invalid character in the selector", i, i + 1);
                        }
                    }
                }
            }

            // finish the last text item:
            if (lastI != format.Length)
            {
                current.Items.Add(new LiteralText(current, lastI)
                {
                    endIndex = format.Length
                });
            }

            // Check that the format is finished:
            if (current.parent != null || currentPlaceholder != null)
            {
                parsingErrors.AddIssue(current, "Format string is missing a closing brace", format.Length, format.Length);
                current.endIndex = format.Length;
                while (current.parent != null)
                {
                    current          = current.parent.parent;
                    current.endIndex = format.Length;
                }
            }

            // Check if there were any parsing errors:
            if (parsingErrors.HasIssues && ErrorAction == ErrorAction.ThrowError)
            {
                throw parsingErrors;
            }

            return(result);
        }