Esempio n. 1
0
        /// <summary>
        /// Handles <see cref="ParsingError"/>s as defined in <see cref="SmartSettings.ParseErrorAction"/>,
        /// which leads to results similar to <see cref="SmartSettings.FormatErrorAction"/>s
        /// </summary>
        /// <param name="parsingErrors"></param>
        /// <param name="currentResult"></param>
        /// <returns>The <see cref="Format"/> which will be further processed with formatting.</returns>
        private Format HandleParsingErrors(ParsingErrors parsingErrors, Format currentResult)
        {
            switch (Settings.ParseErrorAction)
            {
            case ErrorAction.ThrowError:
                throw parsingErrors;

            case ErrorAction.MaintainTokens:
                var fmt = new Format(Settings, currentResult.baseString)
                {
                    startIndex = 0,
                    endIndex   = currentResult.baseString.Length
                };
                fmt.Items.Add(new LiteralText(Settings, fmt));
                return(fmt);

            case ErrorAction.Ignore:
                return(new Format(Settings, string.Empty));

            case ErrorAction.OutputErrorInResult:
                fmt = new Format(Settings, parsingErrors.Message)
                {
                    startIndex = 0,
                    endIndex   = parsingErrors.Message.Length
                };
                fmt.Items.Add(new LiteralText(Settings, fmt));
                return(fmt);

            default:
                return(currentResult);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Handles <see cref="ParsingError"/>s as defined in <see cref="SmartSettings.ParseErrorAction"/>.
        /// </summary>
        /// <param name="parsingErrors"></param>
        /// <param name="currentResult"></param>
        /// <returns>The <see cref="Format"/> which will be further processed by the formatter.</returns>
        private Format HandleParsingErrors(ParsingErrors parsingErrors, Format currentResult)
        {
            switch (Settings.ParseErrorAction)
            {
            case ErrorAction.ThrowError:
                throw parsingErrors;

            case ErrorAction.MaintainTokens:
                // Replace erroneous Placeholders with tokens as LiteralText
                // Placeholder without issues are left unmodified
                for (var i = 0; i < currentResult.Items.Count; i++)
                {
                    if (currentResult.Items[i] is Placeholder ph && parsingErrors.Issues.Any(errItem => errItem.Index >= currentResult.Items[i].startIndex && errItem.Index <= currentResult.Items[i].endIndex))
                    {
                        currentResult.Items[i] = new LiteralText(Settings, ph.Format ?? new Format(Settings, ph.baseString), ph.startIndex)
                        {
                            endIndex = ph.endIndex
                        };
                    }
                }
                return(currentResult);

            case ErrorAction.Ignore:
                // Replace erroneous Placeholders with an empty LiteralText
                for (var i = 0; i < currentResult.Items.Count; i++)
                {
                    if (currentResult.Items[i] is Placeholder ph && parsingErrors.Issues.Any(errItem => errItem.Index >= currentResult.Items[i].startIndex && errItem.Index <= currentResult.Items[i].endIndex))
                    {
                        currentResult.Items[i] = new LiteralText(Settings, ph.Format ?? new Format(Settings, ph.baseString), ph.startIndex)
                        {
                            endIndex = ph.startIndex
                        };
                    }
                }
                return(currentResult);

            case ErrorAction.OutputErrorInResult:
                var fmt = new Format(Settings, parsingErrors.Message)
                {
                    startIndex = 0,
                    endIndex   = parsingErrors.Message.Length
                };
                fmt.Items.Add(new LiteralText(Settings, fmt));
                return(fmt);

            default:
                throw new ArgumentException("Illegal type for ParsingErrors", parsingErrors);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Parses a format string.
        /// </summary>
        /// <param name="format"></param>
        /// <param name="formatterExtensionNames"></param>
        /// <returns>Returns the <see cref="Format"/> for the parsed string.</returns>
        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);
                            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++;
                        }
                        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++;
                        }
                    }
                    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))
                                {
                                    if (parentPlaceholder != null)
                                    {
                                        parentPlaceholder.FormatterName = formatterName;
                                    }
                                }
                                else
                                {
                                    lastI = current.startIndex;
                                }
                            }
                            else
                            {
                                var formatterName = format.Substring(namedFormatterStartIndex,
                                                                     namedFormatterOptionsStartIndex - namedFormatterStartIndex);

                                if (FormatterNameExists(formatterName, formatterExtensionNames))
                                {
                                    if (parentPlaceholder != null)
                                    {
                                        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)
                        {
                            parsingErrors.AddIssue(current, parsingErrorText[ParsingError.TrailingOperatorsInSelector],
                                                   operatorIndex, i);
                        }
                        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)
                        {
                            parsingErrors.AddIssue(current, parsingErrorText[ParsingError.TrailingOperatorsInSelector],
                                                   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)
                        {
                            parsingErrors.AddIssue(current, parsingErrorText[ParsingError.InvalidCharactersInSelector],
                                                   i, i + 1);
                        }
                    }
                }
            }

            // 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);
                current.endIndex = format.Length;
                while (current.parent != null)
                {
                    current          = current.parent.parent;
                    current.endIndex = format.Length;
                }
            }

            // Check for any parsing errors:
            if (parsingErrors.HasIssues)
            {
                OnParsingFailure?.Invoke(this,
                                         new ParsingErrorEventArgs(parsingErrors, Settings.ParseErrorAction == ErrorAction.ThrowError));

                return(HandleParsingErrors(parsingErrors, result));
            }

            return(result);
        }
Esempio n. 4
0
		public Format ParseFormat(string format, string[] formatterExtensionNames)
		{
			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);
			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(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(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 (!_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, ErrorAction != 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 (_alternativeEscaping && c == _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)
							{
								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(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, parsingErrorText[ParsingError.TrailingOperatorsInSelector], operatorIndex, i);
							OnParsingFailure?.Invoke(this, new ParsingErrorEventArgs(current.RawText, operatorIndex, i + 1, ParsingError.TrailingOperatorsInSelector, ErrorAction != ErrorAction.ThrowError));
						}
						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, parsingErrorText[ParsingError.TrailingOperatorsInSelector], operatorIndex, i);
							OnParsingFailure?.Invoke(this, new ParsingErrorEventArgs(current.RawText, operatorIndex, i, ParsingError.TrailingOperatorsInSelector, ErrorAction != 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, ErrorAction != ErrorAction.ThrowError));
						}
					}
				}
			}

			// 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, parsingErrorText[ParsingError.MissingClosingBrace], format.Length, format.Length);
				OnParsingFailure?.Invoke(this, new ParsingErrorEventArgs(current.RawText, format.Length, format.Length, ParsingError.MissingClosingBrace, ErrorAction != 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 && ErrorAction == ErrorAction.ThrowError) throw parsingErrors;

			return result;
		}
 internal ParsingErrorEventArgs(ParsingErrors errors, bool throwsException)
 {
     Errors          = errors;
     ThrowsException = throwsException;
 }
Esempio n. 6
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);
        }