public FormattingInfo(FormattingInfo parent, FormatDetails formatDetails, Placeholder placeholder, object currentValue) { this.Parent = parent; this.FormatDetails = formatDetails; this.Placeholder = placeholder; this.Format = placeholder.Format; this.CurrentValue = currentValue; }
public bool TryEvaluateFormat(IFormattingInfo formattingInfo) { var format = formattingInfo.Format; var current = formattingInfo.CurrentValue; // This method needs the Highest priority so that it comes before the PluralLocalizationExtension and ConditionalExtension // This extension requires at least IEnumerable var enumerable = current as IEnumerable; if (enumerable == null) return false; // Ignore Strings, because they're IEnumerable. // This issue might actually need a solution // for other objects that are IEnumerable. if (current is string) return false; // If the object is IFormattable, ignore it if (current is IFormattable) return false; // This extension requires a | to specify the spacer: if (format == null) return false; var parameters = format.Split('|', 4); if (parameters.Count < 2) return false; // Grab all formatting options: // They must be in one of these formats: // itemFormat|spacer // itemFormat|spacer|lastSpacer // itemFormat|spacer|lastSpacer|twoSpacer var itemFormat = parameters[0]; var spacer = (parameters.Count >= 2) ? parameters[1].GetLiteralText() : ""; var lastSpacer = (parameters.Count >= 3) ? parameters[2].GetLiteralText() : spacer; var twoSpacer = (parameters.Count >= 4) ? parameters[3].GetLiteralText() : lastSpacer; // TODO: [Obsolete] Not necessary, should remove: if (!itemFormat.HasNested) { // The format is not nested, // so we will treat it as an itemFormat: var newItemFormat = new Format(itemFormat.baseString); newItemFormat.startIndex = itemFormat.startIndex; newItemFormat.endIndex = itemFormat.endIndex; newItemFormat.HasNested = true; var newPlaceholder = new Placeholder(newItemFormat, itemFormat.startIndex, 0); newPlaceholder.Format = itemFormat; newPlaceholder.endIndex = itemFormat.endIndex; newItemFormat.Items.Add(newPlaceholder); itemFormat = newItemFormat; } // Let's buffer all items from the enumerable (to ensure the Count without double-enumeration): ICollection items = current as ICollection; if (items == null) { var allItems = new List<object>(); foreach (var item in enumerable) { allItems.Add(item); } items = allItems; } int oldCollectionIndex = CollectionIndex; // In case we have nested arrays, we might need to restore the CollectionIndex CollectionIndex = -1; foreach (object item in items) { CollectionIndex += 1; // Keep track of the index // Determine which spacer to write: if (spacer == null || CollectionIndex == 0) { // Don't write the spacer. } else if (CollectionIndex < items.Count - 1) { formattingInfo.Write(spacer); } else if (CollectionIndex == 1) { formattingInfo.Write(twoSpacer); } else { formattingInfo.Write(lastSpacer); } // Output the nested format for this item: formattingInfo.Write(itemFormat, item); } CollectionIndex = oldCollectionIndex; // Restore the CollectionIndex return true; }
public Format(SmartSettings smartSettings, Placeholder parent, int startIndex) : base(smartSettings, parent, startIndex) { this.parent = parent; Items = new List <FormatItem>(); }
/// <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); }
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; }
/// <summary> /// Initializes the instance of <see cref="Format"/>. /// </summary> /// <param name="smartSettings"></param> /// <param name="parent">The parent <see cref="Placeholder"/>.</param> /// <param name="startIndex">The start index within the format base string.</param> /// <returns>This <see cref="Format"/> instance.</returns> public Format Initialize(SmartSettings smartSettings, Placeholder parent, int startIndex) { base.Initialize(smartSettings, parent, parent.BaseString, startIndex, parent.EndIndex); ParentPlaceholder = parent; return(this); }
public Format(Placeholder parent, int startIndex) : base(parent, startIndex) { this.parent = parent; Items = new List <FormatItem>(); }
public FormattingInfo CreateChild(Placeholder placeholder) { return new FormattingInfo(this, this.FormatDetails, placeholder, this.CurrentValue); }
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); }