mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Show location of the closest error from call stack
This commit is contained in:
parent
e304aafedd
commit
ae94a1dc8a
@ -4,14 +4,16 @@ import com.annimon.ownlang.lib.CallStack;
|
||||
import com.annimon.ownlang.outputsettings.ConsoleOutputSettings;
|
||||
import com.annimon.ownlang.outputsettings.OutputSettings;
|
||||
import com.annimon.ownlang.stages.StagesData;
|
||||
import com.annimon.ownlang.util.ErrorsLocationFormatterStage;
|
||||
import com.annimon.ownlang.util.ExceptionConverterStage;
|
||||
import com.annimon.ownlang.util.ExceptionStackTraceToStringStage;
|
||||
import com.annimon.ownlang.util.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
import static com.annimon.ownlang.util.ErrorsLocationFormatterStage.*;
|
||||
|
||||
public class Console {
|
||||
|
||||
@ -64,14 +66,29 @@ public class Console {
|
||||
}
|
||||
|
||||
public static void handleException(StagesData stagesData, Thread thread, Exception exception) {
|
||||
String mainError = new ExceptionConverterStage()
|
||||
final var joiner = new StringJoiner("\n");
|
||||
joiner.add(new ExceptionConverterStage()
|
||||
.then((data, error) -> List.of(error))
|
||||
.then(new ErrorsLocationFormatterStage())
|
||||
.perform(stagesData, exception);
|
||||
String callStack = CallStack.getFormattedCalls();
|
||||
String stackTrace = new ExceptionStackTraceToStringStage()
|
||||
.perform(stagesData, exception);
|
||||
error(String.join("\n", mainError, "Thread: " + thread.getName(), callStack, stackTrace));
|
||||
.perform(stagesData, exception));
|
||||
final var processedPositions = stagesData.getOrDefault(TAG_POSITIONS, HashSet::new);
|
||||
if (processedPositions.isEmpty()) {
|
||||
// In case no source located errors were printed
|
||||
// Find closest SourceLocated call stack frame
|
||||
CallStack.getCalls().stream()
|
||||
.limit(4)
|
||||
.map(CallStack.CallInfo::range)
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.map(range -> new SourceLocationFormatterStage()
|
||||
.perform(stagesData, range))
|
||||
.ifPresent(joiner::add);
|
||||
}
|
||||
joiner.add("Thread: " + thread.getName());
|
||||
joiner.add(CallStack.getFormattedCalls());
|
||||
joiner.add(new ExceptionStackTraceToStringStage()
|
||||
.perform(stagesData, exception));
|
||||
error(joiner.toString());
|
||||
}
|
||||
|
||||
public static void handleException(Thread thread, Throwable throwable) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.annimon.ownlang.stages;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface StagesData {
|
||||
|
||||
<T> T get(String tag);
|
||||
@ -9,5 +11,10 @@ public interface StagesData {
|
||||
return value != null ? value : other;
|
||||
}
|
||||
|
||||
default <T> T getOrDefault(String tag, Supplier<? extends T> otherSuppler) {
|
||||
T value = get(tag);
|
||||
return value != null ? value : otherSuppler.get();
|
||||
}
|
||||
|
||||
void put(String tag, Object input);
|
||||
}
|
||||
|
@ -4,53 +4,28 @@ import com.annimon.ownlang.Console;
|
||||
import com.annimon.ownlang.stages.Stage;
|
||||
import com.annimon.ownlang.stages.StagesData;
|
||||
import com.annimon.ownlang.util.input.SourceLoaderStage;
|
||||
import java.util.HashSet;
|
||||
import static com.annimon.ownlang.util.SourceLocationFormatterStage.printPosition;
|
||||
|
||||
public class ErrorsLocationFormatterStage implements Stage<Iterable<? extends SourceLocatedError>, String> {
|
||||
public static final String TAG_POSITIONS = "formattedPositions";
|
||||
|
||||
@Override
|
||||
public String perform(StagesData stagesData, Iterable<? extends SourceLocatedError> input) {
|
||||
final var sb = new StringBuilder();
|
||||
final String source = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE, "");
|
||||
final var lines = source.split("\r?\n");
|
||||
final var lines = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE_LINES, new String[0]);
|
||||
for (SourceLocatedError error : input) {
|
||||
sb.append(Console.newline());
|
||||
sb.append(error);
|
||||
sb.append(Console.newline());
|
||||
final Range range = error.getRange();
|
||||
if (range != null && lines.length > 0) {
|
||||
var positions = stagesData.getOrDefault(TAG_POSITIONS, HashSet::new);
|
||||
positions.add(range);
|
||||
stagesData.put(TAG_POSITIONS, positions);
|
||||
printPosition(sb, range.normalize(), lines);
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package com.annimon.ownlang.util;
|
||||
|
||||
public record SimpleError(String message) implements SourceLocatedError {
|
||||
public record SimpleError(String message, Range range) implements SourceLocatedError {
|
||||
public SimpleError(String message) {
|
||||
this(message, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
@ -10,4 +14,9 @@ public record SimpleError(String message) implements SourceLocatedError {
|
||||
public String toString() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range getRange() {
|
||||
return range;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
package com.annimon.ownlang.util;
|
||||
|
||||
import com.annimon.ownlang.Console;
|
||||
import com.annimon.ownlang.stages.Stage;
|
||||
import com.annimon.ownlang.stages.StagesData;
|
||||
import com.annimon.ownlang.util.input.SourceLoaderStage;
|
||||
|
||||
public class SourceLocationFormatterStage implements Stage<Range, String> {
|
||||
|
||||
@Override
|
||||
public String perform(StagesData stagesData, Range input) {
|
||||
final var lines = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE_LINES, new String[0]);
|
||||
final var sb = new StringBuilder();
|
||||
if (input != null && lines.length > 0) {
|
||||
printPosition(sb, input.normalize(), lines);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,13 +10,16 @@ import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SourceLoaderStage implements Stage<InputSource, String> {
|
||||
|
||||
public static final String TAG_SOURCE = "source";
|
||||
public static final String TAG_SOURCE_LINES = "sourceLines";
|
||||
|
||||
@Override
|
||||
public String perform(StagesData stagesData, InputSource inputSource) {
|
||||
try {
|
||||
String result = inputSource.load();
|
||||
stagesData.put(TAG_SOURCE, result);
|
||||
final var lines = (result == null || result.isEmpty())
|
||||
? new String[0]
|
||||
: result.split("\r?\n");
|
||||
stagesData.put(TAG_SOURCE_LINES, lines);
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new OwnLangRuntimeException("Unable to read input " + inputSource, e);
|
||||
|
Loading…
Reference in New Issue
Block a user