Skip to content

Commit

Permalink
Better error reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
eggrobin committed Jun 25, 2024
1 parent 1e5b44e commit f3a1b4f
Showing 1 changed file with 76 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,12 @@ public static int testInvariants(String inputFile, String suffix, boolean doRang
};
int lastPrintedLine = 0;
final ParsePosition pp = new ParsePosition(0);
boolean followingParseError = false;
for (; ; ) {
final int statementStart = pp.getIndex();
final int statementLineNumber = getLineNumber.apply(pp);
final String nextToken = nextToken(pp, source);
while (statementLineNumber >= lastPrintedLine) {
while (lastPrintedLine < statementLineNumber) {
println(lines.get(lastPrintedLine++));
}
if (nextToken == null) {
Expand All @@ -266,20 +267,35 @@ public static int testInvariants(String inputFile, String suffix, boolean doRang
pp.setIndex(statementStart);
testLine(source, pp, inputFile, getLineNumber);
}
followingParseError = false;
} catch (final Exception e) {
final int lineNumber = getLineNumber.apply(pp);
while (lineNumber > lastPrintedLine) {
println(lines.get(lastPrintedLine++));
if (!followingParseError) {
final int lineNumber = getLineNumber.apply(pp);
while (lineNumber > lastPrintedLine) {
println(lines.get(lastPrintedLine++));
}
parseErrorCount =
parseError(
parseErrorCount,
source,
e,
statementStart,
inputFile,
getLineNumber.apply(pp));
}
// Give up on the whole line, it is unlikely to contain anything we can
// parse.
// Try parsing the next line, but since that may be the rest of what we
// failed to parse,
// do not report errors until we successfully parse *something*.
final int nextLine = source.indexOf("\n", pp.getIndex());
if (nextLine >= 0) {
pp.setIndex(source.indexOf("\n", pp.getIndex()));
followingParseError = true;
continue;
} else {
break;
}
parseErrorCount =
parseError(
parseErrorCount,
source,
e,
statementStart,
inputFile,
getLineNumber.apply(pp));
continue;
}
}
println();
Expand Down Expand Up @@ -359,7 +375,7 @@ private static void propertywiseLine(
throws ParseException {
final UnicodeSet set = parseUnicodeSet(line, pp);
if (set.hasStrings()) {
throw new ParseException(
throw new BackwardParseException(
"Set should contain only single code points for property comparison",
pp.getIndex());
}
Expand Down Expand Up @@ -697,13 +713,9 @@ private static String nextToken(ParsePosition pp, String text) {

private static void expectToken(String token, ParsePosition pp, String text)
throws ParseException {
final var next = new ParsePosition(pp.getIndex());
final String actual = nextToken(next, text);
if (!token.equals(actual)) {
throw new ParseException(
"Expected '" + token + "', got '" + actual + "'", pp.getIndex());
if (!token.equals(nextToken(pp, text))) {
throw new BackwardParseException("Expected '" + token + "'", pp.getIndex());
}
pp.setIndex(next.getIndex());
}

private static PropertyPredicate getPropertyPredicate(ParsePosition pp, String line)
Expand Down Expand Up @@ -761,24 +773,28 @@ enum Type {
private final List<FilterOrProp> propOrFilters = new ArrayList<FilterOrProp>();

static UnicodeProperty of(
UnicodeProperty.Factory propSource, String line, ParsePosition pp) {
UnicodeProperty.Factory propSource, String source, ParsePosition pp)
throws ParseException {
final CompoundProperty result = new CompoundProperty();
while (true) {
scan(PATTERN_WHITE_SPACE, line, pp, true);
if (UnicodeSet.resemblesPattern(line, pp.getIndex())) {
scan(PATTERN_WHITE_SPACE, source, pp, true);
if (UnicodeSet.resemblesPattern(source, pp.getIndex())) {
final FilterOrProp propOrFilter = new FilterOrProp();
propOrFilter.filter = parseUnicodeSet(line, pp);
propOrFilter.filter = parseUnicodeSet(source, pp);
propOrFilter.type = FilterOrProp.Type.filter;
result.propOrFilters.add(propOrFilter);
} else if (line.charAt(pp.getIndex()) == '(') {
} else if (source.charAt(pp.getIndex()) == '(') {
final FilterOrProp propOrFilter = new FilterOrProp();
final var matcher =
Pattern.compile("(\\( *([^ )]+)(?: +([^)]+))? *\\)).*", Pattern.DOTALL)
.matcher(line.substring(pp.getIndex()));
.matcher(source.subSequence(pp.getIndex(), source.length()));
if (!matcher.matches()) {
throw new IllegalArgumentException(
"Expected (<operation> <args>), got "
+ line.substring(pp.getIndex()));
+ source.substring(
pp.getIndex(),
Math.min(pp.getIndex() + 50, source.length()))
+ "…");
}
propOrFilter.type = FilterOrProp.Type.sequenceTransformation;
final String expression = matcher.group(1);
Expand Down Expand Up @@ -851,7 +867,7 @@ static UnicodeProperty of(
result.propOrFilters.add(propOrFilter);
pp.setIndex(pp.getIndex() + expression.length());
} else {
final String propName = scan(PROPCHARS, line, pp, true);
final String propName = scan(PROPCHARS, source, pp, true);
if (propName.length() > 0) {
final FilterOrProp propOrFilter = new FilterOrProp();
final VersionedProperty xprop =
Expand All @@ -872,12 +888,12 @@ static UnicodeProperty of(
break;
}
}
scan(PATTERN_WHITE_SPACE, line, pp, true);
scan(PATTERN_WHITE_SPACE, source, pp, true);
final int pos = pp.getIndex();
if (pos == line.length()) {
if (pos == source.length()) {
break;
}
final int cp = line.charAt(pos);
final int cp = source.charAt(pos);
if (cp != '*') {
break;
}
Expand Down Expand Up @@ -1031,10 +1047,11 @@ private static void letLine(ParsePosition pp, String source) throws ParseExcepti
showSet(new ParsePosition(0), value);
}

private static void showLine(String source, ParsePosition pp) {
private static void showLine(String source, ParsePosition pp) throws ParseException {
final var next = new ParsePosition(pp.getIndex());
if (next.equals("Each")) {
if (nextToken(next, source).equals("Each")) {
showLister.setMergeRanges(false);
pp.setIndex(next.getIndex());
}
showSet(pp, source);
showLister.setMergeRanges(doRange);
Expand Down Expand Up @@ -1344,7 +1361,7 @@ private static void checkExpected(
nf.setGroupingUsed(true);
}

private static void showSet(ParsePosition pp, final String value) {
private static void showSet(ParsePosition pp, final String value) throws ParseException {
UnicodeSet valueSet = parseUnicodeSet(value, pp);
final int totalSize = valueSet.size();
int abbreviated = 0;
Expand Down Expand Up @@ -1397,7 +1414,7 @@ private static int parseError(
final int eol = source.indexOf("\n", index);
source =
source.substring(statementStart, index)
+ "☞"
+ (e instanceof BackwardParseException ? "☜" : "☞")
+ source.substring(index, eol >= 0 ? eol : source.length());
} else {
final int sol = source.lastIndexOf("\n", statementStart);
Expand All @@ -1412,7 +1429,7 @@ private static int parseError(
if (message != null) {
println("##" + message);
}
reportParseError(file, lineNumber, message);
reportParseError(file, lineNumber, message + "\n" + source);
e.printStackTrace(out);

out.println("</pre>");
Expand Down Expand Up @@ -1448,7 +1465,8 @@ private static void printErrorLine(String title, Side side, int testFailureCount

private static final String HTML_RULES_CONTROLS =
HTML_RULES
+ ":: [[:C:][:Z:][:whitespace:][:Default_Ignorable_Code_Point:] - [\\u0020\\u0009]] hex/unicode ; ";
+ ":: [[:C:][:Z:][:whitespace:][:Default_Ignorable_Code_Point:] - [\\u0020\\u0009\\u000A]] hex/unicode ; "
+ "\\u000A > '<br>'";

public static final Transliterator toHTMLControl =
Transliterator.createFromRules("any-html", HTML_RULES_CONTROLS, Transliterator.FORWARD);
Expand Down Expand Up @@ -1634,10 +1652,26 @@ public boolean applyPropertyAlias(
}
}

public static UnicodeSet parseUnicodeSet(String source, ParsePosition pp) {
final var relative = new ParsePosition(0);
final var result = new UnicodeSet(source.substring(pp.getIndex()), relative, symbolTable);
pp.setIndex(pp.getIndex() + relative.getIndex());
return result;
// Some of our parse exceptions are thrown with a parse position before the problem.
// However, others are thrown with the parse position after the problem, so the message must be
// adjusted accordingly.
public static class BackwardParseException extends ParseException {
public BackwardParseException(String s, int errorOffset) {
super(s, errorOffset);
}
}

public static UnicodeSet parseUnicodeSet(String source, ParsePosition pp)
throws ParseException {
try {
final var result = new UnicodeSet(source, pp, symbolTable);
return result;
} catch (IllegalArgumentException e) {
// ICU produces unhelpful messages when parsing UnicodeSet deep into
// a large string in a string that contains line terminators, as the
// whole string is escaped and printed.
final String message = e.getMessage().split(" at \"", 2)[0];
throw new BackwardParseException(message, pp.getIndex());
}
}
}

0 comments on commit f3a1b4f

Please sign in to comment.