mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Add input sources and source loading stage
This commit is contained in:
parent
5dcf31c106
commit
c5810f0deb
@ -3,20 +3,21 @@ package com.annimon.ownlang.util;
|
|||||||
import com.annimon.ownlang.Console;
|
import com.annimon.ownlang.Console;
|
||||||
import com.annimon.ownlang.stages.Stage;
|
import com.annimon.ownlang.stages.Stage;
|
||||||
import com.annimon.ownlang.stages.StagesData;
|
import com.annimon.ownlang.stages.StagesData;
|
||||||
|
import com.annimon.ownlang.util.input.SourceLoaderStage;
|
||||||
|
|
||||||
public class ErrorsLocationFormatterStage implements Stage<Iterable<? extends SourceLocatedError>, String> {
|
public class ErrorsLocationFormatterStage implements Stage<Iterable<? extends SourceLocatedError>, String> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String perform(StagesData stagesData, Iterable<? extends SourceLocatedError> input) {
|
public String perform(StagesData stagesData, Iterable<? extends SourceLocatedError> input) {
|
||||||
final var sb = new StringBuilder();
|
final var sb = new StringBuilder();
|
||||||
final String source = stagesData.get(SourceLoaderStage.TAG_SOURCE);
|
final String source = stagesData.getOrDefault(SourceLoaderStage.TAG_SOURCE, "");
|
||||||
final var lines = source.split("\r?\n");
|
final var lines = source.split("\r?\n");
|
||||||
for (SourceLocatedError error : input) {
|
for (SourceLocatedError error : input) {
|
||||||
sb.append(Console.newline());
|
sb.append(Console.newline());
|
||||||
sb.append(error);
|
sb.append(error);
|
||||||
sb.append(Console.newline());
|
sb.append(Console.newline());
|
||||||
final Range range = error.getRange();
|
final Range range = error.getRange();
|
||||||
if (range != null) {
|
if (range != null && lines.length > 0) {
|
||||||
printPosition(sb, range.normalize(), lines);
|
printPosition(sb, range.normalize(), lines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.annimon.ownlang.util.input;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface InputSource {
|
||||||
|
String getPath();
|
||||||
|
|
||||||
|
String load() throws IOException;
|
||||||
|
|
||||||
|
default String getBasePath() {
|
||||||
|
int i = getPath().lastIndexOf("/");
|
||||||
|
if (i == -1) return "";
|
||||||
|
return getPath().substring(0, i + 1);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.annimon.ownlang.util.input;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public record InputSourceFile(String path) implements InputSource {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String load() throws IOException {
|
||||||
|
if (Files.isReadable(Path.of(path))) {
|
||||||
|
try (InputStream is = new FileInputStream(path)) {
|
||||||
|
return SourceLoaderStage.readStream(is);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IOException(path + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "File " + path;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.annimon.ownlang.util.input;
|
||||||
|
|
||||||
|
public record InputSourceProgram(String program) implements InputSource {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String load() {
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Program";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.annimon.ownlang.util.input;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public record InputSourceResource(String path) implements InputSource {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String load() throws IOException {
|
||||||
|
try (InputStream is = getClass().getResourceAsStream(path)) {
|
||||||
|
if (is != null) {
|
||||||
|
return SourceLoaderStage.readStream(is);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IOException(path + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Resource " + path;
|
||||||
|
}
|
||||||
|
}
|
@ -1,37 +1,25 @@
|
|||||||
package com.annimon.ownlang.util;
|
package com.annimon.ownlang.util.input;
|
||||||
|
|
||||||
import com.annimon.ownlang.exceptions.OwnLangRuntimeException;
|
import com.annimon.ownlang.exceptions.OwnLangRuntimeException;
|
||||||
import com.annimon.ownlang.stages.Stage;
|
import com.annimon.ownlang.stages.Stage;
|
||||||
import com.annimon.ownlang.stages.StagesData;
|
import com.annimon.ownlang.stages.StagesData;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class SourceLoaderStage implements Stage<String, String> {
|
public class SourceLoaderStage implements Stage<InputSource, String> {
|
||||||
|
|
||||||
public static final String TAG_SOURCE = "source";
|
public static final String TAG_SOURCE = "source";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String perform(StagesData stagesData, String name) {
|
public String perform(StagesData stagesData, InputSource inputSource) {
|
||||||
try {
|
try {
|
||||||
String result = readSource(name);
|
String result = inputSource.load();
|
||||||
stagesData.put(TAG_SOURCE, result);
|
stagesData.put(TAG_SOURCE, result);
|
||||||
return result;
|
return result;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new OwnLangRuntimeException("Unable to read input " + name, e);
|
throw new OwnLangRuntimeException("Unable to read input " + inputSource, e);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readSource(String name) throws IOException {
|
|
||||||
try (InputStream is = getClass().getResourceAsStream("/" + name)) {
|
|
||||||
if (is != null) {
|
|
||||||
return readStream(is);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try (InputStream is = new FileInputStream(name)) {
|
|
||||||
return readStream(is);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,4 +56,3 @@ tasks.register('runOptimizationDumper', JavaExec) {
|
|||||||
classpath = sourceSets.main.runtimeClasspath
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
args '../program.own'
|
args '../program.own'
|
||||||
}
|
}
|
||||||
//
|
|
||||||
|
@ -3,13 +3,13 @@ package com.annimon.ownlang;
|
|||||||
import com.annimon.ownlang.exceptions.OwnLangParserException;
|
import com.annimon.ownlang.exceptions.OwnLangParserException;
|
||||||
import com.annimon.ownlang.exceptions.StoppedException;
|
import com.annimon.ownlang.exceptions.StoppedException;
|
||||||
import com.annimon.ownlang.parser.Beautifier;
|
import com.annimon.ownlang.parser.Beautifier;
|
||||||
import com.annimon.ownlang.parser.SourceLoader;
|
|
||||||
import com.annimon.ownlang.parser.Token;
|
import com.annimon.ownlang.parser.Token;
|
||||||
import com.annimon.ownlang.parser.ast.Statement;
|
import com.annimon.ownlang.parser.ast.Statement;
|
||||||
import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage;
|
import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage;
|
||||||
import com.annimon.ownlang.parser.linters.LinterStage;
|
import com.annimon.ownlang.parser.linters.LinterStage;
|
||||||
import com.annimon.ownlang.parser.optimization.OptimizationStage;
|
import com.annimon.ownlang.parser.optimization.OptimizationStage;
|
||||||
import com.annimon.ownlang.stages.*;
|
import com.annimon.ownlang.stages.*;
|
||||||
|
import com.annimon.ownlang.util.input.SourceLoaderStage;
|
||||||
import com.annimon.ownlang.utils.Repl;
|
import com.annimon.ownlang.utils.Repl;
|
||||||
import com.annimon.ownlang.utils.Sandbox;
|
import com.annimon.ownlang.utils.Sandbox;
|
||||||
import com.annimon.ownlang.utils.TimeMeasurement;
|
import com.annimon.ownlang.utils.TimeMeasurement;
|
||||||
@ -23,17 +23,12 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public final class Main {
|
public final class Main {
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
if (args.length == 0) {
|
final RunOptions options = new RunOptions();
|
||||||
try {
|
if (args.length == 0 && (options.detectDefaultProgramPath() == null)) {
|
||||||
runDefault();
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
printUsage();
|
printUsage();
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final RunOptions options = new RunOptions();
|
|
||||||
String input = null;
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
switch (args[i]) {
|
switch (args[i]) {
|
||||||
case "-a":
|
case "-a":
|
||||||
@ -83,72 +78,68 @@ public final class Main {
|
|||||||
case "-f":
|
case "-f":
|
||||||
case "--file":
|
case "--file":
|
||||||
if (i + 1 < args.length) {
|
if (i + 1 < args.length) {
|
||||||
input = SourceLoader.readSource(args[i + 1]);
|
options.programPath = args[i + 1];
|
||||||
createOwnLangArgs(args, i + 2);
|
createOwnLangArgs(args, i + 2);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "--sandbox":
|
case "--sandbox":
|
||||||
createOwnLangArgs(args, i + 1);
|
Sandbox.main(createOwnLangArgs(args, i + 1));
|
||||||
final String[] ownlangArgs = Shared.getOwnlangArgs();
|
|
||||||
String[] newArgs = new String[ownlangArgs.length];
|
|
||||||
System.arraycopy(ownlangArgs, 0, newArgs, 0, ownlangArgs.length);
|
|
||||||
Sandbox.main(newArgs);
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (input == null) {
|
if (options.programSource == null) {
|
||||||
input = args[i];
|
options.programSource = args[i];
|
||||||
createOwnLangArgs(args, i + 1);
|
createOwnLangArgs(args, i + 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (input == null) {
|
|
||||||
throw new IllegalArgumentException("Empty input");
|
|
||||||
}
|
|
||||||
if (options.beautifyMode) {
|
if (options.beautifyMode) {
|
||||||
|
String input = new SourceLoaderStage()
|
||||||
|
.perform(new StagesDataMap(), options.toInputSource());
|
||||||
System.out.println(Beautifier.beautify(input));
|
System.out.println(Beautifier.beautify(input));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
run(input, options);
|
run(options);
|
||||||
}
|
|
||||||
|
|
||||||
private static void runDefault() throws IOException {
|
|
||||||
final RunOptions options = new RunOptions();
|
|
||||||
run(SourceLoader.readSource("program.own"), options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printUsage() {
|
private static void printUsage() {
|
||||||
System.out.println("OwnLang version " + Version.VERSION + "\n\n" +
|
System.out.println("OwnLang version %s\n\n".formatted(Version.VERSION) + """
|
||||||
"Usage: ownlang [options]\n" +
|
Usage: ownlang [options]
|
||||||
" options:\n" +
|
options:
|
||||||
" -f, --file [input] Run program file. Required.\n" +
|
-f, --file [input] Run program file. Required.
|
||||||
" -r, --repl Enter to a REPL mode\n" +
|
-r, --repl Enter to a REPL mode
|
||||||
" -l, --lint Find bugs in code\n" +
|
-l, --lint Find bugs in code
|
||||||
" -o N, --optimize N Perform optimization with N passes\n" +
|
-o N, --optimize N Perform optimization with N (0...9) passes
|
||||||
" -b, --beautify Beautify source code\n" +
|
-b, --beautify Beautify source code
|
||||||
" -a, --showast Show AST of program\n" +
|
-a, --showast Show AST of program
|
||||||
" -t, --showtokens Show lexical tokens\n" +
|
-t, --showtokens Show lexical tokens
|
||||||
" -m, --showtime Show elapsed time of parsing and execution");
|
-m, --showtime Show elapsed time of parsing and execution
|
||||||
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createOwnLangArgs(String[] javaArgs, int index) {
|
private static String[] createOwnLangArgs(String[] javaArgs, int index) {
|
||||||
if (index >= javaArgs.length) return;
|
final String[] ownlangArgs;
|
||||||
final String[] ownlangArgs = new String[javaArgs.length - index];
|
if (index >= javaArgs.length) {
|
||||||
|
ownlangArgs = new String[0];
|
||||||
|
} else {
|
||||||
|
ownlangArgs = new String[javaArgs.length - index];
|
||||||
System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length);
|
System.arraycopy(javaArgs, index, ownlangArgs, 0, ownlangArgs.length);
|
||||||
|
}
|
||||||
Shared.setOwnlangArgs(ownlangArgs);
|
Shared.setOwnlangArgs(ownlangArgs);
|
||||||
|
return ownlangArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void run(String input, RunOptions options) {
|
private static void run(RunOptions options) {
|
||||||
final var measurement = new TimeMeasurement();
|
final var measurement = new TimeMeasurement();
|
||||||
final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop);
|
final var scopedStages = new ScopedStageFactory(measurement::start, measurement::stop);
|
||||||
|
|
||||||
final var stagesData = new StagesDataMap();
|
final var stagesData = new StagesDataMap();
|
||||||
stagesData.put(SourceLoaderStage.TAG_SOURCE, input);
|
|
||||||
try {
|
try {
|
||||||
scopedStages.create("Lexer", new LexerStage())
|
scopedStages.create("Source loader", new SourceLoaderStage())
|
||||||
|
.then(scopedStages.create("Lexer", new LexerStage()))
|
||||||
.then(scopedStages.create("Parser", new ParserStage()))
|
.then(scopedStages.create("Parser", new ParserStage()))
|
||||||
.thenConditional(options.optimizationLevel > 0,
|
.thenConditional(options.optimizationLevel > 0,
|
||||||
scopedStages.create("Optimization",
|
scopedStages.create("Optimization",
|
||||||
@ -157,7 +148,7 @@ public final class Main {
|
|||||||
scopedStages.create("Linter", new LinterStage()))
|
scopedStages.create("Linter", new LinterStage()))
|
||||||
.then(scopedStages.create("Function adding", new FunctionAddingStage()))
|
.then(scopedStages.create("Function adding", new FunctionAddingStage()))
|
||||||
.then(scopedStages.create("Execution", new ExecutionStage()))
|
.then(scopedStages.create("Execution", new ExecutionStage()))
|
||||||
.perform(stagesData, input);
|
.perform(stagesData, options.toInputSource());
|
||||||
} catch (OwnLangParserException ex) {
|
} catch (OwnLangParserException ex) {
|
||||||
final var error = new ParseErrorsFormatterStage()
|
final var error = new ParseErrorsFormatterStage()
|
||||||
.perform(stagesData, ex.getParseErrors());
|
.perform(stagesData, ex.getParseErrors());
|
||||||
@ -185,20 +176,4 @@ public final class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class RunOptions {
|
|
||||||
boolean showTokens, showAst, showMeasurements;
|
|
||||||
boolean lintMode;
|
|
||||||
boolean beautifyMode;
|
|
||||||
int optimizationLevel;
|
|
||||||
|
|
||||||
RunOptions() {
|
|
||||||
showTokens = false;
|
|
||||||
showAst = false;
|
|
||||||
showMeasurements = false;
|
|
||||||
lintMode = false;
|
|
||||||
beautifyMode = false;
|
|
||||||
optimizationLevel = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.annimon.ownlang;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.util.input.InputSource;
|
||||||
|
import com.annimon.ownlang.util.input.InputSourceFile;
|
||||||
|
import com.annimon.ownlang.util.input.InputSourceProgram;
|
||||||
|
import com.annimon.ownlang.util.input.InputSourceResource;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class RunOptions {
|
||||||
|
private static final String RESOURCE_PREFIX = "resource:";
|
||||||
|
private static final String DEFAULT_PROGRAM = "program.own";
|
||||||
|
|
||||||
|
// input
|
||||||
|
String programPath;
|
||||||
|
String programSource;
|
||||||
|
// modes
|
||||||
|
boolean lintMode;
|
||||||
|
boolean beautifyMode;
|
||||||
|
int optimizationLevel;
|
||||||
|
// flags
|
||||||
|
boolean showTokens;
|
||||||
|
boolean showAst;
|
||||||
|
boolean showMeasurements;
|
||||||
|
|
||||||
|
String detectDefaultProgramPath() {
|
||||||
|
if (getClass().getResource("/" + DEFAULT_PROGRAM) != null) {
|
||||||
|
return RESOURCE_PREFIX + "/" + DEFAULT_PROGRAM;
|
||||||
|
}
|
||||||
|
if (Files.isReadable(Path.of(DEFAULT_PROGRAM))) {
|
||||||
|
return DEFAULT_PROGRAM;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputSource toInputSource() {
|
||||||
|
if (programSource != null) {
|
||||||
|
return new InputSourceProgram(programSource);
|
||||||
|
}
|
||||||
|
if (programPath == null) {
|
||||||
|
// No arguments. Default to program.own
|
||||||
|
programPath = detectDefaultProgramPath();
|
||||||
|
if (programPath == null) {
|
||||||
|
throw new IllegalArgumentException("Empty input");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (programPath.startsWith(RESOURCE_PREFIX)) {
|
||||||
|
String path = programPath.substring(RESOURCE_PREFIX.length());
|
||||||
|
return new InputSourceResource(path);
|
||||||
|
} else {
|
||||||
|
return new InputSourceFile(programPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,9 @@ import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage;
|
|||||||
import com.annimon.ownlang.parser.optimization.OptimizationStage;
|
import com.annimon.ownlang.parser.optimization.OptimizationStage;
|
||||||
import com.annimon.ownlang.parser.visitors.AbstractVisitor;
|
import com.annimon.ownlang.parser.visitors.AbstractVisitor;
|
||||||
import com.annimon.ownlang.stages.*;
|
import com.annimon.ownlang.stages.*;
|
||||||
import com.annimon.ownlang.util.SourceLoaderStage;
|
import com.annimon.ownlang.util.input.InputSource;
|
||||||
|
import com.annimon.ownlang.util.input.InputSourceFile;
|
||||||
|
import com.annimon.ownlang.util.input.SourceLoaderStage;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
@ -24,11 +26,12 @@ import static org.junit.jupiter.api.Assertions.*;
|
|||||||
|
|
||||||
public class ProgramsTest {
|
public class ProgramsTest {
|
||||||
private static final String RES_DIR = "src/test/resources";
|
private static final String RES_DIR = "src/test/resources";
|
||||||
private static Stage<String, Statement> testPipeline;
|
private static Stage<InputSource, Statement> testPipeline;
|
||||||
|
|
||||||
public static Stream<String> data() {
|
public static Stream<InputSource> data() {
|
||||||
return scanDirectory(RES_DIR)
|
return scanDirectory(RES_DIR)
|
||||||
.map(File::getPath);
|
.map(File::getPath)
|
||||||
|
.map(InputSourceFile::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
@ -85,16 +88,16 @@ public class ProgramsTest {
|
|||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource("data")
|
@MethodSource("data")
|
||||||
public void testProgram(String programPath) {
|
public void testProgram(InputSource inputSource) {
|
||||||
final StagesDataMap stagesData = new StagesDataMap();
|
final StagesDataMap stagesData = new StagesDataMap();
|
||||||
try {
|
try {
|
||||||
testPipeline.perform(stagesData, programPath);
|
testPipeline.perform(stagesData, inputSource);
|
||||||
} catch (OwnLangParserException ex) {
|
} catch (OwnLangParserException ex) {
|
||||||
final var error = new ParseErrorsFormatterStage()
|
final var error = new ParseErrorsFormatterStage()
|
||||||
.perform(stagesData, ex.getParseErrors());
|
.perform(stagesData, ex.getParseErrors());
|
||||||
fail(programPath + "\n" + error, ex);
|
fail(inputSource + "\n" + error, ex);
|
||||||
} catch (Exception oae) {
|
} catch (Exception oae) {
|
||||||
fail(programPath, oae);
|
fail(inputSource.toString(), oae);
|
||||||
Console.handleException(stagesData, Thread.currentThread(), oae);
|
Console.handleException(stagesData, Thread.currentThread(), oae);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user