diff --git a/eo-parser/src/main/java/org/eolang/parser/ParsingException.java b/eo-parser/src/main/java/org/eolang/parser/ParsingException.java index 0182070258..e166d58d32 100644 --- a/eo-parser/src/main/java/org/eolang/parser/ParsingException.java +++ b/eo-parser/src/main/java/org/eolang/parser/ParsingException.java @@ -42,6 +42,15 @@ public final class ParsingException extends RuntimeException { */ private final int place; + /** + * Ctor. + * @param line The place + * @param msgs Messages + */ + ParsingException(final int line, final String... msgs) { + this(new IllegalStateException("Parsing error"), line, List.of(msgs)); + } + /** * Ctor. * @param cause Cause of failure diff --git a/eo-parser/src/main/java/org/eolang/parser/XeEoListener.java b/eo-parser/src/main/java/org/eolang/parser/XeEoListener.java index 9521158af6..d848f9c976 100644 --- a/eo-parser/src/main/java/org/eolang/parser/XeEoListener.java +++ b/eo-parser/src/main/java/org/eolang/parser/XeEoListener.java @@ -25,10 +25,9 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.util.HashMap; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; import org.antlr.v4.runtime.ParserRuleContext; @@ -78,9 +77,9 @@ public final class XeEoListener implements EoListener, Iterable { private final long start; /** - * Errors map. + * Errors. */ - private final Map errors; + private final List errors; /** * Ctor. @@ -90,7 +89,7 @@ public final class XeEoListener implements EoListener, Iterable { public XeEoListener(final String name) { this.name = name; this.dirs = new Directives(); - this.errors = new HashMap<>(0); + this.errors = new ArrayList<>(0); this.objects = new Objects.ObjXembly(); this.start = System.nanoTime(); } @@ -107,16 +106,7 @@ public void enterProgram(final EoParser.ProgramContext ctx) { public void exitProgram(final EoParser.ProgramContext ctx) { this.dirs.xpath("/program").strict(1); if (!this.errors.isEmpty()) { - this.dirs.addIf("errors").strict(1); - for (final Map.Entry error : this.errors.entrySet()) { - this.dirs - .add("error") - .attr("check", "eo-parser") - .attr("line", error.getKey().getStart().getLine()) - .attr("severity", "critical") - .set(error.getValue()); - } - this.dirs.up().up(); + this.dirs.append(new DrErrors(this.errors)); } this.dirs .attr("ms", (System.nanoTime() - this.start) / (1000L * 1000L)) @@ -483,12 +473,16 @@ public void exitHapplicationTailReversed(final EoParser.HapplicationTailReversed } @Override - public void enterHapplicationTailReversedFirst(final EoParser.HapplicationTailReversedFirstContext ctx) { + public void enterHapplicationTailReversedFirst( + final EoParser.HapplicationTailReversedFirstContext ctx + ) { this.objects.enter(); } @Override - public void exitHapplicationTailReversedFirst(final EoParser.HapplicationTailReversedFirstContext ctx) { + public void exitHapplicationTailReversedFirst( + final EoParser.HapplicationTailReversedFirstContext ctx + ) { this.objects.leave(); } @@ -573,12 +567,16 @@ public void exitVapplicationArgBound(final EoParser.VapplicationArgBoundContext } @Override - public void enterVapplicationArgBoundCurrent(final EoParser.VapplicationArgBoundCurrentContext ctx) { + public void enterVapplicationArgBoundCurrent( + final EoParser.VapplicationArgBoundCurrentContext ctx + ) { // Nothing here } @Override - public void exitVapplicationArgBoundCurrent(final EoParser.VapplicationArgBoundCurrentContext ctx) { + public void exitVapplicationArgBoundCurrent( + final EoParser.VapplicationArgBoundCurrentContext ctx + ) { // Nothing here } @@ -603,22 +601,30 @@ public void exitVapplicationArgUnbound(final EoParser.VapplicationArgUnboundCont } @Override - public void enterVapplicationArgUnboundCurrent(final EoParser.VapplicationArgUnboundCurrentContext ctx) { + public void enterVapplicationArgUnboundCurrent( + final EoParser.VapplicationArgUnboundCurrentContext ctx + ) { // Nothing here } @Override - public void exitVapplicationArgUnboundCurrent(final EoParser.VapplicationArgUnboundCurrentContext ctx) { + public void exitVapplicationArgUnboundCurrent( + final EoParser.VapplicationArgUnboundCurrentContext ctx + ) { // Nothing here } @Override - public void enterVapplicationArgUnboundNext(final EoParser.VapplicationArgUnboundNextContext ctx) { + public void enterVapplicationArgUnboundNext( + final EoParser.VapplicationArgUnboundNextContext ctx + ) { // Nothing here } @Override - public void exitVapplicationArgUnboundNext(final EoParser.VapplicationArgUnboundNextContext ctx) { + public void exitVapplicationArgUnboundNext( + final EoParser.VapplicationArgUnboundNextContext ctx + ) { // Nothing here } @@ -707,12 +713,16 @@ public void exitAttributesAs(final EoParser.AttributesAsContext ctx) { } @Override - public void enterVapplicationArgHanonymBoundBody(final EoParser.VapplicationArgHanonymBoundBodyContext ctx) { + public void enterVapplicationArgHanonymBoundBody( + final EoParser.VapplicationArgHanonymBoundBodyContext ctx + ) { // Nothing here } @Override - public void exitVapplicationArgHanonymBoundBody(final EoParser.VapplicationArgHanonymBoundBodyContext ctx) { + public void exitVapplicationArgHanonymBoundBody( + final EoParser.VapplicationArgHanonymBoundBodyContext ctx + ) { // Nothing here } @@ -865,12 +875,16 @@ public void exitMethodTailOptional(final EoParser.MethodTailOptionalContext ctx) } @Override - public void enterVmethodHeadApplicationTail(final EoParser.VmethodHeadApplicationTailContext ctx) { + public void enterVmethodHeadApplicationTail( + final EoParser.VmethodHeadApplicationTailContext ctx + ) { // Nothing here } @Override - public void exitVmethodHeadApplicationTail(final EoParser.VmethodHeadApplicationTailContext ctx) { + public void exitVmethodHeadApplicationTail( + final EoParser.VmethodHeadApplicationTailContext ctx + ) { // Nothing here } @@ -1040,7 +1054,21 @@ public void enterAs(final EoParser.AsContext ctx) { } else { final int index = Integer.parseInt(ctx.INT().getText()); if (index < 0) { - this.errors.put(ctx, "Object binding can't be negative"); + this.errors.add( + new ParsingException( + ctx.getStart().getLine(), + new MsgLocated( + ctx.getStart().getLine(), + ctx.getStart().getCharPositionInLine(), + "Object binding can't be negative" + ).formatted(), + new MsgUnderlined( + XeEoListener.line(ctx), + ctx.getStart().getCharPositionInLine(), + ctx.getText().length() + ).formatted() + ) + ); } has = String.format("α%d", index); } @@ -1084,7 +1112,7 @@ public void enterData(final EoParser.DataContext ctx) { text.substring(1, text.length() - 1) ).getBytes(StandardCharsets.UTF_8) ); - } else if (ctx.TEXT() != null) { + } else { base = "string"; final int indent = ctx.getStart().getCharPositionInLine(); data = new BytesToHex( @@ -1092,10 +1120,6 @@ public void enterData(final EoParser.DataContext ctx) { XeEoListener.trimMargin(text, indent) ).getBytes(StandardCharsets.UTF_8) ); - } else { - base = "unknown"; - data = ctx::getText; - this.errors.put(ctx, String.format("Unknown data type: %s", ctx.getText())); } this.objects.prop("base", base).data(data.get()); } @@ -1197,4 +1221,26 @@ private static String trimMargin(final String text, final int indent) { } return res.toString(); } + + /** + * Get line from context. + * @param ctx Context + * @return Line + */ + private static String line(final ParserRuleContext ctx) { + final Token token = ctx.start; + final int number = token.getLine(); + final String[] lines = token.getInputStream().toString().split("\n"); + if (number > 0 && number <= lines.length) { + return lines[number - 1]; + } else { + throw new IllegalArgumentException( + String.format( + "Line number '%s' out of bounds, total lines: %d", + number, + lines.length + ) + ); + } + } } diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-typos/broken-binding.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/broken-binding.yaml index 11a02f5b39..36f02aca58 100644 --- a/eo-parser/src/test/resources/org/eolang/parser/eo-typos/broken-binding.yaml +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/broken-binding.yaml @@ -21,11 +21,10 @@ # SOFTWARE. --- line: 4 -# @todo #3706:30min Add exact location of the error in the message. -# This message is already quite clear, but it would be better if it included -# the exact location of the error. message: |- - Object binding can't be negative + [4:6] error: 'Object binding can't be negative' + 42:-1 + ^^^ input: | # No comments [] > foo