Browse Source

[Utils] CompositeFormatStringParser: More error handling + refactoring of the code.

newNRvisualizers
Simon Lindgren 13 years ago
parent
commit
101294e394
  1. 35
      ICSharpCode.NRefactory.Tests/Utils/CompositeFormatStringParser/CompositeFormatStringParserTests.cs
  2. 157
      ICSharpCode.NRefactory/Utils/CompositeFormatStringParser/CompositeFormatStringParser.cs

35
ICSharpCode.NRefactory.Tests/Utils/CompositeFormatStringParser/CompositeFormatStringParserTests.cs

@ -170,6 +170,15 @@ namespace ICSharpCode.NRefactory.Utils
ErrorTest(errors[0], "{", "{{", 0, 1); ErrorTest(errors[0], "{", "{{", 0, 1);
} }
[Test]
public void UnescapedOpenBracesInFixedText()
{
var segments = ParseTest("a { { a", new TextSegment("a { { a"));
var errors = SegmentTest(2, segments.First());
ErrorTest(errors[0], "{", "{{", 2, 3);
ErrorTest(errors[1], "{", "{{", 4, 5);
}
[Test] [Test]
public void UnescapedLoneEndingBrace() public void UnescapedLoneEndingBrace()
{ {
@ -193,9 +202,10 @@ namespace ICSharpCode.NRefactory.Utils
{ {
var segments = ParseTest("Some text {0,", var segments = ParseTest("Some text {0,",
new TextSegment("Some text "), new TextSegment("Some text "),
new FormatItem(0) { StartLocation = 10, EndLocation = 13 }); new FormatItem(0, 0) { StartLocation = 10, EndLocation = 13 });
var errors = SegmentTest(1, segments.Skip(1).First()); var errors = SegmentTest(2, segments.Skip(1).First());
ErrorTest(errors[0], ",", "}", 12, 13); ErrorTest(errors[0], "", "0", 13, 13);
ErrorTest(errors[1], "", "}", 13, 13);
} }
[Test] [Test]
@ -203,9 +213,10 @@ namespace ICSharpCode.NRefactory.Utils
{ {
var segments = ParseTest("Some text {0, ", var segments = ParseTest("Some text {0, ",
new TextSegment("Some text "), new TextSegment("Some text "),
new FormatItem(0) { StartLocation = 10, EndLocation = 16 }); new FormatItem(0, 0) { StartLocation = 10, EndLocation = 16 });
var errors = SegmentTest(1, segments.Skip(1).First()); var errors = SegmentTest(2, segments.Skip(1).First());
ErrorTest(errors[0], ", ", "}", 12, 16); ErrorTest(errors[0], "", "0", 16, 16);
ErrorTest(errors[1], "", "}", 16, 16);
} }
[Test] [Test]
@ -321,11 +332,19 @@ namespace ICSharpCode.NRefactory.Utils
var segments = ParseTest("Text {0 Text {1}", var segments = ParseTest("Text {0 Text {1}",
new TextSegment("Text "), new TextSegment("Text "),
new FormatItem(0) { StartLocation = 5, EndLocation = 7 }, new FormatItem(0) { StartLocation = 5, EndLocation = 7 },
new TextSegment(" Text ", 7), new TextSegment(" Text ", 7),
new FormatItem(1) { StartLocation = 13, EndLocation = 16 }); new FormatItem(1) { StartLocation = 13, EndLocation = 16 });
var errors = SegmentTest(1, segments.Skip(1).First()); var errors = SegmentTest(1, segments.Skip(1).First());
ErrorTest(errors[0], "", "}", 7, 7); ErrorTest(errors[0], "", "}", 7, 7);
} }
[Test]
public void EndWithEscapedBrace()
{
var segments = ParseTest("{0:}}", new FormatItem(0, null, "}") { StartLocation = 0, EndLocation = 5 });
var errors = SegmentTest(1, segments.First());
ErrorTest(errors[0], "", "}", 5, 5);
}
} }
} }

157
ICSharpCode.NRefactory/Utils/CompositeFormatStringParser/CompositeFormatStringParser.cs

@ -25,7 +25,6 @@
// THE SOFTWARE. // THE SOFTWARE.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
namespace ICSharpCode.NRefactory.Utils namespace ICSharpCode.NRefactory.Utils
@ -59,76 +58,74 @@ namespace ICSharpCode.NRefactory.Utils
var result = new FormatStringParseResult(); var result = new FormatStringParseResult();
// Format string syntax: http://msdn.microsoft.com/en-us/library/txafckwd.aspx // Format string syntax: http://msdn.microsoft.com/en-us/library/txafckwd.aspx
int start = 0; int textStart = 0;
var length = format.Length; var length = format.Length;
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
if (format [i] == '{') { // Get fixed text
if (i + 1 == length) { GetText (format, ref i);
// This is the end of the string.
var textSegment = new TextSegment (format.Substring (start, i - start + 1), start) {
Errors = {
new DefaultFormatStringError {
StartLocation = i,
EndLocation = i + 1,
Message = "Curly braces need to be escaped",
OriginalText = "{",
SuggestedReplacementText = "{{"
}
}
};
result.Segments.Add(textSegment);
return result;
} else if (format [i + 1] == '{') {
// Escape sequence; we're still in a text segment
// Skip ahead to the char after the escape sequence
++i;
continue;
} else {
// This is the end of the text segment and the start of a FormatItem
if (i - start > 0) {
result.Segments.Add(new TextSegment (UnEscape (format.Substring (start, i - start))));
start = i;
}
}
if (i < format.Length && format [i] == '{') {
int formatItemStart = i;
int index; int index;
int? alignment = null; int? alignment = null;
string argumentFormat = null; string argumentFormat = null;
var textSegmentErrors = new List<IFormatStringError>(GetErrors());
// Index // Try to parse the parts of the format item
++i; ++i;
index = ParseIndex(format, ref i); index = ParseIndex (format, ref i);
CheckForMissingEndBrace (format, i, length); CheckForMissingEndBrace (format, i, length);
// Alignment alignment = ParseAlignment (format, ref i, length);
alignment = ParseAlignment(format, ref i, length);
CheckForMissingEndBrace (format, i, length); CheckForMissingEndBrace (format, i, length);
// Format string argumentFormat = ParseSubFormatString (format, ref i, length);
argumentFormat = ParseSubFormatString(format, ref i, length);
CheckForMissingEndBrace (format, i, length); CheckForMissingEndBrace (format, i, length);
// Handle unclosed format items in the middle of fixed text // Check what we parsed
if (i < length && format[i] != '}') if (i == formatItemStart + 1 && (i == length || (i < length && format [i] != '}'))) {
// There were no format item after all, this was just an
// unescaped left brace
SetErrors(textSegmentErrors);
AddError (new DefaultFormatStringError {
Message = "Unescaped '{'",
StartLocation = formatItemStart,
EndLocation = formatItemStart + 1,
OriginalText = "{",
SuggestedReplacementText = "{{"
});
continue;
} else if (formatItemStart - textStart > 0) {
// We have parsed a format item, end the text segment
var textSegment = new TextSegment (UnEscape (format.Substring (textStart, formatItemStart - textStart)));
textSegment.Errors = textSegmentErrors;
result.Segments.Add (textSegment);
}
// Unclosed format items in fixed text gets advances i one step too far
if (i < length && format [i] != '}')
--i; --i;
// i may actually point outside of format; if that happens, we want the last position // i may actually point outside of format if there is a syntactical error
// if that happens, we want the last position
var endLocation = Math.Min (length, i + 1); var endLocation = Math.Min (length, i + 1);
var errors = GetErrors (); result.Segments.Add (new FormatItem (index, alignment, argumentFormat) {
result.Segments.Add(new FormatItem (index, alignment, argumentFormat) { StartLocation = formatItemStart,
StartLocation = start,
EndLocation = endLocation, EndLocation = endLocation,
Errors = errors Errors = GetErrors ()
}); });
ClearErrors (); ClearErrors ();
// The next potential text segment starts after this format item // The next potential text segment starts after this format item
start = i + 1; textStart = i + 1;
} }
} }
// Handle remaining text // Handle remaining text
if (start < length) { if (textStart < length) {
result.Segments.Add(new TextSegment (UnEscape (format.Substring (start)), start)); var textSegment = new TextSegment (UnEscape (format.Substring (textStart)), textStart);
textSegment.Errors = GetErrors();
result.Segments.Add (textSegment);
} }
return result; return result;
} }
@ -156,24 +153,19 @@ namespace ICSharpCode.NRefactory.Utils
++i; ++i;
while (i < length && char.IsWhiteSpace(format [i])) while (i < length && char.IsWhiteSpace(format [i]))
++i; ++i;
if (i == length) {
var originalText = format.Substring (alignmentBegin); int parsedCharacters;
var message = string.Format ("Unexpected end of string: '{0}'", originalText); var number = GetAndCheckNumber (format, ",:}", ref i, alignmentBegin + 1, out parsedCharacters);
AddMissingEndBraceError(alignmentBegin, i, message, originalText); if (parsedCharacters == 0) {
} else { AddError (new DefaultFormatStringError {
int parsedCharacters; StartLocation = i,
var number = GetAndCheckNumber(format, ",:}", ref i, alignmentBegin + 1, out parsedCharacters); EndLocation = i,
if (parsedCharacters == 0) { Message = "Missing alignment",
AddError (new DefaultFormatStringError { OriginalText = "",
StartLocation = i, SuggestedReplacementText = "0"
EndLocation = i, });
Message = "Missing alignment",
OriginalText = "",
SuggestedReplacementText = "0"
});
}
return number ?? 0;
} }
return number ?? 0;
} }
return null; return null;
} }
@ -183,7 +175,8 @@ namespace ICSharpCode.NRefactory.Utils
if (i < length && format [i] == ':') { if (i < length && format [i] == ':') {
++i; ++i;
int begin = i; int begin = i;
while (i < length) { GetText(format, ref i);
/*while (i < length) {
char c = format [i]; char c = format [i];
if (c != '}') { if (c != '}') {
++i; ++i;
@ -197,7 +190,7 @@ namespace ICSharpCode.NRefactory.Utils
// This is the end of the FormatItem // This is the end of the FormatItem
break; break;
} }
} }*/
var escaped = format.Substring (begin, i - begin); var escaped = format.Substring (begin, i - begin);
return UnEscape (escaped); return UnEscape (escaped);
} }
@ -206,23 +199,31 @@ namespace ICSharpCode.NRefactory.Utils
void CheckForMissingEndBrace (string format, int i, int length) void CheckForMissingEndBrace (string format, int i, int length)
{ {
if (i == length && format [length - 1] != '}') { if (i == length) {// && format [length - 1] != '}') {
AddMissingEndBraceError(i, i, "Missing '}'", ""); int j;
for (j = i - 1; format[j] == '}'; j--);
var oddEndBraceCount = (i - j) % 2 == 1;
if (oddEndBraceCount) {
AddMissingEndBraceError(i, i, "Missing '}'", "");
}
return; return;
} }
return; return;
} }
string GetText (string format, string delimiters, ref int index) void GetText (string format, ref int index, string delimiters = "")
{ {
int start = index; while (index < format.Length) {// && !delimiters.Contains(format[index].ToString())) {
while (index < format.Length && !delimiters.Contains(format[index].ToString())) { if (format [index] == '{' || format[index] == '}') {
if (format[index] == '{' && (index + 1 < format.Length && format[index + 1] != '{')) if (index + 1 < format.Length && format [index + 1] == format[index])
++index;
else
break;
} else if (delimiters.Contains(format[index].ToString())) {
break; break;
}
++index; ++index;
} };
return format.Substring (start, index - start);
} }
int? GetNumber (string format, ref int index) int? GetNumber (string format, ref int index)
@ -250,8 +251,9 @@ namespace ICSharpCode.NRefactory.Utils
int? GetAndCheckNumber (string format, string delimiters, ref int index, int numberFieldStart, out int parsedCharacters) int? GetAndCheckNumber (string format, string delimiters, ref int index, int numberFieldStart, out int parsedCharacters)
{ {
int fieldIndex = index; int fieldIndex = index;
var numberText = GetText (format, delimiters, ref fieldIndex); GetText (format, ref fieldIndex, delimiters);
int fieldEnd = fieldIndex; int fieldEnd = fieldIndex;
var numberText = format.Substring(index, fieldEnd - index);
parsedCharacters = numberText.Length; parsedCharacters = numberText.Length;
int numberLength = 0; int numberLength = 0;
int? number = GetNumber (numberText, ref numberLength); int? number = GetNumber (numberText, ref numberLength);
@ -317,6 +319,11 @@ namespace ICSharpCode.NRefactory.Utils
{ {
return errors; return errors;
} }
void SetErrors (IList<IFormatStringError> errors)
{
this.errors = errors;
}
void ClearErrors () void ClearErrors ()
{ {

Loading…
Cancel
Save