From 2578f0a6b4c8f22e14db65d6cbc5784d66e3b38b Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sat, 30 Sep 2023 23:55:00 +0300 Subject: [PATCH] Add parse errors formatter --- .../main/java/com/annimon/ownlang/Main.java | 4 +- .../stages/ParseErrorsFormatterStage.java | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java index 517748a..ccd0d7a 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -154,7 +154,9 @@ public final class Main { .then(scopedStages.create("Execution", new ExecutionStage())) .perform(stagesData, input); } catch (OwnLangParserException ex) { - System.err.println(ex.getParseErrors()); + final var error = new ParseErrorsFormatterStage() + .perform(stagesData, ex.getParseErrors()); + System.err.println(error); } catch (StoppedException ex) { // skip } catch (Exception ex) { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java new file mode 100644 index 0000000..5864dd9 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/stages/ParseErrorsFormatterStage.java @@ -0,0 +1,62 @@ +package com.annimon.ownlang.stages; + +import com.annimon.ownlang.Console; +import com.annimon.ownlang.parser.Pos; +import com.annimon.ownlang.parser.Range; +import com.annimon.ownlang.parser.error.ParseError; +import com.annimon.ownlang.parser.error.ParseErrors; + +public class ParseErrorsFormatterStage implements Stage { + + @Override + public String perform(StagesData stagesData, ParseErrors input) { + final var sb = new StringBuilder(); + final String source = stagesData.get(SourceLoaderStage.TAG_SOURCE); + final var lines = source.split("\r?\n"); + for (ParseError parseError : input) { + sb.append(Console.newline()); + sb.append(parseError); + sb.append(Console.newline()); + final Range range = parseError.range().normalize(); + printPosition(sb, range, lines); + if (parseError.hasStackTrace()) { + sb.append("Stack trace:"); + sb.append(Console.newline()); + for (StackTraceElement el : parseError.stackTraceElements()) { + sb.append(" ").append(el).append(Console.newline()); + } + } + } + return sb.toString(); + } + + private static void printPosition(StringBuilder sb, Range range, String[] lines) { + final Pos start = range.start(); + final int linesCount = lines.length;; + if (range.isOnSameLine()) { + if (start.row() < linesCount) { + sb.append(lines[start.row()]); + sb.append(Console.newline()); + sb.append(" ".repeat(start.col())); + sb.append("^".repeat(range.end().col() - start.col() + 1)); + sb.append(Console.newline()); + } + } else { + if (start.row() < linesCount) { + String line = lines[start.row()]; + sb.append(line); + sb.append(Console.newline()); + sb.append(" ".repeat(start.col())); + sb.append("^".repeat(Math.max(1, line.length() - start.col()))); + sb.append(Console.newline()); + } + final Pos end = range.end(); + if (end.row() < linesCount) { + sb.append(lines[end.row()]); + sb.append(Console.newline()); + sb.append("^".repeat(end.col())); + sb.append(Console.newline()); + } + } + } +}