public void AddIssue(Format parent, string issue, int startIndex, int endIndex) { Issues.Add(new ParsingIssue(issue, startIndex, endIndex - startIndex)); }
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); }
public ParsingErrors(Format result) { this.result = result; this.Issues = new List <ParsingIssue>(); }