[muscript] Generate error data in create method, not in toString

This commit is contained in:
Johannes Frohnmeyer 2022-06-13 11:06:01 +02:00
parent ed680f57a9
commit a9cd45c39d
Signed by: Johannes
GPG Key ID: E76429612C2929F4
3 changed files with 34 additions and 19 deletions

View File

@ -3,29 +3,44 @@ package io.gitlab.jfronny.muscript.compiler;
/**
* Class for storing errors produced while parsing.
*/
public record Error(String source, int character, String message) {
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (character >= source.length()) {
builder.append("Error at character ").append(character);
}
else {
builder.append("Error at '").append(source.charAt(character)).append("' (character ").append(character).append(")");
}
builder.append(": ").append(message);
public record Error(String message, String line, int lineNumber, int column) {
/**
* Generates an error by getting the relevant data from what is available while lexing/parsing
* @param source The complete source for the code that generated this error
* @param character The index of the current character
* @param message The error message
* @return An error using the provided information
*/
public static Error create(String source, int character, String message) {
int lineStart = source.lastIndexOf('\n', character);
int lineEnd = source.indexOf('\n', character);
if (lineEnd == -1) lineEnd = source.length();
String line = source.substring(lineStart + 1, lineEnd);
int lineNumber = lineStart > 0 ? (int) source.substring(0, lineStart).chars().filter(c -> c == '\n').count() : 1;
int lineIndex = lineStart > 0 ? (int) source.substring(0, lineStart).chars().filter(c -> c == '\n').count() : 0;
int column = character - lineStart;
return new Error(message, source.substring(lineStart + 1, lineEnd), lineIndex + 1, column);
}
/**
* Converts this error to a human-readable error message
* @return A string representation
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (column > line.length())
builder.append("Error at character ").append(column);
else
builder.append("Error at '").append(line.charAt(column - 1)).append("' (character ").append(column).append(")");
builder.append(": ").append(message);
String linePrefix = String.format("%1$6d", lineNumber) + " | ";
builder.append('\n').append(linePrefix).append(line);
builder.append('\n').append(" ".repeat(linePrefix.length() + (character - lineStart - 1))).append("^-- Here");
builder.append('\n').append(" ".repeat(linePrefix.length() + (column - 1))).append("^-- Here");
return builder.toString();
}
}

View File

@ -230,7 +230,7 @@ public class Parser {
// Helpers
private ParseException error(String message) {
return new ParseException(new Error(lexer.source, current.current - 1, message));
return new ParseException(Error.create(lexer.source, current.current - 1, message));
}
private TokenData consume(Token token, String message) {

View File

@ -9,17 +9,17 @@ public class ErrorTest {
@Test
void invalidCode() {
assertEquals("""
Error at 'e' (character 8): Expected number but is Boolean
Error at 'e' (character 9): Expected number but is Boolean
1 | 15 + true
^-- Here""",
assertThrows(Parser.ParseException.class, () -> Parser.parse("15 + true")).error.toString());
assertEquals("""
Error at ''' (character 9): Expected number but is String
Error at ''' (character 10): Expected number but is String
1 | 15 + 'yes'
^-- Here""",
assertThrows(Parser.ParseException.class, () -> Parser.parse("15 + 'yes'")).error.toString());
assertEquals("""
Error at '=' (character 7): Unexpected character
Error at '=' (character 8): Unexpected character
1 | string = 'Value'
^-- Here""",
assertThrows(Parser.ParseException.class, () -> Parser.parse("string = 'Value'")).error.toString());