From 35971e874b8e8c823d0422a50b256d16bec1c26f Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Sun, 15 Oct 2023 22:47:39 +0300 Subject: [PATCH] Add semantic linter as a required stage --- .../util/input/InputSourceDetector.java | 27 +++++++++ .../main/java/com/annimon/ownlang/Main.java | 18 ++++-- .../java/com/annimon/ownlang/RunOptions.java | 33 +++++------ .../exceptions/BaseParserException.java | 20 ------- .../exceptions/OwnLangParserException.java | 26 ++++----- .../ownlang/exceptions/ParseException.java | 13 +++-- .../ownlang/parser/error/ParseErrors.java | 22 ++++++-- .../error/ParseErrorsFormatterStage.java | 6 +- .../parser/linters/AssignValidator.java | 8 +-- .../DefaultFunctionsOverrideValidator.java | 7 +-- .../linters/IncludeSourceValidator.java | 33 +++++++++++ .../ownlang/parser/linters/LintVisitor.java | 5 +- .../ownlang/parser/linters/LinterResult.java | 35 +++++++++++- .../ownlang/parser/linters/LinterResults.java | 55 +++++++++++++++++++ .../ownlang/parser/linters/LinterStage.java | 34 +++++++++--- .../annimon/ownlang/parser/ProgramsTest.java | 8 ++- 16 files changed, 258 insertions(+), 92 deletions(-) create mode 100644 ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceDetector.java delete mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java create mode 100644 ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResults.java diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceDetector.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceDetector.java new file mode 100644 index 0000000..81178b6 --- /dev/null +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/input/InputSourceDetector.java @@ -0,0 +1,27 @@ +package com.annimon.ownlang.util.input; + +import java.nio.file.Files; +import java.nio.file.Path; + +public record InputSourceDetector() { + public static final String RESOURCE_PREFIX = "resource:"; + + public boolean isReadable(String programPath) { + if (programPath.startsWith(RESOURCE_PREFIX)) { + String path = programPath.substring(RESOURCE_PREFIX.length()); + return getClass().getResource(path) != null; + } else { + Path path = Path.of(programPath); + return Files.isReadable(path) && Files.isRegularFile(path); + } + } + + public InputSource toInputSource(String programPath) { + if (programPath.startsWith(RESOURCE_PREFIX)) { + String path = programPath.substring(RESOURCE_PREFIX.length()); + return new InputSourceResource(path); + } else { + return new InputSourceFile(programPath); + } + } +} 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 3255e3d..78460e1 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/Main.java @@ -15,6 +15,7 @@ import com.annimon.ownlang.utils.Sandbox; import com.annimon.ownlang.utils.TimeMeasurement; import java.io.IOException; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; /** @@ -72,8 +73,13 @@ public final class Main { case "-l": case "--lint": - options.lintMode = true; - return; + final String lintMode = i + 1 < args.length ? args[++i] : LinterStage.Mode.SEMANTIC.name(); + options.lintMode = switch (lintMode.toLowerCase(Locale.ROOT)) { + case "none" -> LinterStage.Mode.NONE; + case "full" -> LinterStage.Mode.FULL; + default -> LinterStage.Mode.SEMANTIC; + }; + break; case "-f": case "--file": @@ -112,8 +118,8 @@ public final class Main { options: -f, --file [input] Run program file. Required. -r, --repl Enter to a REPL mode - -l, --lint Find bugs in code - -o N, --optimize N Perform optimization with N (0...9) passes + -l, --lint Find bugs in code. Mode: none, semantic, full + -o, --optimize N Perform optimization with N (0...9) passes -b, --beautify Beautify source code -a, --showast Show AST of program -t, --showtokens Show lexical tokens @@ -145,8 +151,8 @@ public final class Main { .thenConditional(options.optimizationLevel > 0, scopedStages.create("Optimization", new OptimizationStage(options.optimizationLevel, options.showAst))) - .thenConditional(options.lintMode, - scopedStages.create("Linter", new LinterStage())) + .thenConditional(options.linterEnabled(), + scopedStages.create("Linter", new LinterStage(options.lintMode))) .then(scopedStages.create("Function adding", new FunctionAddingStage())) .then(scopedStages.create("Execution", new ExecutionStage())) .perform(stagesData, options.toInputSource()); diff --git a/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java b/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java index 10ed0c3..2a81c4e 100644 --- a/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java +++ b/ownlang-desktop/src/main/java/com/annimon/ownlang/RunOptions.java @@ -1,21 +1,17 @@ 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; +import com.annimon.ownlang.parser.linters.LinterStage; +import com.annimon.ownlang.util.input.*; +import static com.annimon.ownlang.util.input.InputSourceDetector.RESOURCE_PREFIX; 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; + LinterStage.Mode lintMode = LinterStage.Mode.SEMANTIC; boolean beautifyMode; int optimizationLevel; // flags @@ -23,11 +19,18 @@ public class RunOptions { boolean showAst; boolean showMeasurements; + private final InputSourceDetector inputSourceDetector = new InputSourceDetector(); + + boolean linterEnabled() { + return lintMode != null && lintMode != LinterStage.Mode.NONE; + } + String detectDefaultProgramPath() { - if (getClass().getResource("/" + DEFAULT_PROGRAM) != null) { - return RESOURCE_PREFIX + "/" + DEFAULT_PROGRAM; + final String resourcePath = RESOURCE_PREFIX + "/" + DEFAULT_PROGRAM; + if (inputSourceDetector.isReadable(resourcePath)) { + return resourcePath; } - if (Files.isReadable(Path.of(DEFAULT_PROGRAM))) { + if (inputSourceDetector.isReadable(DEFAULT_PROGRAM)) { return DEFAULT_PROGRAM; } return null; @@ -44,12 +47,6 @@ public class RunOptions { 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); - } + return inputSourceDetector.toInputSource(programPath); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java deleted file mode 100644 index c323a11..0000000 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/BaseParserException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.annimon.ownlang.exceptions; - -import com.annimon.ownlang.util.Range; - -/** - * Base type for all lexer and parser exceptions - */ -public abstract class BaseParserException extends RuntimeException { - - private final Range range; - - public BaseParserException(String message, Range range) { - super(message); - this.range = range; - } - - public Range getRange() { - return range; - } -} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java index 6e3d532..1fb4cf8 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/OwnLangParserException.java @@ -1,27 +1,27 @@ package com.annimon.ownlang.exceptions; -import com.annimon.ownlang.parser.error.ParseError; -import com.annimon.ownlang.parser.error.ParseErrors; +import com.annimon.ownlang.util.SourceLocatedError; +import java.util.Collection; +import java.util.List; /** - * Single Exception for Lexer and Parser errors + * Single Exception for Lexer, Parser and Linter errors */ public class OwnLangParserException extends RuntimeException { - private final ParseErrors parseErrors; + private final Collection errors; - public OwnLangParserException(ParseError parseError) { - super(parseError.toString()); - this.parseErrors = new ParseErrors(); - parseErrors.add(parseError);; + public OwnLangParserException(SourceLocatedError error) { + super(error.toString()); + errors = List.of(error);; } - public OwnLangParserException(ParseErrors parseErrors) { - super(parseErrors.toString()); - this.parseErrors = parseErrors; + public OwnLangParserException(Collection errors) { + super(errors.toString()); + this.errors = errors; } - public ParseErrors getParseErrors() { - return parseErrors; + public Collection getParseErrors() { + return errors; } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java index c4adc1d..4a803e1 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/exceptions/ParseException.java @@ -6,13 +6,16 @@ import com.annimon.ownlang.util.Range; * * @author aNNiMON */ -public final class ParseException extends BaseParserException { +public final class ParseException extends RuntimeException { - public ParseException(String message) { - super(message, Range.ZERO); - } + private final Range range; public ParseException(String message, Range range) { - super(message, range); + super(message); + this.range = range; + } + + public Range getRange() { + return range; } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java index 7662742..bab8e9d 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrors.java @@ -1,11 +1,12 @@ package com.annimon.ownlang.parser.error; import com.annimon.ownlang.Console; +import java.util.AbstractList; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -public final class ParseErrors implements Iterable { +public final class ParseErrors extends AbstractList { private final List errors; @@ -16,11 +17,17 @@ public final class ParseErrors implements Iterable { public void clear() { errors.clear(); } - - public void add(ParseError parseError) { - errors.add(parseError); + + @Override + public boolean add(ParseError parseError) { + return errors.add(parseError); } - + + @Override + public ParseError get(int index) { + return errors.get(index); + } + public boolean hasErrors() { return !errors.isEmpty(); } @@ -30,6 +37,11 @@ public final class ParseErrors implements Iterable { return errors.iterator(); } + @Override + public int size() { + return errors.size(); + } + @Override public String toString() { final StringBuilder result = new StringBuilder(); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java index 9e9611f..8944448 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/error/ParseErrorsFormatterStage.java @@ -4,11 +4,13 @@ import com.annimon.ownlang.stages.Stage; import com.annimon.ownlang.stages.StagesData; import com.annimon.ownlang.util.ErrorsLocationFormatterStage; import com.annimon.ownlang.util.ErrorsStackTraceFormatterStage; +import com.annimon.ownlang.util.SourceLocatedError; +import java.util.Collection; -public class ParseErrorsFormatterStage implements Stage { +public class ParseErrorsFormatterStage implements Stage, String> { @Override - public String perform(StagesData stagesData, ParseErrors input) { + public String perform(StagesData stagesData, Collection input) { String error = new ErrorsLocationFormatterStage() .perform(stagesData, input); String stackTrace = new ErrorsStackTraceFormatterStage() diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java index 70149f1..5fee252 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java @@ -1,8 +1,6 @@ package com.annimon.ownlang.parser.linters; -import com.annimon.ownlang.Console; import com.annimon.ownlang.parser.ast.*; -import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -14,7 +12,7 @@ final class AssignValidator extends LintVisitor { private final Set moduleConstants = new HashSet<>(); - AssignValidator(Collection results) { + AssignValidator(LinterResults results) { super(results); } @@ -24,8 +22,8 @@ final class AssignValidator extends LintVisitor { if (s.target instanceof VariableExpression varExpr) { final String variable = varExpr.name; if (moduleConstants.contains(variable)) { - results.add(new LinterResult(LinterResult.Severity.WARNING, - String.format("Variable \"%s\" overrides constant", variable))); + results.add(LinterResult.warning( + "Variable \"%s\" overrides constant".formatted(variable))); } } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java index f5cbde0..12c765c 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java @@ -1,7 +1,6 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.parser.ast.*; -import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -9,7 +8,7 @@ final class DefaultFunctionsOverrideValidator extends LintVisitor { private final Set moduleFunctions = new HashSet<>(); - DefaultFunctionsOverrideValidator(Collection results) { + DefaultFunctionsOverrideValidator(LinterResults results) { super(results); } @@ -17,8 +16,8 @@ final class DefaultFunctionsOverrideValidator extends LintVisitor { public void visit(FunctionDefineStatement s) { super.visit(s); if (moduleFunctions.contains(s.name)) { - results.add(new LinterResult(LinterResult.Severity.WARNING, - String.format("Function \"%s\" overrides default module function", s.name))); + results.add(LinterResult.warning( + "Function \"%s\" overrides default module function".formatted(s.name))); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java new file mode 100644 index 0000000..91c1a00 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/IncludeSourceValidator.java @@ -0,0 +1,33 @@ +package com.annimon.ownlang.parser.linters; + +import com.annimon.ownlang.parser.ast.IncludeStatement; +import com.annimon.ownlang.parser.ast.ValueExpression; +import com.annimon.ownlang.util.input.InputSourceDetector; + +/** + * + * @author aNNiMON + */ +final class IncludeSourceValidator extends LintVisitor { + private final InputSourceDetector detector; + + IncludeSourceValidator(LinterResults results) { + super(results); + detector = new InputSourceDetector(); + } + + @Override + public void visit(IncludeStatement s) { + super.visit(s); + if (s.expression instanceof ValueExpression expr) { + final String path = expr.eval().asString(); + if (!detector.isReadable(path)) { + results.add(LinterResult.error( + "Include statement path \"%s\" is not readable".formatted(path))); + } + } else { + results.add(LinterResult.warning( + "Include statement path \"%s\" is not a constant string".formatted(s.expression))); + } + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java index 7906fa1..aba3a3a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LintVisitor.java @@ -5,12 +5,11 @@ import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.parser.visitors.VisitorUtils; -import java.util.Collection; abstract class LintVisitor extends AbstractVisitor { - protected final Collection results; + protected final LinterResults results; - LintVisitor(Collection results) { + LintVisitor(LinterResults results) { this.results = results; } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java index d02d1e3..5401677 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResult.java @@ -1,9 +1,42 @@ package com.annimon.ownlang.parser.linters; -record LinterResult(Severity severity, String message) { +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocatedError; + +record LinterResult(Severity severity, String message, Range range) implements SourceLocatedError { enum Severity { ERROR, WARNING } + static LinterResult warning(String message) { + return new LinterResult(Severity.WARNING, message); + } + + static LinterResult error(String message) { + return new LinterResult(Severity.ERROR, message); + } + + LinterResult(Severity severity, String message) { + this(severity, message, null); + } + + public boolean isError() { + return severity == Severity.ERROR; + } + + public boolean isWarning() { + return severity == Severity.WARNING; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public Range getRange() { + return range; + } + @Override public String toString() { return severity.name() + ": " + message; diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResults.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResults.java new file mode 100644 index 0000000..af9b08d --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterResults.java @@ -0,0 +1,55 @@ +package com.annimon.ownlang.parser.linters; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +class LinterResults extends AbstractList { + private final List results; + + LinterResults() { + this(new ArrayList<>()); + } + + LinterResults(List results) { + this.results = results; + } + + @Override + public boolean add(LinterResult result) { + return results.add(result); + } + + @Override + public LinterResult get(int index) { + return results.get(index); + } + + @Override + public Iterator iterator() { + return results.iterator(); + } + + @Override + public int size() { + return results.size(); + } + + public boolean hasErrors() { + return results.stream().anyMatch(LinterResult::isError); + } + + public Stream errors() { + return results.stream().filter(LinterResult::isError); + } + + public boolean hasWarnings() { + return results.stream().anyMatch(LinterResult::isWarning); + } + + public Stream warnings() { + return results.stream().filter(LinterResult::isWarning); + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java index 7023530..b815778 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java @@ -1,6 +1,7 @@ package com.annimon.ownlang.parser.linters; import com.annimon.ownlang.Console; +import com.annimon.ownlang.exceptions.OwnLangParserException; import com.annimon.ownlang.lib.ScopeHandler; import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; @@ -11,23 +12,42 @@ import java.util.Comparator; import java.util.List; public class LinterStage implements Stage { + public enum Mode { NONE, SEMANTIC, FULL } + + private final Mode mode; + + public LinterStage(Mode mode) { + this.mode = mode; + } @Override public Node perform(StagesData stagesData, Node input) { - final List results = new ArrayList<>(); - final Visitor[] validators = new Visitor[] { - new AssignValidator(results), - new DefaultFunctionsOverrideValidator(results) - }; + if (mode == Mode.NONE) return input; - ScopeHandler.resetScope(); + final LinterResults results = new LinterResults(); + final List validators = new ArrayList<>(); + validators.add(new IncludeSourceValidator(results)); + + if (mode == Mode.SEMANTIC) { + validators.forEach(input::accept); + if (results.hasErrors()) { + throw new OwnLangParserException(results.errors().toList()); + } + return input; + } + + // Full lint validation with Console output + validators.add(new AssignValidator(results)); + validators.add(new DefaultFunctionsOverrideValidator(results)); + + ScopeHandler.resetScope(); // TODO special linter scope? for (Visitor validator : validators) { input.accept(validator); ScopeHandler.resetScope(); } results.sort(Comparator.comparing(LinterResult::severity)); - Console.println(String.format("Lint validation completed. %d results found!", results.size())); + Console.println("Lint validation completed. %d results found!".formatted(results.size())); for (LinterResult r : results) { switch (r.severity()) { case ERROR -> Console.error(r.toString()); diff --git a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java index 8533e2d..af113af 100644 --- a/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java +++ b/ownlang-parser/src/test/java/com/annimon/ownlang/parser/ProgramsTest.java @@ -9,6 +9,7 @@ import com.annimon.ownlang.parser.ast.FunctionDefineStatement; import com.annimon.ownlang.parser.ast.Node; import com.annimon.ownlang.parser.ast.Visitor; import com.annimon.ownlang.parser.error.ParseErrorsFormatterStage; +import com.annimon.ownlang.parser.linters.LinterStage; import com.annimon.ownlang.parser.optimization.OptimizationStage; import com.annimon.ownlang.parser.visitors.AbstractVisitor; import com.annimon.ownlang.stages.*; @@ -16,7 +17,6 @@ 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.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.io.File; @@ -39,7 +39,9 @@ public class ProgramsTest { testPipeline = new SourceLoaderStage() .then(new LexerStage()) .then(new ParserStage()) + .then(new LinterStage(LinterStage.Mode.SEMANTIC)) .thenConditional(true, new OptimizationStage(9)) + .then(ProgramsTest::mockOUnit) .then(new ExecutionStage()) .then((stagesData, input) -> { input.accept(testFunctionsExecutor); @@ -47,8 +49,7 @@ public class ProgramsTest { }); } - @BeforeEach - public void initialize() { + private static Node mockOUnit(StagesData stagesData, Node input) { ScopeHandler.resetScope(); // Let's mock junit methods as ounit functions ScopeHandler.setFunction("assertEquals", (args) -> { @@ -84,6 +85,7 @@ public class ProgramsTest { } return NumberValue.ONE; }); + return input; } @ParameterizedTest