internal ParsingErrorEventArgs(ParsingErrors errors, bool throwsException) { Errors = errors; ThrowsException = throwsException; }
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); }
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); }