diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index f6cef06d3..27344b7ad 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -31,12 +31,19 @@ All notable changes to this project will be documented in this file. ### Changed -* **Deprecation:** Changed the syntax of properties to use the `@` prefix for Mindustry built-in variables (e.g. `vault1.@coal`). Omitting the `@` prefix from a property name generates a deprecation warning. -* **Deprecation:** Changed Mindustry Logic functions to require the `out` modifier when passing in an output argument, in the same way as the user-defined functions. Omitting the `out` modifier from a Mindustry Logic function calls generates a deprecation warning. * Changed the [Loop Unrolling optimization](doc/syntax/SYNTAX-6-OPTIMIZATIONS.markdown#loop-unrolling) to replace output iterator variables with the variable assigned to them. * Changed the [Single Step Eliminator](doc/syntax/SYNTAX-6-OPTIMIZATIONS.markdown#single-step-elimination) to remove a jump is if there is an identical jump preceding it and there are no other jumps or labels between them. Active on `experimental`. * Changed the expression evaluator to evaluate operations over known built-in values. The change enhances the Data Flow and Jump Normalization optimizations. -* Changed the Schemacode compiler to correctly output positions of error messages generated by Mindcode compiler, taking into account both the source file and/or position of the Mindcode program or program snippet within the source file. +* Changed the Schemacode compiler to correctly output positions of error messages generated by both the Schemacode and Mindcode compilers, taking into account both the source file and/or position of the Mindcode program or program snippet within the source file. + +### Deprecated + +* Deprecated the syntax of properties omitting the `@` prefix for Mindustry built-in variables (e.g. `vault1.coal` instead of `vault1.@coal`). +* Deprecated the syntax of Mindustry Logic function calls not requiring the `out` modifier when passing in an output argument. + +### Miscellaneous + +* Improved the command-line tool output of the Compile Schemacode action. ## 2.4.0 - 2024-10-23 @@ -97,12 +104,12 @@ All notable changes to this project will be documented in this file. ### Deprecated -- Deprecated the original Mindcode syntax. -- Deprecated the usage of parentheses around the list of values in list iteration loops. -- Deprecated escaped double quotes in string literals. -- Deprecated kebab-case identifiers (note that built-in mlog variables, such as `@blast-compound`, will continue to be supported). -- Deprecated the `printf()` function in language target `ML7A` and earlier. -- Deprecated the `configure` property. This property from Mindustry Logic 6 was replaced by `config` in Mindustry Logic 7. +* Deprecated the original Mindcode syntax. +* Deprecated the usage of parentheses around the list of values in list iteration loops. +* Deprecated escaped double quotes in string literals. +* Deprecated kebab-case identifiers (note that built-in mlog variables, such as `@blast-compound`, will continue to be supported). +* Deprecated the `printf()` function in language target `ML7A` and earlier. +* Deprecated the `configure` property. This property from Mindustry Logic 6 was replaced by `config` in Mindustry Logic 7. ### Miscellaneous diff --git a/compiler/src/main/java/info/teksol/mindcode/AstElement.java b/compiler/src/main/java/info/teksol/mindcode/AstElement.java new file mode 100644 index 000000000..24726833d --- /dev/null +++ b/compiler/src/main/java/info/teksol/mindcode/AstElement.java @@ -0,0 +1,7 @@ +package info.teksol.mindcode; + +public interface AstElement { + + InputPosition inputPosition(); + +} diff --git a/compiler/src/main/java/info/teksol/mindcode/CompilerMessage.java b/compiler/src/main/java/info/teksol/mindcode/CompilerMessage.java index c3437ff06..16203bef4 100644 --- a/compiler/src/main/java/info/teksol/mindcode/CompilerMessage.java +++ b/compiler/src/main/java/info/teksol/mindcode/CompilerMessage.java @@ -1,7 +1,60 @@ package info.teksol.mindcode; -public interface CompilerMessage extends MindcodeMessage { +import org.intellij.lang.annotations.PrintFormat; - InputPosition inputPosition(); +import java.util.Locale; +import java.util.Objects; +import java.util.function.Function; +public record CompilerMessage(MessageLevel level, InputPosition inputPosition, String message) implements MindcodeMessage { + + public CompilerMessage { + Objects.requireNonNull(level); + Objects.requireNonNull(inputPosition); + Objects.requireNonNull(message); + } + + @Override + public MindcodeMessage translatePosition(InputPositionTranslator translator) { + if (!inputPosition.isEmpty()) { + InputPosition translated = translator.apply(inputPosition); + if (translated != inputPosition) { + return new CompilerMessage(level, translated, message); + } + } + return this; + } + + public static CompilerMessage error(InputPosition inputPosition, @PrintFormat String format, Object... args) { + Objects.requireNonNull(inputPosition); + return new CompilerMessage(MessageLevel.ERROR, inputPosition, String.format(Locale.US, format, args)); + } + + public static CompilerMessage warn(InputPosition inputPosition, @PrintFormat String format, Object... args) { + Objects.requireNonNull(inputPosition); + return new CompilerMessage(MessageLevel.WARNING, inputPosition, String.format(Locale.US, format, args)); + } + + public static CompilerMessage info(InputPosition inputPosition, @PrintFormat String format, Object... args) { + Objects.requireNonNull(inputPosition); + return new CompilerMessage(MessageLevel.INFO, inputPosition, String.format(Locale.US, format, args)); + } + + public static CompilerMessage debug(InputPosition inputPosition, @PrintFormat String format, Object... args) { + Objects.requireNonNull(inputPosition); + return new CompilerMessage(MessageLevel.DEBUG, inputPosition, String.format(Locale.US, format, args)); + } + + public String formatMessage(Function positionFormatter) { + return positionFormatter.apply(inputPosition()) + " " + (isErrorOrWarning() ? level().getTitle() + ": " : "") + formatMessage(); + } + + @Override + public String toString() { + return "MindcodeCompilerMessage{" + + "level=" + level + + ", inputPosition=" + inputPosition + + ", message='" + message + '\'' + + '}'; + } } diff --git a/compiler/src/main/java/info/teksol/mindcode/MindcodeErrorListener.java b/compiler/src/main/java/info/teksol/mindcode/MindcodeErrorListener.java index f054b4f8d..ab193df81 100644 --- a/compiler/src/main/java/info/teksol/mindcode/MindcodeErrorListener.java +++ b/compiler/src/main/java/info/teksol/mindcode/MindcodeErrorListener.java @@ -1,6 +1,5 @@ package info.teksol.mindcode; -import info.teksol.mindcode.compiler.MindcodeCompilerMessage; import info.teksol.mindcode.grammar.MissingSemicolonException; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.atn.ATNConfigSet; @@ -34,10 +33,10 @@ public int getAmbiguities() { return ambiguities; } - private final Set reportedMessages = new HashSet<>(); + private final Set reportedMessages = new HashSet<>(); private void reportError(int line, int charPositionInLine, @PrintFormat String format, Object... args) { - MindcodeCompilerMessage message = MindcodeCompilerMessage.error( + CompilerMessage message = CompilerMessage.error( new InputPosition(inputFile, line, charPositionInLine + 1), format, args); if (reportedMessages.add(message)) { messageConsumer.accept(message); diff --git a/compiler/src/main/java/info/teksol/mindcode/ast/AstNode.java b/compiler/src/main/java/info/teksol/mindcode/ast/AstNode.java index 21f5f381b..b9598daea 100644 --- a/compiler/src/main/java/info/teksol/mindcode/ast/AstNode.java +++ b/compiler/src/main/java/info/teksol/mindcode/ast/AstNode.java @@ -1,16 +1,14 @@ package info.teksol.mindcode.ast; -import info.teksol.mindcode.InputPosition; +import info.teksol.mindcode.AstElement; import info.teksol.mindcode.compiler.generator.AstContextType; import info.teksol.mindcode.compiler.generator.AstSubcontextType; import java.util.List; -public interface AstNode { +public interface AstNode extends AstElement { List getChildren(); - InputPosition getInputPosition(); - AstContextType getContextType(); AstSubcontextType getSubcontextType(); diff --git a/compiler/src/main/java/info/teksol/mindcode/ast/AstNodeBuilder.java b/compiler/src/main/java/info/teksol/mindcode/ast/AstNodeBuilder.java index d96a9d358..2e052c06f 100644 --- a/compiler/src/main/java/info/teksol/mindcode/ast/AstNodeBuilder.java +++ b/compiler/src/main/java/info/teksol/mindcode/ast/AstNodeBuilder.java @@ -1,7 +1,6 @@ package info.teksol.mindcode.ast; import info.teksol.mindcode.*; -import info.teksol.mindcode.compiler.MindcodeCompilerMessage; import info.teksol.mindcode.grammar.MindcodeBaseVisitor; import info.teksol.mindcode.grammar.MindcodeParser; import org.antlr.v4.runtime.ParserRuleContext; @@ -35,11 +34,11 @@ public static Seq generate(InputFile inputFile, Consumer messag } private void error(InputPosition position, @PrintFormat String format, Object... args) { - messageConsumer.accept(MindcodeCompilerMessage.error(position, format, args)); + messageConsumer.accept(CompilerMessage.error(position, format, args)); } private void warn(InputPosition position, @PrintFormat String format, Object... args) { - messageConsumer.accept(MindcodeCompilerMessage.warn(position, format, args)); + messageConsumer.accept(CompilerMessage.warn(position, format, args)); } @Override @@ -463,7 +462,7 @@ public AstNode visitFunction_declaration(MindcodeParser.Function_declarationCont if (parameters.size() > offset) { parameters.subList(0, parameters.size() - offset).stream() .filter(FunctionParameter::isVarArgs) - .forEach(p -> error(p.getInputPosition(), + .forEach(p -> error(p.inputPosition(), "Only the last parameter of an inline function can be declared as vararg.")); } diff --git a/compiler/src/main/java/info/teksol/mindcode/ast/BaseAstNode.java b/compiler/src/main/java/info/teksol/mindcode/ast/BaseAstNode.java index 5ed5b96e1..03a2e88c4 100644 --- a/compiler/src/main/java/info/teksol/mindcode/ast/BaseAstNode.java +++ b/compiler/src/main/java/info/teksol/mindcode/ast/BaseAstNode.java @@ -42,7 +42,7 @@ protected BaseAstNode(InputPosition inputPosition, List child this.children = safeCopy(tmp); } - public InputPosition getInputPosition() { + public InputPosition inputPosition() { return inputPosition; } diff --git a/compiler/src/main/java/info/teksol/mindcode/ast/NumericValue.java b/compiler/src/main/java/info/teksol/mindcode/ast/NumericValue.java index b8f7d10de..90f301c1b 100644 --- a/compiler/src/main/java/info/teksol/mindcode/ast/NumericValue.java +++ b/compiler/src/main/java/info/teksol/mindcode/ast/NumericValue.java @@ -24,7 +24,7 @@ public NumericValue(InputPosition inputPosition, double value) { * @return numeric literal representation of the value, or null if literal representation doesn't exist */ public NumericLiteral toNumericLiteral(InstructionProcessor instructionProcessor) { - return instructionProcessor.mlogFormat(value).map(str -> new NumericLiteral(getInputPosition(), str)).orElse(null); + return instructionProcessor.mlogFormat(value).map(str -> new NumericLiteral(inputPosition(), str)).orElse(null); } @Override diff --git a/compiler/src/main/java/info/teksol/mindcode/ast/Seq.java b/compiler/src/main/java/info/teksol/mindcode/ast/Seq.java index 180133e0b..e7d0ac218 100644 --- a/compiler/src/main/java/info/teksol/mindcode/ast/Seq.java +++ b/compiler/src/main/java/info/teksol/mindcode/ast/Seq.java @@ -26,7 +26,7 @@ public static Seq append(Seq first, Seq second) { } else if (second == null) { return first; } else { - return new Seq(second.getInputPosition(), first, second); + return new Seq(second.inputPosition(), first, second); } } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/LogicInstructionPrinter.java b/compiler/src/main/java/info/teksol/mindcode/compiler/LogicInstructionPrinter.java index aacba1d8a..35810e4f1 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/LogicInstructionPrinter.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/LogicInstructionPrinter.java @@ -129,8 +129,8 @@ public static String toStringWithSourceCode(InstructionProcessor instructionProc addArgs(instructionProcessor.getPrintArgumentCount(instruction), lineBuffer, instruction); AstContext astContext = instruction.getAstContext(); - if (astContext.node() != null && astContext.node().getInputPosition() != null) { - InputFile file = astContext.node().getInputPosition().inputFile(); + if (astContext.node() != null && astContext.node().inputPosition() != null) { + InputFile file = astContext.node().inputPosition().inputFile(); String srcLine = "** Corresponding source code line not found! **"; List lines = allLines.get(file); if (lines == null) { @@ -138,7 +138,7 @@ public static String toStringWithSourceCode(InstructionProcessor instructionProc allLines.put(file, lines); } - int line = astContext.node().getInputPosition().line() - 1; + int line = astContext.node().inputPosition().line() - 1; if (line == prevLine) { srcLine = "..."; } else if (line >= 0 && line < lines.size()) { diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/MindcodeCompiler.java b/compiler/src/main/java/info/teksol/mindcode/compiler/MindcodeCompiler.java index 41417bd51..2279f9ee9 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/MindcodeCompiler.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/MindcodeCompiler.java @@ -201,7 +201,7 @@ private GeneratorOutput generateCode(Seq program) { private List optimize(GeneratorOutput generatorOutput) { messageConsumer.accept( - MindcodeOptimizerMessage.debug("%s", profile.getOptimizationLevels().entrySet().stream() + OptimizerMessage.debug("%s", profile.getOptimizationLevels().entrySet().stream() .sorted(Comparator.comparing(e -> e.getKey().getOptionName())) .map(e -> e.getKey().getOptionName() + " = " + e.getValue().name().toLowerCase()) .collect(Collectors.joining(",\n ", "Active optimizations:\n ", "\n")) diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/MindcodeCompilerMessage.java b/compiler/src/main/java/info/teksol/mindcode/compiler/MindcodeCompilerMessage.java deleted file mode 100644 index 65dc6f0f5..000000000 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/MindcodeCompilerMessage.java +++ /dev/null @@ -1,64 +0,0 @@ -package info.teksol.mindcode.compiler; - -import info.teksol.mindcode.InputPosition; -import info.teksol.mindcode.InputPositionTranslator; -import info.teksol.mindcode.MessageLevel; -import info.teksol.mindcode.MindcodeMessage; -import org.intellij.lang.annotations.PrintFormat; - -import java.util.Locale; -import java.util.Objects; -import java.util.function.Function; - -public record MindcodeCompilerMessage(MessageLevel level, InputPosition inputPosition, String message) implements MindcodeMessage { - - public MindcodeCompilerMessage { - Objects.requireNonNull(level); - Objects.requireNonNull(inputPosition); - Objects.requireNonNull(message); - } - - @Override - public MindcodeMessage translatePosition(InputPositionTranslator translator) { - if (!inputPosition.isEmpty()) { - InputPosition translated = translator.apply(inputPosition); - if (translated != inputPosition) { - return new MindcodeCompilerMessage(level, translated, message); - } - } - return this; - } - - public static MindcodeCompilerMessage error(InputPosition inputPosition, @PrintFormat String format, Object... args) { - Objects.requireNonNull(inputPosition); - return new MindcodeCompilerMessage(MessageLevel.ERROR, inputPosition, String.format(Locale.US, format, args)); - } - - public static MindcodeCompilerMessage warn(InputPosition inputPosition, @PrintFormat String format, Object... args) { - Objects.requireNonNull(inputPosition); - return new MindcodeCompilerMessage(MessageLevel.WARNING, inputPosition, String.format(Locale.US, format, args)); - } - - public static MindcodeCompilerMessage info(InputPosition inputPosition, @PrintFormat String format, Object... args) { - Objects.requireNonNull(inputPosition); - return new MindcodeCompilerMessage(MessageLevel.INFO, inputPosition, String.format(Locale.US, format, args)); - } - - public static MindcodeCompilerMessage debug(InputPosition inputPosition, @PrintFormat String format, Object... args) { - Objects.requireNonNull(inputPosition); - return new MindcodeCompilerMessage(MessageLevel.DEBUG, inputPosition, String.format(Locale.US, format, args)); - } - - public String formatMessage(Function positionFormatter) { - return positionFormatter.apply(inputPosition()) + " " + (isErrorOrWarning() ? level().getTitle() + ": " : "") + formatMessage(); - } - - @Override - public String toString() { - return "MindcodeCompilerMessage{" + - "level=" + level + - ", inputPosition=" + inputPosition + - ", message='" + message + '\'' + - '}'; - } -} diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/functions/DeprecatedPropertyHandler.java b/compiler/src/main/java/info/teksol/mindcode/compiler/functions/DeprecatedPropertyHandler.java index 610c1ac23..0fe357cdd 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/functions/DeprecatedPropertyHandler.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/functions/DeprecatedPropertyHandler.java @@ -1,7 +1,7 @@ package info.teksol.mindcode.compiler.functions; +import info.teksol.mindcode.CompilerMessage; import info.teksol.mindcode.ast.AstNode; -import info.teksol.mindcode.compiler.MindcodeCompilerMessage; import info.teksol.mindcode.compiler.generator.AbstractMessageEmitter; import info.teksol.mindcode.compiler.instructions.LogicInstruction; import info.teksol.mindcode.compiler.instructions.MlogInstruction; @@ -73,7 +73,7 @@ public String getNote() { @Override public LogicValue handleProperty(AstNode node, Consumer program, LogicValue target, List arguments) { if (!warningEmitted) { - functionMapper.getMessageConsumer().accept(MindcodeCompilerMessage.warn(node.getInputPosition(), + functionMapper.getMessageConsumer().accept(CompilerMessage.warn(node.inputPosition(), "Function '%s' is no longer supported in Mindustry Logic version %s; using '%s' instead.", deprecated, functionMapper.processorVersion, replacement.getName())); warningEmitted = true; diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/AbstractMessageEmitter.java b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/AbstractMessageEmitter.java index 6c664d753..75059e730 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/AbstractMessageEmitter.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/AbstractMessageEmitter.java @@ -1,9 +1,9 @@ package info.teksol.mindcode.compiler.generator; +import info.teksol.mindcode.AstElement; +import info.teksol.mindcode.CompilerMessage; import info.teksol.mindcode.InputPosition; import info.teksol.mindcode.MindcodeMessage; -import info.teksol.mindcode.ast.AstNode; -import info.teksol.mindcode.compiler.MindcodeCompilerMessage; import org.intellij.lang.annotations.PrintFormat; import java.util.function.Consumer; @@ -21,22 +21,27 @@ public Consumer getMessageConsumer() { } @Override - public void error(AstNode node, @PrintFormat String format, Object... args) { - messageConsumer.accept(MindcodeCompilerMessage.error(node.getInputPosition(), format, args)); + public void addMessage(MindcodeMessage message) { + messageConsumer.accept(message); + } + + @Override + public void error(AstElement element, @PrintFormat String format, Object... args) { + messageConsumer.accept(CompilerMessage.error(element.inputPosition(), format, args)); } @Override public void error(InputPosition position, @PrintFormat String format, Object... args) { - messageConsumer.accept(MindcodeCompilerMessage.error(position, format, args)); + messageConsumer.accept(CompilerMessage.error(position, format, args)); } @Override - public void warn(AstNode node, @PrintFormat String format, Object... args) { - messageConsumer.accept(MindcodeCompilerMessage.warn(node.getInputPosition(), format, args)); + public void warn(AstElement element, @PrintFormat String format, Object... args) { + messageConsumer.accept(CompilerMessage.warn(element.inputPosition(), format, args)); } @Override public void warn(InputPosition position, @PrintFormat String format, Object... args) { - messageConsumer.accept(MindcodeCompilerMessage.warn(position, format, args)); + messageConsumer.accept(CompilerMessage.warn(position, format, args)); } } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/AstContext.java b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/AstContext.java index 9d9b0344f..f8783b225 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/AstContext.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/AstContext.java @@ -285,7 +285,7 @@ public AstNode node() { } public InputPosition inputPosition() { - return node != null ? node.getInputPosition() : null; + return node != null ? node.inputPosition() : null; } public String functionPrefix() { diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/CallGraphCreator.java b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/CallGraphCreator.java index 6c6bd098b..08de0e632 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/CallGraphCreator.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/CallGraphCreator.java @@ -59,11 +59,11 @@ private void visitNode(AstNode nodeToVisit) { private void visitStackAllocation(StackAllocation node) { if (allocatedStack != null) { - error(node.getInputPosition(), "Multiple stack allocations."); + error(node.inputPosition(), "Multiple stack allocations."); } if (!activeFunction.isMain()) { - error(node.getInputPosition(), "Stack allocation must not be declared within a function."); + error(node.inputPosition(), "Stack allocation must not be declared within a function."); } allocatedStack = node; diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/ConstantExpressionEvaluator.java b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/ConstantExpressionEvaluator.java index 72b6db2ca..6bb98dc6c 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/ConstantExpressionEvaluator.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/ConstantExpressionEvaluator.java @@ -67,7 +67,7 @@ private AstNode evaluateBinaryOp(BinaryOp node) { if (getObject(a) instanceof MindustryString || getObject(b) instanceof MindustryString) { // Only addition of a string and a non-null value is supported if (operation == Operation.ADD && a != null && b != null) { - return new StringLiteral(node.getInputPosition(), a.print() + b.print()); + return new StringLiteral(node.inputPosition(), a.print() + b.print()); } else { error(node, "Unsupported string expression."); return node; @@ -103,15 +103,15 @@ private AstNode evaluatePartially(BinaryOp original, MindustryVariable fixed, As // If the fixed value is nonzero, collapses to true // If the fixed value is zero, evaluates to the other node - case "or" -> fixed.getDoubleValue() != 0 ? new BooleanLiteral(original.getInputPosition(), true) : other; + case "or" -> fixed.getDoubleValue() != 0 ? new BooleanLiteral(original.inputPosition(), true) : other; // If the fixed value is zero, evaluates to false // Nonzero values cannot be resolved - case "&", "&&" -> fixed.getDoubleValue() == 0 ? new BooleanLiteral(original.getInputPosition(), false) : original; + case "&", "&&" -> fixed.getDoubleValue() == 0 ? new BooleanLiteral(original.inputPosition(), false) : original; // If the fixed value is zero (= false), evaluates to false // If the fixed value is nonzero, evaluates to the other node - case "and" -> fixed.getDoubleValue() == 0 ? new BooleanLiteral(original.getInputPosition(), false) : other; + case "and" -> fixed.getDoubleValue() == 0 ? new BooleanLiteral(original.inputPosition(), false) : other; default -> original; }; @@ -133,7 +133,7 @@ private NumericLiteral ensureMlog(Constant node, NumericValue value) { if (numericLiteral == null) { error(node, "Value assigned to constant '%s' (%s) doesn't have a valid mlog representation.", node.getName(), value.getAsDouble()); - return new NumericLiteral(node.getInputPosition(), "0"); + return new NumericLiteral(node.inputPosition(), "0"); } return numericLiteral; } @@ -199,7 +199,7 @@ private AstNode evaluateUnaryOp(UnaryOp node) { private AstNode evaluateVarRef(VarRef node) { if (constants.containsKey(node.getName())) { - return constants.get(node.getName()).withInputPosition(node.getInputPosition()); + return constants.get(node.getName()).withInputPosition(node.inputPosition()); } else { return node; } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/LogicFunction.java b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/LogicFunction.java index d0b328fda..ee6ae4a53 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/LogicFunction.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/LogicFunction.java @@ -320,7 +320,7 @@ public int hashCode() { } public InputPosition getInputPosition() { - return declaration != null ? declaration.getInputPosition() : null; + return declaration != null ? declaration.inputPosition() : null; } public String format() { diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/LogicInstructionGenerator.java b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/LogicInstructionGenerator.java index a4ba44b4e..93117d3ae 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/LogicInstructionGenerator.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/LogicInstructionGenerator.java @@ -1048,7 +1048,7 @@ public LogicValue visitAssignment(Assignment node) { LogicArgument prop = visit(propertyAccess.getProperty()); String propertyName = prop instanceof LogicBuiltIn lb ? lb.getName().substring(1) : prop.toMlog(); // Implicit out modifier as this is an assignment - LogicFunctionArgument argument = new LogicFunctionArgument(node.getValue().getInputPosition(), rvalue, false, false); + LogicFunctionArgument argument = new LogicFunctionArgument(node.getValue().inputPosition(), rvalue, false, false); if (functionMapper.handleProperty(node, instructions::add, propertyName, propTarget, List.of(argument)) == null) { error(node, "Undefined property '%s.%s'.", propTarget.toMlog(), prop.toMlog()); return NULL; @@ -1140,7 +1140,7 @@ public AstNodeListElement(AstNode node) { @Override public InputPosition getInputPosition() { - return node.getInputPosition(); + return node.inputPosition(); } @Override @@ -1188,7 +1188,7 @@ public LogicValue visitForEachStatement(ForEachExpression node) { return VOID; } - loopStack.enterLoop(node.getInputPosition(), node.getLabel(), exitLabel, contLabel); + loopStack.enterLoop(node.inputPosition(), node.getLabel(), exitLabel, contLabel); // All but the last value int limit = values.size() - iterators.size(); @@ -1317,7 +1317,7 @@ public LogicValue visitRangedForExpression(RangedForExpression node) { final LogicLabel beginLabel = nextLabel(); final LogicLabel continueLabel = nextLabel(); final LogicLabel doneLabel = nextLabel(); - loopStack.enterLoop(node.getInputPosition(), node.getLabel(), doneLabel, continueLabel); + loopStack.enterLoop(node.inputPosition(), node.getLabel(), doneLabel, continueLabel); setSubcontextType(AstSubcontextType.CONDITION, multiplier); emit(createLabel(beginLabel)); emit(createJump(doneLabel, node.getRange().maxValueComparison().inverse(), variable, fixedUpperBound)); @@ -1343,7 +1343,7 @@ public LogicValue visitWhileStatement(WhileExpression node) { final LogicLabel continueLabel = nextLabel(); final LogicLabel doneLabel = nextLabel(); // Not using try/finally to ensure stack consistency - any exception stops compilation anyway - loopStack.enterLoop(node.getInputPosition(), node.getLabel(), doneLabel, continueLabel); + loopStack.enterLoop(node.inputPosition(), node.getLabel(), doneLabel, continueLabel); setSubcontextType(AstSubcontextType.CONDITION, LOOP_REPETITIONS); emit(createLabel(beginLabel)); final LogicValue cond = visit(node.getCondition()); @@ -1367,7 +1367,7 @@ public LogicValue visitDoWhileStatement(DoWhileExpression node) { final LogicLabel continueLabel = nextLabel(); final LogicLabel doneLabel = nextLabel(); // Not using try/finally to ensure stack consistency - any exception stops compilation anyway - loopStack.enterLoop(node.getInputPosition(), node.getLabel(), doneLabel, continueLabel); + loopStack.enterLoop(node.inputPosition(), node.getLabel(), doneLabel, continueLabel); setSubcontextType(AstSubcontextType.BODY, LOOP_REPETITIONS); emit(createLabel(beginLabel)); visit(node.getBody()); @@ -1736,22 +1736,22 @@ public LogicValue visitStackAllocation(StackAllocation node) { @Override public LogicValue visitBreakStatement(BreakStatement node) { - final LogicLabel label = loopStack.getBreakLabel(node.getInputPosition(), node.getLabel()); + final LogicLabel label = loopStack.getBreakLabel(node.inputPosition(), node.getLabel()); emit(createJumpUnconditional(label)); return VOID; } @Override public LogicValue visitContinueStatement(ContinueStatement node) { - final LogicLabel label = loopStack.getContinueLabel(node.getInputPosition(), node.getLabel()); + final LogicLabel label = loopStack.getContinueLabel(node.inputPosition(), node.getLabel()); emit(createJumpUnconditional(label)); return VOID; } @Override public LogicValue visitReturnStatement(ReturnStatement node) { - final LogicValue retval = returnStack.getReturnValue(node.getInputPosition()); - final LogicLabel label = returnStack.getReturnLabel(node.getInputPosition()); + final LogicValue retval = returnStack.getReturnValue(node.inputPosition()); + final LogicLabel label = returnStack.getReturnLabel(node.inputPosition()); if (retval instanceof LogicVariable target) { if (node.getRetval() instanceof VoidLiteral) { error(node, "Missing return value in 'return' statement."); diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/MessageEmitter.java b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/MessageEmitter.java index 9ece670d2..e23d53535 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/generator/MessageEmitter.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/generator/MessageEmitter.java @@ -1,20 +1,22 @@ package info.teksol.mindcode.compiler.generator; +import info.teksol.mindcode.AstElement; import info.teksol.mindcode.InputPosition; import info.teksol.mindcode.MindcodeMessage; -import info.teksol.mindcode.ast.AstNode; import org.intellij.lang.annotations.PrintFormat; import java.util.function.Consumer; public interface MessageEmitter { - void error(AstNode node, @PrintFormat String format, Object... args); + Consumer getMessageConsumer(); - void error(InputPosition position, @PrintFormat String format, Object... args); + void addMessage(MindcodeMessage message); - Consumer getMessageConsumer(); + void error(AstElement node, @PrintFormat String format, Object... args); + + void error(InputPosition position, @PrintFormat String format, Object... args); - void warn(AstNode node, @PrintFormat String format, Object... args); + void warn(AstElement node, @PrintFormat String format, Object... args); void warn(InputPosition position, @PrintFormat String format, Object... args); } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/instructions/BaseInstructionProcessor.java b/compiler/src/main/java/info/teksol/mindcode/compiler/instructions/BaseInstructionProcessor.java index a10e05911..600832d49 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/instructions/BaseInstructionProcessor.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/instructions/BaseInstructionProcessor.java @@ -1,10 +1,10 @@ package info.teksol.mindcode.compiler.instructions; +import info.teksol.mindcode.CompilerMessage; import info.teksol.mindcode.InputPosition; import info.teksol.mindcode.MindcodeInternalError; import info.teksol.mindcode.MindcodeMessage; import info.teksol.mindcode.compiler.CompilerProfile; -import info.teksol.mindcode.compiler.MindcodeCompilerMessage; import info.teksol.mindcode.compiler.generator.AbstractMessageEmitter; import info.teksol.mindcode.compiler.generator.AstContext; import info.teksol.mindcode.compiler.generator.AstSubcontextType; @@ -661,7 +661,7 @@ protected Optional mlogFormat(double value, String literal) { if (relDiff > 1e-9) { // This warning doesn't come with an input position // This will no longer happen in ML8 - won't fix. - messageConsumer.accept(MindcodeCompilerMessage.warn(InputPosition.EMPTY, + messageConsumer.accept(CompilerMessage.warn(InputPosition.EMPTY, "Loss of precision while creating mlog literals (original value %s, encoded value %s)", literal, mlog)); } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/AbstractOptimizer.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/AbstractOptimizer.java index f6927311e..b7d640f83 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/AbstractOptimizer.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/AbstractOptimizer.java @@ -78,7 +78,7 @@ protected boolean experimental() { } protected void emitMessage(MessageLevel level, @PrintFormat String format, Object... args) { - messageRecipient.accept(new MindcodeOptimizerMessage(level, String.format(format, args))); + messageRecipient.accept(new OptimizerMessage(level, String.format(format, args))); } @Override diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/CaseSwitcher.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/CaseSwitcher.java index 109370259..24a7b2df8 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/CaseSwitcher.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/CaseSwitcher.java @@ -179,7 +179,7 @@ public OptimizationResult apply(int costLimit) { @Override public String toString() { - return getName() + ": convert case at line " + astContext.node().getInputPosition().line(); + return getName() + ": convert case at line " + astContext.node().inputPosition().line(); } } } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/FunctionInliner.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/FunctionInliner.java index cdbb7864c..60b1896dd 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/FunctionInliner.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/FunctionInliner.java @@ -227,7 +227,7 @@ public OptimizationResult apply(int costLimit) { @Override public String toString() { - return getName() + ": inline function call at line " + astContext.node().getInputPosition().line(); + return getName() + ": inline function call at line " + astContext.node().inputPosition().line(); } } } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/LoopOptimizer.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/LoopOptimizer.java index d49ac58b4..4acbdb713 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/LoopOptimizer.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/LoopOptimizer.java @@ -174,7 +174,7 @@ public OptimizationResult apply(int costLimit) { @Override public String toString() { - return getName() + ": replicate condition at line " + astContext.node().getInputPosition().line(); + return getName() + ": replicate condition at line " + astContext.node().inputPosition().line(); } } } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/LoopUnroller.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/LoopUnroller.java index b77d73f43..69336a4c6 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/LoopUnroller.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/LoopUnroller.java @@ -484,7 +484,7 @@ public OptimizationResult apply(int costLimit) { @Override public String toString() { - return getName() + ": unroll loop at line " + astContext.node().getInputPosition().line(); + return getName() + ": unroll loop at line " + astContext.node().inputPosition().line(); } } @@ -500,7 +500,7 @@ public OptimizationResult apply(int costLimit) { @Override public String toString() { - return getName() + ": unroll iteration loop at line " + astContext.node().getInputPosition().line(); + return getName() + ": unroll iteration loop at line " + astContext.node().inputPosition().line(); } } } diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/MindcodeOptimizerMessage.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/MindcodeOptimizerMessage.java deleted file mode 100644 index 16fef1981..000000000 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/MindcodeOptimizerMessage.java +++ /dev/null @@ -1,44 +0,0 @@ -package info.teksol.mindcode.compiler.optimization; - -import info.teksol.mindcode.MessageLevel; -import info.teksol.mindcode.MindcodeMessage; -import org.intellij.lang.annotations.PrintFormat; - -import java.util.Locale; -import java.util.Objects; - -public record MindcodeOptimizerMessage(MessageLevel level, String message) implements MindcodeMessage { - - public MindcodeOptimizerMessage { - Objects.requireNonNull(level); - Objects.requireNonNull(message); - } - - public static MindcodeOptimizerMessage error(@PrintFormat String format, Object... args) { - return new MindcodeOptimizerMessage(MessageLevel.ERROR, String.format(Locale.US, format, args)); - } - - public static MindcodeOptimizerMessage warn(@PrintFormat String format, Object... args) { - return new MindcodeOptimizerMessage(MessageLevel.WARNING, String.format(Locale.US, format, args)); - } - - public static MindcodeOptimizerMessage info(@PrintFormat String format, Object... args) { - return new MindcodeOptimizerMessage(MessageLevel.INFO, String.format(Locale.US, format, args)); - } - - public static MindcodeOptimizerMessage debug(String message) { - return new MindcodeOptimizerMessage(MessageLevel.DEBUG, message); - } - - public static MindcodeOptimizerMessage debug(@PrintFormat String format, Object... args) { - return new MindcodeOptimizerMessage(MessageLevel.DEBUG, String.format(Locale.US, format, args)); - } - - @Override - public String toString() { - return "MindcodeOptimizerMessage{" + - "level=" + level + - ", message='" + message + '\'' + - '}'; - } -} diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/OptimizationCoordinator.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/OptimizationCoordinator.java index 3228da7cb..587fa2a40 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/OptimizationCoordinator.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/OptimizationCoordinator.java @@ -61,7 +61,7 @@ public List optimize(GeneratorOutput generatorOutput) { generatorOutput.callGraph(), generatorOutput.rootAstContext()); int count = program.stream().mapToInt(LogicInstruction::getRealSize).sum(); - messageRecipient.accept(MindcodeOptimizerMessage.info("%6d instructions before optimizations.", count)); + messageRecipient.accept(OptimizerMessage.info("%6d instructions before optimizations.", count)); debugPrinter.registerIteration(null, "", List.copyOf(program)); @@ -73,16 +73,16 @@ public List optimize(GeneratorOutput generatorOutput) { modified = optimizePhase(ITERATED, optimizers, pass, generatorOutput); } if (modified) { - messageRecipient.accept(MindcodeOptimizerMessage.warn("Optimization passes limit (%d) reached.", profile.getOptimizationPasses())); + messageRecipient.accept(OptimizerMessage.warn("Optimization passes limit (%d) reached.", profile.getOptimizationPasses())); } optimizePhase(FINAL, optimizers, 0, generatorOutput); optimizers.values().forEach(Optimizer::generateFinalMessages); int newCount = program.stream().mapToInt(LogicInstruction::getRealSize).sum(); - messageRecipient.accept(MindcodeOptimizerMessage.info("%6d instructions after optimizations.", newCount)); + messageRecipient.accept(OptimizerMessage.info("%6d instructions after optimizations.", newCount)); if (modified) { - messageRecipient.accept(MindcodeOptimizerMessage.warn("\nOptimization passes limited at %d.", + messageRecipient.accept(OptimizerMessage.warn("\nOptimization passes limited at %d.", profile.getOptimizationPasses())); } @@ -155,7 +155,7 @@ private boolean optimizePhase(OptimizationPhase phase, Map outputPossibleOptimization(t, costLimit, selectedAction, difference)); } @@ -177,12 +177,12 @@ private int codeSize() { .thenComparing(Comparator.comparingInt(OptimizationAction::cost).reversed()); private void outputPossibleOptimization(OptimizationAction opt, int costLimit, OptimizationAction selected, int difference) { - MindcodeOptimizerMessage message; + OptimizerMessage message; if (opt == selected) { - message = MindcodeOptimizerMessage.debug(" * %-60s cost %5d, benefit %10.1f, efficiency %10.1f (%+d instructions)", + message = OptimizerMessage.debug(" * %-60s cost %5d, benefit %10.1f, efficiency %10.1f (%+d instructions)", opt, opt.cost(), opt.benefit(), opt.efficiency(), difference); } else { - message = MindcodeOptimizerMessage.debug(" %s %-60s cost %5d, benefit %10.1f, efficiency %10.1f", + message = OptimizerMessage.debug(" %s %-60s cost %5d, benefit %10.1f, efficiency %10.1f", opt.cost() > costLimit ? "!" : " ", opt, opt.cost(), opt.benefit(), opt.efficiency()); } optimizationStatistics.add(message); diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/OptimizerMessage.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/OptimizerMessage.java new file mode 100644 index 000000000..269829b53 --- /dev/null +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/OptimizerMessage.java @@ -0,0 +1,44 @@ +package info.teksol.mindcode.compiler.optimization; + +import info.teksol.mindcode.MessageLevel; +import info.teksol.mindcode.MindcodeMessage; +import org.intellij.lang.annotations.PrintFormat; + +import java.util.Locale; +import java.util.Objects; + +public record OptimizerMessage(MessageLevel level, String message) implements MindcodeMessage { + + public OptimizerMessage { + Objects.requireNonNull(level); + Objects.requireNonNull(message); + } + + public static OptimizerMessage error(@PrintFormat String format, Object... args) { + return new OptimizerMessage(MessageLevel.ERROR, String.format(Locale.US, format, args)); + } + + public static OptimizerMessage warn(@PrintFormat String format, Object... args) { + return new OptimizerMessage(MessageLevel.WARNING, String.format(Locale.US, format, args)); + } + + public static OptimizerMessage info(@PrintFormat String format, Object... args) { + return new OptimizerMessage(MessageLevel.INFO, String.format(Locale.US, format, args)); + } + + public static OptimizerMessage debug(String message) { + return new OptimizerMessage(MessageLevel.DEBUG, message); + } + + public static OptimizerMessage debug(@PrintFormat String format, Object... args) { + return new OptimizerMessage(MessageLevel.DEBUG, String.format(Locale.US, format, args)); + } + + @Override + public String toString() { + return "MindcodeOptimizerMessage{" + + "level=" + level + + ", message='" + message + '\'' + + '}'; + } +} diff --git a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/ReturnOptimizer.java b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/ReturnOptimizer.java index 7bc393f20..213b7a4cf 100644 --- a/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/ReturnOptimizer.java +++ b/compiler/src/main/java/info/teksol/mindcode/compiler/optimization/ReturnOptimizer.java @@ -119,7 +119,7 @@ public OptimizationResult apply(int costLimit) { @Override public String toString() { - return getName() + ": optimize return at line " + astContext.node().getInputPosition().line(); + return getName() + ": optimize return at line " + astContext.node().inputPosition().line(); } } } diff --git a/compiler/src/main/java/info/teksol/mindcode/logic/LogicFunctionArgument.java b/compiler/src/main/java/info/teksol/mindcode/logic/LogicFunctionArgument.java index 1c4b46379..f921c36e2 100644 --- a/compiler/src/main/java/info/teksol/mindcode/logic/LogicFunctionArgument.java +++ b/compiler/src/main/java/info/teksol/mindcode/logic/LogicFunctionArgument.java @@ -8,11 +8,11 @@ public record LogicFunctionArgument(InputPosition pos, LogicValue value, boolean inModifier, boolean outModifier) { public LogicFunctionArgument(FunctionArgument argument, LogicValue value) { - this(argument.getInputPosition(), Objects.requireNonNull(value), argument.hasInModifier(), argument.hasOutModifier()); + this(argument.inputPosition(), Objects.requireNonNull(value), argument.hasInModifier(), argument.hasOutModifier()); } public LogicFunctionArgument(FunctionArgument argument) { - this(argument.getInputPosition(), null, argument.hasInModifier(), argument.hasOutModifier()); + this(argument.inputPosition(), null, argument.hasInModifier(), argument.hasOutModifier()); } public boolean hasValue() { diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/ExpectedMessages.java b/compiler/src/main/java/info/teksol/util/ExpectedMessages.java similarity index 97% rename from compiler/src/test/java/info/teksol/mindcode/compiler/ExpectedMessages.java rename to compiler/src/main/java/info/teksol/util/ExpectedMessages.java index c6bdbff8e..94e1eb71f 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/ExpectedMessages.java +++ b/compiler/src/main/java/info/teksol/util/ExpectedMessages.java @@ -1,7 +1,8 @@ -package info.teksol.mindcode.compiler; +package info.teksol.util; import com.ibm.icu.impl.Assert; import info.teksol.mindcode.MindcodeMessage; +import org.intellij.lang.annotations.Language; import java.util.ArrayList; import java.util.List; @@ -90,7 +91,7 @@ public ExpectedMessages add(int line, int column, String message) { * @param pattern regex pattern to be expected * @return this instance */ - public ExpectedMessages addRegex(String pattern) { + public ExpectedMessages addRegex(@Language("RegExp") String pattern) { matchers.add(new MatchCounter(new RegexMessageMatcher(pattern))); return this; } @@ -104,7 +105,7 @@ public ExpectedMessages addRegex(String pattern) { * @param pattern regex pattern to be expected * @return this instance */ - public ExpectedMessages addRegex(int line, int column, String pattern) { + public ExpectedMessages addRegex(int line, int column, @Language("RegExp") String pattern) { matchers.add(new MatchCounter(new PositionalMessageMatcher(line, column, new RegexMessageMatcher(pattern)))); return this; } diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/UnexpectedMessageException.java b/compiler/src/main/java/info/teksol/util/UnexpectedMessageException.java similarity index 80% rename from compiler/src/test/java/info/teksol/mindcode/compiler/UnexpectedMessageException.java rename to compiler/src/main/java/info/teksol/util/UnexpectedMessageException.java index ac4cca84e..6ad19ddd0 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/UnexpectedMessageException.java +++ b/compiler/src/main/java/info/teksol/util/UnexpectedMessageException.java @@ -1,4 +1,4 @@ -package info.teksol.mindcode.compiler; +package info.teksol.util; public class UnexpectedMessageException extends RuntimeException { public UnexpectedMessageException(String message) { diff --git a/compiler/src/test/java/info/teksol/emulator/processor/AbstractProcessorTest.java b/compiler/src/test/java/info/teksol/emulator/processor/AbstractProcessorTest.java index a522b9896..1c59dbe31 100644 --- a/compiler/src/test/java/info/teksol/emulator/processor/AbstractProcessorTest.java +++ b/compiler/src/test/java/info/teksol/emulator/processor/AbstractProcessorTest.java @@ -3,9 +3,13 @@ import info.teksol.emulator.blocks.Memory; import info.teksol.emulator.blocks.MindustryBlock; import info.teksol.mindcode.MindcodeMessage; -import info.teksol.mindcode.compiler.*; +import info.teksol.mindcode.compiler.CompilerProfile; +import info.teksol.mindcode.compiler.LogicInstructionLabelResolver; +import info.teksol.mindcode.compiler.LogicInstructionPrinter; +import info.teksol.mindcode.compiler.TimingMessage; import info.teksol.mindcode.compiler.instructions.LogicInstruction; import info.teksol.mindcode.compiler.optimization.*; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; diff --git a/compiler/src/test/java/info/teksol/emulator/processor/AlgorithmsTest.java b/compiler/src/test/java/info/teksol/emulator/processor/AlgorithmsTest.java index 3513dd2e3..2205ab221 100644 --- a/compiler/src/test/java/info/teksol/emulator/processor/AlgorithmsTest.java +++ b/compiler/src/test/java/info/teksol/emulator/processor/AlgorithmsTest.java @@ -3,9 +3,9 @@ import info.teksol.emulator.blocks.Memory; import info.teksol.emulator.blocks.graphics.LogicDisplay; import info.teksol.mindcode.compiler.CompilerProfile; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.optimization.Optimization; import info.teksol.mindcode.compiler.optimization.OptimizationLevel; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.*; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; diff --git a/compiler/src/test/java/info/teksol/emulator/processor/ProcessorTest.java b/compiler/src/test/java/info/teksol/emulator/processor/ProcessorTest.java index ccaf2b89e..4a54e6914 100644 --- a/compiler/src/test/java/info/teksol/emulator/processor/ProcessorTest.java +++ b/compiler/src/test/java/info/teksol/emulator/processor/ProcessorTest.java @@ -1,10 +1,10 @@ package info.teksol.emulator.processor; import info.teksol.mindcode.compiler.CompilerProfile; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.SortCategory; import info.teksol.mindcode.compiler.optimization.Optimization; import info.teksol.mindcode.compiler.optimization.OptimizationLevel; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/emulator/processor/interceptor/AbstractInterceptorTest.java b/compiler/src/test/java/info/teksol/emulator/processor/interceptor/AbstractInterceptorTest.java index 77b68130a..277aa4d6f 100644 --- a/compiler/src/test/java/info/teksol/emulator/processor/interceptor/AbstractInterceptorTest.java +++ b/compiler/src/test/java/info/teksol/emulator/processor/interceptor/AbstractInterceptorTest.java @@ -5,12 +5,12 @@ import info.teksol.emulator.processor.AbstractProcessorTest; import info.teksol.emulator.processor.Processor; import info.teksol.mindcode.MindcodeInternalError; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.LogicInstructionLabelResolver; import info.teksol.mindcode.compiler.instructions.LogicInstruction; import info.teksol.mindcode.compiler.optimization.DebugPrinter; import info.teksol.mindcode.compiler.optimization.DiffDebugPrinter; import info.teksol.mindcode.compiler.optimization.Optimizer; +import info.teksol.util.ExpectedMessages; import java.nio.file.Path; import java.util.List; diff --git a/compiler/src/test/java/info/teksol/mindcode/AbstractAstTest.java b/compiler/src/test/java/info/teksol/mindcode/AbstractAstTest.java index 1ae3426c6..6d592e397 100644 --- a/compiler/src/test/java/info/teksol/mindcode/AbstractAstTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/AbstractAstTest.java @@ -3,12 +3,12 @@ import info.teksol.mindcode.ast.AstNode; import info.teksol.mindcode.ast.AstNodeBuilder; import info.teksol.mindcode.ast.AstPrettyPrinter; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.instructions.InstructionProcessor; import info.teksol.mindcode.compiler.instructions.InstructionProcessorFactory; import info.teksol.mindcode.grammar.AbstractParserTest; import info.teksol.mindcode.logic.ProcessorEdition; import info.teksol.mindcode.logic.ProcessorVersion; +import info.teksol.util.ExpectedMessages; public class AbstractAstTest extends AbstractParserTest { diff --git a/compiler/src/test/java/info/teksol/mindcode/ast/AstNodeBuilderTest.java b/compiler/src/test/java/info/teksol/mindcode/ast/AstNodeBuilderTest.java index 0adf1cefe..d4da8858e 100644 --- a/compiler/src/test/java/info/teksol/mindcode/ast/AstNodeBuilderTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/ast/AstNodeBuilderTest.java @@ -1,8 +1,8 @@ package info.teksol.mindcode.ast; import info.teksol.mindcode.AbstractAstTest; -import info.teksol.mindcode.compiler.ExpectedMessages; -import info.teksol.mindcode.compiler.UnexpectedMessageException; +import info.teksol.util.ExpectedMessages; +import info.teksol.util.UnexpectedMessageException; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/AbstractGeneratorTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/AbstractGeneratorTest.java index 674d1b5ed..40519470b 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/AbstractGeneratorTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/AbstractGeneratorTest.java @@ -14,6 +14,7 @@ import info.teksol.mindcode.compiler.instructions.InstructionProcessorFactory; import info.teksol.mindcode.compiler.instructions.LogicInstruction; import info.teksol.mindcode.logic.*; +import info.teksol.util.ExpectedMessages; import java.util.*; import java.util.function.Consumer; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/DirectiveProcessorTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/DirectiveProcessorTest.java index 2f32e2ea1..0592c8938 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/DirectiveProcessorTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/DirectiveProcessorTest.java @@ -5,6 +5,7 @@ import info.teksol.mindcode.ast.Seq; import info.teksol.mindcode.compiler.optimization.OptimizationLevel; import info.teksol.mindcode.logic.ProcessorVersion; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/LogicInstructionPrinterTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/LogicInstructionPrinterTest.java index a075da322..69df68e00 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/LogicInstructionPrinterTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/LogicInstructionPrinterTest.java @@ -1,5 +1,6 @@ package info.teksol.mindcode.compiler; +import info.teksol.util.UnexpectedMessageException; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/functions/BuiltInFunctionsLogic7Test.java b/compiler/src/test/java/info/teksol/mindcode/compiler/functions/BuiltInFunctionsLogic7Test.java index e5b64dce6..17f45a5b6 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/functions/BuiltInFunctionsLogic7Test.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/functions/BuiltInFunctionsLogic7Test.java @@ -1,8 +1,8 @@ package info.teksol.mindcode.compiler.functions; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.logic.ProcessorVersion; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/functions/BuiltInFunctionsTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/functions/BuiltInFunctionsTest.java index 1e757bb5a..cea39627d 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/functions/BuiltInFunctionsTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/functions/BuiltInFunctionsTest.java @@ -1,7 +1,7 @@ package info.teksol.mindcode.compiler.functions; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/functions/StandardFunctionsLogic8Test.java b/compiler/src/test/java/info/teksol/mindcode/compiler/functions/StandardFunctionsLogic8Test.java index a65d1e2e8..cda220594 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/functions/StandardFunctionsLogic8Test.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/functions/StandardFunctionsLogic8Test.java @@ -1,8 +1,8 @@ package info.teksol.mindcode.compiler.functions; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.logic.ProcessorVersion; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/functions/WorldProcessorFunctionsTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/functions/WorldProcessorFunctionsTest.java index cff148276..122ab1ca6 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/functions/WorldProcessorFunctionsTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/functions/WorldProcessorFunctionsTest.java @@ -1,7 +1,7 @@ package info.teksol.mindcode.compiler.functions; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/AstContextTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/AstContextTest.java index 0423fc0cc..1fccc7aee 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/AstContextTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/AstContextTest.java @@ -252,7 +252,7 @@ public List getChildren() { } @Override - public InputPosition getInputPosition() { + public InputPosition inputPosition() { return null; } diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/CallGraphCreatorTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/CallGraphCreatorTest.java index 36f601072..aad73eb39 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/CallGraphCreatorTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/CallGraphCreatorTest.java @@ -3,7 +3,7 @@ import info.teksol.mindcode.InputPosition; import info.teksol.mindcode.ast.Seq; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/ConstantExpressionEvaluatorTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/ConstantExpressionEvaluatorTest.java index 6c490e37c..3c6367f4d 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/ConstantExpressionEvaluatorTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/ConstantExpressionEvaluatorTest.java @@ -1,9 +1,9 @@ package info.teksol.mindcode.compiler.generator; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.logic.ProcessorVersion; import info.teksol.mindcode.mimex.Icons; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorFunctionsModifiersTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorFunctionsModifiersTest.java index d8b33cb11..93fcb7a63 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorFunctionsModifiersTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorFunctionsModifiersTest.java @@ -1,7 +1,7 @@ package info.teksol.mindcode.compiler.generator; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorFunctionsTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorFunctionsTest.java index c55ecc153..8bcaf39bd 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorFunctionsTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorFunctionsTest.java @@ -1,9 +1,9 @@ package info.teksol.mindcode.compiler.generator; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.instructions.PushOrPopInstruction; import info.teksol.mindcode.compiler.instructions.SetInstruction; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorTest.java index 6c12773b5..5990da4b8 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LogicInstructionGeneratorTest.java @@ -1,8 +1,8 @@ package info.teksol.mindcode.compiler.generator; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.logic.ProcessorVersion; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LoopStackTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LoopStackTest.java index 38661adfe..d2022adb5 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LoopStackTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/LoopStackTest.java @@ -1,7 +1,7 @@ package info.teksol.mindcode.compiler.generator; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.logic.LogicLabel; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/ReturnStackTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/ReturnStackTest.java index b6162ea73..c5e656ea2 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/generator/ReturnStackTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/generator/ReturnStackTest.java @@ -1,8 +1,8 @@ package info.teksol.mindcode.compiler.generator; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.logic.LogicLabel; import info.teksol.mindcode.logic.LogicVariable; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/instructions/BaseInstructionProcessorTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/instructions/BaseInstructionProcessorTest.java index 6128a9a55..e223449b9 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/instructions/BaseInstructionProcessorTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/instructions/BaseInstructionProcessorTest.java @@ -2,12 +2,12 @@ import info.teksol.mindcode.MindcodeInternalError; import info.teksol.mindcode.compiler.AbstractGeneratorTest; -import info.teksol.mindcode.compiler.UnexpectedMessageException; import info.teksol.mindcode.logic.BaseArgument; import info.teksol.mindcode.logic.InstructionParameterType; import info.teksol.mindcode.logic.ProcessorVersion; import info.teksol.mindcode.logic.TypedArgument; import info.teksol.mindcode.mimex.BlockType; +import info.teksol.util.UnexpectedMessageException; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/AbstractOptimizerTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/AbstractOptimizerTest.java index 73e60f9bb..38cd6bd01 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/AbstractOptimizerTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/AbstractOptimizerTest.java @@ -3,11 +3,11 @@ import info.teksol.mindcode.MessageLevel; import info.teksol.mindcode.compiler.AbstractGeneratorTest; import info.teksol.mindcode.compiler.CompilerProfile; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.TimingMessage; import info.teksol.mindcode.compiler.generator.CallGraph; import info.teksol.mindcode.compiler.generator.GeneratorOutput; import info.teksol.mindcode.compiler.instructions.LogicInstruction; +import info.teksol.util.ExpectedMessages; import java.util.List; import java.util.function.Consumer; @@ -74,7 +74,7 @@ protected List optimizeInstructions(TestCompiler compiler, Gen final OptimizationCoordinator optimizer = createMindcodeOptimizer(compiler); optimizer.setDebugPrinter(debugPrinter); result = optimizer.optimize(generatorOutput); - debugPrinter.print(s -> compiler.addMessage(new MindcodeOptimizerMessage(MessageLevel.INFO, s))); + debugPrinter.print(s -> compiler.addMessage(new OptimizerMessage(MessageLevel.INFO, s))); return result; } diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/DataFlowOptimizerTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/DataFlowOptimizerTest.java index 6ae025193..4e6045ff4 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/DataFlowOptimizerTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/DataFlowOptimizerTest.java @@ -2,9 +2,9 @@ import info.teksol.mindcode.MindcodeMessage; import info.teksol.mindcode.compiler.CompilerProfile; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.GenerationGoal; import info.teksol.mindcode.logic.ProcessorVersion; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/DeadCodeEliminatorTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/DeadCodeEliminatorTest.java index 95fd9078d..77bc7388d 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/DeadCodeEliminatorTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/DeadCodeEliminatorTest.java @@ -1,6 +1,6 @@ package info.teksol.mindcode.compiler.optimization; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/GeneralOptimizationTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/GeneralOptimizationTest.java index b17b90bd9..63c742bef 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/GeneralOptimizationTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/GeneralOptimizationTest.java @@ -1,8 +1,8 @@ package info.teksol.mindcode.compiler.optimization; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.GenerationGoal; import info.teksol.mindcode.compiler.generator.AstContext; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/LoopOptimizerTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/LoopOptimizerTest.java index 93fe237e0..470f87281 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/LoopOptimizerTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/LoopOptimizerTest.java @@ -1,8 +1,8 @@ package info.teksol.mindcode.compiler.optimization; import info.teksol.mindcode.compiler.CompilerProfile; -import info.teksol.mindcode.compiler.ExpectedMessages; import info.teksol.mindcode.compiler.GenerationGoal; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/StackOptimizerTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/StackOptimizerTest.java index 10c257680..e6930eadd 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/StackOptimizerTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/StackOptimizerTest.java @@ -1,6 +1,6 @@ package info.teksol.mindcode.compiler.optimization; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/UnreachableCodeEliminatorTest.java b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/UnreachableCodeEliminatorTest.java index 01ba7a5df..5f53539b0 100644 --- a/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/UnreachableCodeEliminatorTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/compiler/optimization/UnreachableCodeEliminatorTest.java @@ -1,6 +1,6 @@ package info.teksol.mindcode.compiler.optimization; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; diff --git a/compiler/src/test/java/info/teksol/mindcode/grammar/AbstractParserTest.java b/compiler/src/test/java/info/teksol/mindcode/grammar/AbstractParserTest.java index 8fc2d8ddb..e79418995 100644 --- a/compiler/src/test/java/info/teksol/mindcode/grammar/AbstractParserTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/grammar/AbstractParserTest.java @@ -2,7 +2,7 @@ import info.teksol.mindcode.MindcodeErrorListener; import info.teksol.mindcode.MindcodeMessage; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; @@ -33,7 +33,7 @@ protected MindcodeParser.ProgramContext parse(Consumer messageC } protected MindcodeParser.ProgramContext parse(String code) { - return parse(ExpectedMessages.throwOnMessage(),code); + return parse(ExpectedMessages.throwOnMessage(), code); } diff --git a/compiler/src/test/java/info/teksol/mindcode/grammar/MindcodeParserTest.java b/compiler/src/test/java/info/teksol/mindcode/grammar/MindcodeParserTest.java index bb91a0342..170c2dbea 100644 --- a/compiler/src/test/java/info/teksol/mindcode/grammar/MindcodeParserTest.java +++ b/compiler/src/test/java/info/teksol/mindcode/grammar/MindcodeParserTest.java @@ -1,7 +1,7 @@ package info.teksol.mindcode.grammar; import info.teksol.mindcode.MindcodeMessage; -import info.teksol.mindcode.compiler.ExpectedMessages; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -166,7 +166,7 @@ void refusesInvalidInputs() { List messages = parseWithErrors("1..0;"); ExpectedMessages.create() .add(1, 2, "Parse error: missing ';' before '.'") - .addRegex(1, 2, "Parse error: '\\.': mismatched input '\\.' expecting \\{.*\\}") + .addRegex(1, 2, "Parse error: '\\.': mismatched input '\\.' expecting \\{.*}") .validate(messages); } diff --git a/schemacode/src/main/java/info/teksol/schemacode/SchemacodeCompiler.java b/schemacode/src/main/java/info/teksol/schemacode/SchemacodeCompiler.java index 00645b22c..3d0674696 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/SchemacodeCompiler.java +++ b/schemacode/src/main/java/info/teksol/schemacode/SchemacodeCompiler.java @@ -1,6 +1,7 @@ package info.teksol.schemacode; import info.teksol.mindcode.InputFile; +import info.teksol.mindcode.MindcodeErrorListener; import info.teksol.mindcode.MindcodeMessage; import info.teksol.mindcode.compiler.CompilerOutput; import info.teksol.mindcode.compiler.CompilerProfile; @@ -12,7 +13,8 @@ import info.teksol.schemacode.mindustry.SchematicsIO; import info.teksol.schemacode.schematics.Schematic; import info.teksol.schemacode.schematics.SchematicsBuilder; -import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -28,11 +30,12 @@ public class SchemacodeCompiler { * Parses schemacode source into AST tree. * * @param definition source code - * @param messageListener message listener + * @param messageConsumer message consumer * @return Top node of parsed AST tree */ - static DefinitionsContext parseSchematics(InputFile inputFile, Consumer messageListener) { - ErrorListener errorListener = new ErrorListener(messageListener); + static DefinitionsContext parseSchematics(InputFile inputFile, Consumer messageConsumer) { + final MindcodeErrorListener errorListener = new MindcodeErrorListener(messageConsumer); + errorListener.setInputFile(inputFile); final SchemacodeLexer lexer = new SchemacodeLexer(CharStreams.fromString(inputFile.code())); lexer.removeErrorListeners(); @@ -90,23 +93,4 @@ public static CompilerOutput compileAndEncode(InputFile inputFile, Compi private static boolean hasErrors(List messages) { return messages.stream().anyMatch(MindcodeMessage::isError); } - - - private static class ErrorListener extends BaseErrorListener { - private final Consumer messageListener; - - public ErrorListener(Consumer messageListener) { - this.messageListener = messageListener; - } - - @Override - public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, - String msg, RecognitionException e) { - if (offendingSymbol == null) { - messageListener.accept(SchemacodeCompilerMessage.error("Syntax error on line " + line + ":" + charPositionInLine + ": " + msg)); - } else { - messageListener.accept(SchemacodeCompilerMessage.error("Syntax error: " + offendingSymbol + " on line " + line + ":" + charPositionInLine + ": " + msg)); - } - } - } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/SchemacodeCompilerMessage.java b/schemacode/src/main/java/info/teksol/schemacode/SchemacodeCompilerMessage.java deleted file mode 100644 index 1a5439635..000000000 --- a/schemacode/src/main/java/info/teksol/schemacode/SchemacodeCompilerMessage.java +++ /dev/null @@ -1,32 +0,0 @@ -package info.teksol.schemacode; - -import info.teksol.mindcode.MessageLevel; -import info.teksol.mindcode.MindcodeMessage; - -// TODO Change parent to CompilerMessage and provide positional information for messages -public record SchemacodeCompilerMessage(MessageLevel level, String message) implements MindcodeMessage { - - public static SchemacodeCompilerMessage error(String message) { - return new SchemacodeCompilerMessage(MessageLevel.ERROR, message); - } - - public static SchemacodeCompilerMessage warn(String message) { - return new SchemacodeCompilerMessage(MessageLevel.WARNING, message); - } - - public static SchemacodeCompilerMessage info(String message) { - return new SchemacodeCompilerMessage(MessageLevel.INFO, message); - } - - public static SchemacodeCompilerMessage debug(String message) { - return new SchemacodeCompilerMessage(MessageLevel.DEBUG, message); - } - - @Override - public String toString() { - return "SchemacodeCompilerMessage{" + - "level=" + level + - ", message='" + message + '\'' + - '}'; - } -} diff --git a/schemacode/src/main/java/info/teksol/schemacode/SchematicsDecompiler.java b/schemacode/src/main/java/info/teksol/schemacode/SchematicsDecompiler.java index c84f580b5..6020b77f2 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/SchematicsDecompiler.java +++ b/schemacode/src/main/java/info/teksol/schemacode/SchematicsDecompiler.java @@ -1,5 +1,6 @@ package info.teksol.schemacode; +import info.teksol.mindcode.ToolMessage; import info.teksol.mindcode.compiler.CompilerOutput; import info.teksol.schemacode.mindustry.SchematicsIO; import info.teksol.schemacode.schematics.Decompiler; @@ -21,7 +22,7 @@ public static CompilerOutput decompile(String encodedSchematics) { try { binary = Base64.getDecoder().decode(encodedSchematics); } catch (IllegalArgumentException e) { - return new CompilerOutput<>("", List.of(SchemacodeCompilerMessage.error("Error decoding schematics string: " + e.getMessage()))); + return new CompilerOutput<>("", List.of(ToolMessage.error("Error decoding schematics string: " + e.getMessage()))); } try (InputStream is = new ByteArrayInputStream(binary)) { @@ -33,7 +34,7 @@ public static CompilerOutput decompile(String encodedSchematics) { String schemaDefinition = decompiler.buildCode(); return new CompilerOutput<>(schemaDefinition, List.of()); } catch (Exception e) { - return new CompilerOutput<>("", List.of(SchemacodeCompilerMessage.error(e.toString()))); + return new CompilerOutput<>("", List.of(ToolMessage.error(e.toString()))); } } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstBlock.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstBlock.java index aa971c4e9..59d536dd7 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstBlock.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstBlock.java @@ -1,7 +1,9 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; + import java.util.List; -public record AstBlock(List labels, String type, AstCoordinates position, AstDirection direction, +public record AstBlock(InputPosition inputPosition, Listlabels, String type, AstCoordinates position, AstDirection direction, AstConfiguration configuration) implements AstSchemaItem { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstBlockReference.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstBlockReference.java index fba508128..1063a4cb6 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstBlockReference.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstBlockReference.java @@ -1,4 +1,6 @@ package info.teksol.schemacode.ast; -public record AstBlockReference(String item) implements AstConfiguration { +import info.teksol.mindcode.InputPosition; + +public record AstBlockReference(InputPosition inputPosition, String item) implements AstConfiguration { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstBoolean.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstBoolean.java index 8a97d9a4b..7e106414f 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstBoolean.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstBoolean.java @@ -1,5 +1,7 @@ package info.teksol.schemacode.ast; -public record AstBoolean(boolean value) implements AstConfiguration { +import info.teksol.mindcode.InputPosition; + +public record AstBoolean(InputPosition inputPosition, boolean value) implements AstConfiguration { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstConnection.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstConnection.java index ed4c37593..3a5909e51 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstConnection.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstConnection.java @@ -1,31 +1,32 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.mindustry.Position; import info.teksol.schemacode.schematics.SchematicsBuilder; -public record AstConnection(AstCoordinates position, String id) implements AstConfiguration { +public record AstConnection(InputPosition inputPosition, AstCoordinates position, String id) implements AstConfiguration { - public AstConnection(AstCoordinates position) { - this(position, null); + public AstConnection(InputPosition inputPosition, AstCoordinates position) { + this(inputPosition, position, null); } - public AstConnection(int x, int y) { - this(new AstCoordinates(x, y), null); + public AstConnection(InputPosition inputPosition, int x, int y) { + this(inputPosition, new AstCoordinates(inputPosition, x, y), null); } - public AstConnection(int x, int y, boolean relative) { - this(new AstCoordinates(x, y, relative), null); + public AstConnection(InputPosition inputPosition, int x, int y, boolean relative) { + this(inputPosition, new AstCoordinates(inputPosition, x, y, relative), null); } - public AstConnection(String id) { - this(null, id); + public AstConnection(InputPosition inputPosition, String id) { + this(inputPosition, null, id); } public Position evaluate(SchematicsBuilder builder, Position lastPosition) { if (position != null) { return position.evaluate(builder, lastPosition); } else { - return builder.getBlockPosition(id).position(); + return builder.getBlockPosition(this, id).position(); } } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstConnections.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstConnections.java index 29abcdcb7..6c33bb5a0 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstConnections.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstConnections.java @@ -1,10 +1,12 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; + import java.util.List; -public record AstConnections(List connections) implements AstConfiguration { +public record AstConnections(InputPosition inputPosition, List connections) implements AstConfiguration { - public AstConnections(AstConnection... connections) { - this(List.of(connections)); + public AstConnections(InputPosition inputPosition, AstConnection... connections) { + this(inputPosition, List.of(connections)); } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstCoordinates.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstCoordinates.java index 784c088be..39424c58a 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstCoordinates.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstCoordinates.java @@ -1,20 +1,21 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.mindustry.Position; import info.teksol.schemacode.schematics.SchematicsBuilder; -public record AstCoordinates(Position coordinates, boolean relative, String relativeTo) implements AstSchemaItem { +public record AstCoordinates(InputPosition inputPosition, Position coordinates, boolean relative, String relativeTo) implements AstSchemaItem { - public AstCoordinates(int x, int y, String relativeTo) { - this(new Position(x, y), true, relativeTo); + public AstCoordinates(InputPosition inputPosition, int x, int y, String relativeTo) { + this(inputPosition,new Position(x, y), true, relativeTo); } - public AstCoordinates(int x, int y) { - this(new Position(x, y), false, null); + public AstCoordinates(InputPosition inputPosition, int x, int y) { + this(inputPosition,new Position(x, y), false, null); } - public AstCoordinates(int x, int y, boolean relative) { - this(new Position(x, y), relative, null); + public AstCoordinates(InputPosition inputPosition, int x, int y, boolean relative) { + this(inputPosition,new Position(x, y), relative, null); } public Position coordinates() { @@ -31,7 +32,7 @@ public int getY() { public Position evaluate(SchematicsBuilder builder, Position lastPosition) { if (relative) { - Position rel = relativeTo == null ? lastPosition : builder.getBlockPosition(relativeTo).position(); + Position rel = relativeTo == null ? lastPosition : builder.getBlockPosition(this, relativeTo).position(); return rel.add(coordinates); } else { return builder.getAnchor(coordinates()); @@ -43,10 +44,10 @@ public String getRelativeTo() { } public AstCoordinates relative(boolean negate) { - return negate ? new AstCoordinates(-getX(), -getY(), true) : new AstCoordinates(getX(), getY(), true); + return negate ? new AstCoordinates(inputPosition, -getX(), -getY(), true) : new AstCoordinates(inputPosition, getX(), getY(), true); } public AstCoordinates relativeTo(String id) { - return new AstCoordinates(getX(), getY(), id); + return new AstCoordinates(inputPosition, getX(), getY(), id); } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstDefinitions.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstDefinitions.java index a1bbf9cc0..a6274845b 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstDefinitions.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstDefinitions.java @@ -1,6 +1,8 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; + import java.util.List; -public record AstDefinitions(List definitions) implements AstSchemaItem { +public record AstDefinitions(InputPosition inputPosition, List definitions) implements AstSchemaItem { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstDirection.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstDirection.java index ef742f20a..e37294acd 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstDirection.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstDirection.java @@ -1,4 +1,6 @@ package info.teksol.schemacode.ast; -public record AstDirection(String direction) implements AstSchemaItem { +import info.teksol.mindcode.InputPosition; + +public record AstDirection(InputPosition inputPosition, String direction) implements AstSchemaItem { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstItemReference.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstItemReference.java index 37ffe7129..6171da0cf 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstItemReference.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstItemReference.java @@ -1,4 +1,6 @@ package info.teksol.schemacode.ast; -public record AstItemReference(String item) implements AstConfiguration { +import info.teksol.mindcode.InputPosition; + +public record AstItemReference(InputPosition inputPosition, String item) implements AstConfiguration { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstLinkPattern.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstLinkPattern.java index e9423c0d9..1dfecb688 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstLinkPattern.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstLinkPattern.java @@ -1,5 +1,6 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.mindustry.Position; import info.teksol.schemacode.mindustry.ProcessorConfiguration.Link; import info.teksol.schemacode.schematics.SchematicsBuilder; @@ -7,7 +8,7 @@ import java.util.function.Consumer; import java.util.regex.Pattern; -public record AstLinkPattern(String match) implements AstLink { +public record AstLinkPattern(InputPosition inputPosition, String match) implements AstLink { @Override public void getProcessorLinks(Consumer linkConsumer, SchematicsBuilder builder, Position processorPosition) { diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstLinkPos.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstLinkPos.java index 7ee5bbc78..e316860f6 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstLinkPos.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstLinkPos.java @@ -1,12 +1,13 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.mindustry.Position; import info.teksol.schemacode.mindustry.ProcessorConfiguration.Link; import info.teksol.schemacode.schematics.SchematicsBuilder; import java.util.function.Consumer; -public record AstLinkPos(AstConnection connection, String name, boolean virtual) implements AstLink { +public record AstLinkPos(InputPosition inputPosition, AstConnection connection, String name, boolean virtual) implements AstLink { @Override public void getProcessorLinks(Consumer linkConsumer, SchematicsBuilder builder, Position processorPosition) { diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstLiquidReference.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstLiquidReference.java index 6e89ee069..83db26fa4 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstLiquidReference.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstLiquidReference.java @@ -1,4 +1,6 @@ package info.teksol.schemacode.ast; -public record AstLiquidReference(String liquid) implements AstConfiguration { +import info.teksol.mindcode.InputPosition; + +public record AstLiquidReference(InputPosition inputPosition, String liquid) implements AstConfiguration { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstProcessor.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstProcessor.java index 7ffa4ca3c..7888d4f90 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstProcessor.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstProcessor.java @@ -1,8 +1,9 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.schematics.Language; import java.util.List; -public record AstProcessor(List links, AstProgram program, Language language) implements AstConfiguration { +public record AstProcessor(InputPosition inputPosition, List links, AstProgram program, Language language) implements AstConfiguration { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgram.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgram.java index 6f915802b..f4ed06988 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgram.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgram.java @@ -1,15 +1,16 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; import info.teksol.mindcode.InputPositionTranslator; import info.teksol.schemacode.schematics.SchematicsBuilder; import java.util.List; import java.util.stream.Collectors; -public record AstProgram(List snippets) implements AstSchemaItem { +public record AstProgram(InputPosition inputPosition, List snippets) implements AstSchemaItem { - public AstProgram(AstProgramSnippet... snippets) { - this(List.of(snippets)); + public AstProgram(InputPosition inputPosition, AstProgramSnippet... snippets) { + this(inputPosition, List.of(snippets)); } public String getProgramText(SchematicsBuilder builder) { diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgramSnippetFile.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgramSnippetFile.java index 1324fcb25..105744b01 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgramSnippetFile.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgramSnippetFile.java @@ -8,7 +8,7 @@ import java.nio.file.Files; import java.nio.file.Path; -public record AstProgramSnippetFile(AstText fileName) implements AstProgramSnippet { +public record AstProgramSnippetFile(InputPosition inputPosition, AstText fileName) implements AstProgramSnippet { @Override public String getProgramId(SchematicsBuilder builder) { @@ -22,7 +22,7 @@ public String getProgramText(SchematicsBuilder builder) { try { return Files.readString(path); } catch (IOException ex) { - builder.error("Error reading file '%s'.", path.toString()); + builder.error(this, "Error reading file '%s'.", path.toString()); return ""; } } else { diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgramSnippetText.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgramSnippetText.java index 89628b6c5..9957ffea9 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgramSnippetText.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstProgramSnippetText.java @@ -3,7 +3,7 @@ import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.schematics.SchematicsBuilder; -public record AstProgramSnippetText(AstText programText) implements AstProgramSnippet { +public record AstProgramSnippetText(InputPosition inputPosition, AstText programText) implements AstProgramSnippet { @Override public String getProgramId(SchematicsBuilder builder) { diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstRgbaValue.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstRgbaValue.java index fb88333a7..43b898952 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstRgbaValue.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstRgbaValue.java @@ -1,5 +1,7 @@ package info.teksol.schemacode.ast; -public record AstRgbaValue(int red, int green, int blue, int alpha) implements AstColor { +import info.teksol.mindcode.InputPosition; + +public record AstRgbaValue(InputPosition inputPosition, int red, int green, int blue, int alpha) implements AstColor { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchemaAttribute.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchemaAttribute.java index e7b026801..5ba0a65c5 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchemaAttribute.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchemaAttribute.java @@ -1,4 +1,6 @@ package info.teksol.schemacode.ast; -public record AstSchemaAttribute(String attribute, AstSchemaItem value) implements AstSchemaItem { +import info.teksol.mindcode.InputPosition; + +public record AstSchemaAttribute(InputPosition inputPosition, String attribute, AstSchemaItem value) implements AstSchemaItem { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchemaItem.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchemaItem.java index ca3604032..c2621281c 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchemaItem.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchemaItem.java @@ -1,4 +1,10 @@ package info.teksol.schemacode.ast; -public interface AstSchemaItem { +import info.teksol.mindcode.AstElement; +import info.teksol.mindcode.InputPosition; + +public interface AstSchemaItem extends AstElement { + + InputPosition inputPosition(); + } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchematic.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchematic.java index 87215ecf0..d3b5e403d 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchematic.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchematic.java @@ -1,6 +1,8 @@ package info.teksol.schemacode.ast; +import info.teksol.mindcode.InputPosition; + import java.util.List; -public record AstSchematic(List attributes, List blocks) implements AstDefinition { +public record AstSchematic(InputPosition inputPosition, List attributes, List blocks) implements AstDefinition { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchematicsBuilder.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchematicsBuilder.java index 2ed9b545e..2e804dfb6 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchematicsBuilder.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstSchematicsBuilder.java @@ -19,16 +19,14 @@ public class AstSchematicsBuilder extends SchemacodeBaseVisitor { private final InputFile inputFile; - private final Consumer messageListener; - public AstSchematicsBuilder(InputFile inputFile, Consumer messageListener) { + public AstSchematicsBuilder(InputFile inputFile) { this.inputFile = inputFile; - this.messageListener = messageListener; } public static AstDefinitions generate(InputFile inputFile, DefinitionsContext parseTree, Consumer messageListener) { - final AstSchematicsBuilder builder = new AstSchematicsBuilder(inputFile, messageListener); + final AstSchematicsBuilder builder = new AstSchematicsBuilder(inputFile); final AstSchemaItem item = builder.visit(parseTree); return (AstDefinitions) item; } @@ -45,11 +43,11 @@ public AstSchemaItem visitDefinitions(DefinitionsContext ctx) { .map(AstDefinition.class::cast) .toList(); - return new AstDefinitions(list); + return new AstDefinitions(pos(ctx.getStart()), list); } private InputPosition pos(Token token) { - return InputPosition.create(inputFile, token); + return inputFile.isEmpty() ? InputPosition.EMPTY : InputPosition.create(inputFile, token); } @Override @@ -68,29 +66,29 @@ public AstSchemaItem visitSchematic(SchemacodeParser.SchematicContext ctx) { } } - return new AstSchematic(attributes, blocks); + return new AstSchematic(pos(ctx.getStart()), attributes, blocks); } // Attributes @Override public AstSchemaItem visitName(SchemacodeParser.NameContext ctx) { - return new AstSchemaAttribute("name", visit(ctx.textDef())); + return new AstSchemaAttribute(pos(ctx.getStart()), "name", visit(ctx.textDef())); } @Override public AstSchemaItem visitDescription(SchemacodeParser.DescriptionContext ctx) { - return new AstSchemaAttribute("description", visit(ctx.textDef())); + return new AstSchemaAttribute(pos(ctx.getStart()), "description", visit(ctx.textDef())); } @Override public AstSchemaItem visitDimensions(SchemacodeParser.DimensionsContext ctx) { - return new AstSchemaAttribute("dimensions", visit(ctx.coordinates())); + return new AstSchemaAttribute(pos(ctx.getStart()), "dimensions", visit(ctx.coordinates())); } @Override public AstSchemaItem visitSchemaTag(SchemaTagContext ctx) { - return new AstSchemaAttribute("label", visit(ctx.tag)); + return new AstSchemaAttribute(pos(ctx.getStart()), "label", visit(ctx.tag)); } // Blocks @@ -103,7 +101,7 @@ public AstSchemaItem visitBlock(SchemacodeParser.BlockContext ctx) { AstDirection direction = maybeVisit(ctx.direction()); AstConfiguration configuration = maybeVisit(ctx.configuration()); - return new AstBlock(labels, type, position, direction, configuration); + return new AstBlock(pos(ctx.getStart()), labels, type, position, direction, configuration); } @@ -111,7 +109,7 @@ public AstSchemaItem visitBlock(SchemacodeParser.BlockContext ctx) { @Override public AstSchemaItem visitBoolean(BooleanContext ctx) { - return new AstBoolean(ctx.status.getText().equals("enabled")); + return new AstBoolean(pos(ctx.getStart()), ctx.status.getText().equals("enabled")); } @Override @@ -125,12 +123,12 @@ public AstRgbaValue visitColorDef(ColorDefContext ctx) { int green = Integer.parseInt(ctx.green.getText()); int blue = Integer.parseInt(ctx.blue.getText()); int alpha = Integer.parseInt(ctx.alpha.getText()); - return new AstRgbaValue(red, green, blue, alpha); + return new AstRgbaValue(pos(ctx.getStart()), red, green, blue, alpha); } @Override public AstVirtual visitVirtual(SchemacodeParser.VirtualContext ctx) { - return AstVirtual.VIRTUAL; + return new AstVirtual(pos(ctx.getStart())); } @Override @@ -140,47 +138,47 @@ public AstConnections visitConnections(SchemacodeParser.ConnectionsContext ctx) .map(AstConnection.class::cast) .toList(); - return new AstConnections(list); + return new AstConnections(pos(ctx.getStart()), list); } @Override public AstConnection visitConnAbs(SchemacodeParser.ConnAbsContext ctx) { - return new AstConnection(visitCoordinates(ctx.coordinates())); + return new AstConnection(pos(ctx.getStart()), visitCoordinates(ctx.coordinates())); } @Override public AstConnection visitConnRel(SchemacodeParser.ConnRelContext ctx) { - return new AstConnection(visitRelativeCoordinates(ctx.relativeCoordinates())); + return new AstConnection(pos(ctx.getStart()), visitRelativeCoordinates(ctx.relativeCoordinates())); } @Override public AstConnection visitConnName(SchemacodeParser.ConnNameContext ctx) { - return new AstConnection(ctx.Id().getSymbol().getText()); + return new AstConnection(pos(ctx.getStart()), ctx.Id().getSymbol().getText()); } @Override public AstSchemaItem visitBlocktype(SchemacodeParser.BlocktypeContext ctx) { - return new AstBlockReference(ctx.Ref().getSymbol().getText()); + return new AstBlockReference(pos(ctx.getStart()), ctx.Ref().getSymbol().getText()); } @Override public AstSchemaItem visitUnitcommand(UnitcommandContext ctx) { - return new AstUnitCommandReference(ctx.Ref().getSymbol().getText()); + return new AstUnitCommandReference(pos(ctx.getStart()), ctx.Ref().getSymbol().getText()); } @Override public AstItemReference visitItem(SchemacodeParser.ItemContext ctx) { - return new AstItemReference(ctx.Ref().getSymbol().getText()); + return new AstItemReference(pos(ctx.getStart()), ctx.Ref().getSymbol().getText()); } @Override public AstSchemaItem visitLiquid(SchemacodeParser.LiquidContext ctx) { - return new AstLiquidReference(ctx.Ref().getSymbol().getText()); + return new AstLiquidReference(pos(ctx.getStart()), ctx.Ref().getSymbol().getText()); } @Override public AstSchemaItem visitUnit(SchemacodeParser.UnitContext ctx) { - return new AstUnitReference(ctx.Ref().getSymbol().getText()); + return new AstUnitReference(pos(ctx.getStart()), ctx.Ref().getSymbol().getText()); } @Override @@ -216,12 +214,12 @@ public AstProcessor visitProcessor(SchemacodeParser.ProcessorContext ctx) { language = Language.NONE; } - return new AstProcessor(links, program, language); + return new AstProcessor(pos(ctx.getStart()), links, program, language); } @Override public AstLinkPattern visitLinkPattern(SchemacodeParser.LinkPatternContext ctx) { - return new AstLinkPattern(ctx.linkPattern.getText()); + return new AstLinkPattern(pos(ctx.getStart()), ctx.linkPattern.getText()); } @Override @@ -229,7 +227,7 @@ public AstLinkPos visitLinkPos(SchemacodeParser.LinkPosContext ctx) { AstConnection connection = (AstConnection) visit(ctx.linkPos); String name = ctx.alias == null ? null : ctx.alias.getText(); boolean virtual = ctx.virtual != null; - return new AstLinkPos(connection, name, virtual); + return new AstLinkPos(pos(ctx.getStart()), connection, name, virtual); } @Override @@ -239,17 +237,17 @@ public AstSchemaItem visitProgram(ProgramContext ctx) { .map(AstProgramSnippet.class::cast) .toList(); - return new AstProgram(snippets); + return new AstProgram(pos(ctx.getStart()), snippets); } @Override public AstSchemaItem visitProgramString(SchemacodeParser.ProgramStringContext ctx) { - return new AstProgramSnippetText((AstText) visit(ctx.text)); + return new AstProgramSnippetText(pos(ctx.getStart()), (AstText) visit(ctx.text)); } @Override public AstSchemaItem visitProgramFile(SchemacodeParser.ProgramFileContext ctx) { - return new AstProgramSnippetFile((AstText) visit(ctx.file)); + return new AstProgramSnippetFile(pos(ctx.getStart()), (AstText) visit(ctx.file)); } // Coordinates & direction @@ -263,7 +261,7 @@ public AstCoordinates visitPosition(SchemacodeParser.PositionContext ctx) { public AstCoordinates visitCoordinates(SchemacodeParser.CoordinatesContext ctx) { int x = Integer.parseInt(ctx.x.getText()); int y = Integer.parseInt(ctx.y.getText()); - return new AstCoordinates(x, y); + return new AstCoordinates(pos(ctx.getStart()), x, y); } @Override @@ -285,7 +283,7 @@ public AstCoordinates visitCoordinatesRelativeTo(SchemacodeParser.CoordinatesRel @Override public AstDirection visitDirection(SchemacodeParser.DirectionContext ctx) { - return new AstDirection(ctx.dir.getText()); + return new AstDirection(pos(ctx.getStart()), ctx.dir.getText()); } // Labels @@ -305,7 +303,7 @@ private static List processLabels(SchemacodeParser.LabelListContext labe public AstStringConstant visitStringValue(SchemacodeParser.StringValueContext ctx) { String name = ctx.name.getText(); AstText text = (AstText) visit(ctx.string); - return new AstStringConstant(name, text) ; + return new AstStringConstant(pos(ctx.getStart()), name, text) ; } @Override @@ -316,9 +314,9 @@ public AstStringLiteral visitTextLine(SchemacodeParser.TextLineContext ctx) { @Override public AstStringBlock visitTextBlock(SchemacodeParser.TextBlockContext ctx) { if (ctx.TextBlock1() != null) { - return AstStringBlock.fromTerminalNode(inputFile, ctx.TextBlock1()); + return AstStringBlock.fromTerminalNode(pos(ctx.TextBlock1().getSymbol()), ctx.TextBlock1().getText()); } else if (ctx.TextBlock2() != null) { - return AstStringBlock.fromTerminalNode(inputFile, ctx.TextBlock2()); + return AstStringBlock.fromTerminalNode(pos(ctx.TextBlock2().getSymbol()), ctx.TextBlock2().getText()); } else { throw new SchematicsInternalError("No text value in TextBlock"); } @@ -326,6 +324,6 @@ public AstStringBlock visitTextBlock(SchemacodeParser.TextBlockContext ctx) { @Override public AstSchemaItem visitTextId(SchemacodeParser.TextIdContext ctx) { - return new AstStringRef(ctx.Id().getText()); + return new AstStringRef(pos(ctx.Id().getSymbol()), ctx.Id().getText()); } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringBlock.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringBlock.java index 477be8671..178521b17 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringBlock.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringBlock.java @@ -1,11 +1,9 @@ package info.teksol.schemacode.ast; -import info.teksol.mindcode.InputFile; import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.schematics.SchematicsBuilder; -import org.antlr.v4.runtime.tree.TerminalNode; -public record AstStringBlock(String text, InputPosition inputPosition, int indent) implements AstText { +public record AstStringBlock(InputPosition inputPosition, String text, int indent) implements AstText { @Override public InputPosition getTextPosition(SchematicsBuilder builder) { @@ -22,8 +20,7 @@ public String getText(SchematicsBuilder builder) { return text; } - public static AstStringBlock fromTerminalNode(InputFile inputFile, TerminalNode node) { - String nodeText = node.getText(); + public static AstStringBlock fromTerminalNode(InputPosition inputPosition, String nodeText) { String unquoted = nodeText.substring(3, nodeText.length() - 3); // Skip first newline, if there isn't a newline (how so?), index + 1 will be equal to 0 int start = unquoted.indexOf('\n'); @@ -32,11 +29,10 @@ public static AstStringBlock fromTerminalNode(InputFile inputFile, TerminalNode String text = textBlock.stripIndent(); int newLine = text.indexOf('\n'); int indent = textBlock.indexOf(newLine >= 0 ? text.substring(0, newLine) : text); - int line = node.getSymbol().getLine() + 1; - return new AstStringBlock(text, new InputPosition(inputFile, line, 1), indent); + return new AstStringBlock(inputPosition, text, indent); } public static AstStringBlock fromText(String text) { - return new AstStringBlock(text, InputPosition.EMPTY, 0); + return new AstStringBlock(InputPosition.EMPTY, text, 0); } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringConstant.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringConstant.java index ce10f24dc..7586759a2 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringConstant.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringConstant.java @@ -1,4 +1,6 @@ package info.teksol.schemacode.ast; -public record AstStringConstant(String name, AstText value) implements AstDefinition { +import info.teksol.mindcode.InputPosition; + +public record AstStringConstant(InputPosition inputPosition, String name, AstText value) implements AstDefinition { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringLiteral.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringLiteral.java index 43d1879e6..d6b189fd2 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringLiteral.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringLiteral.java @@ -5,7 +5,7 @@ import info.teksol.schemacode.schematics.SchematicsBuilder; import org.antlr.v4.runtime.tree.TerminalNode; -public record AstStringLiteral(String text, InputPosition inputPosition) implements AstText { +public record AstStringLiteral(InputPosition inputPosition, String text) implements AstText { @Override public InputPosition getTextPosition(SchematicsBuilder builder) { @@ -22,14 +22,14 @@ public static AstStringLiteral fromTerminalNode(InputFile inputFile, TerminalNod String text = nodeText.substring(1, nodeText.length() - 1); int line = node.getSymbol().getLine(); int column = node.getSymbol().getCharPositionInLine() + 2; - return new AstStringLiteral(text, new InputPosition(inputFile, line, column)); + return new AstStringLiteral(new InputPosition(inputFile, line, column), text); } public static AstStringLiteral fromText(String text) { - return new AstStringLiteral(text, InputPosition.EMPTY); + return new AstStringLiteral(InputPosition.EMPTY, text); } public static AstStringLiteral fromText(String text, int line, int column) { - return new AstStringLiteral(text, new InputPosition(InputFile.EMPTY, line, column)); + return new AstStringLiteral(new InputPosition(InputFile.EMPTY, line, column), text); } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringRef.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringRef.java index 5159022b5..b1622c411 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringRef.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstStringRef.java @@ -3,20 +3,20 @@ import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.schematics.SchematicsBuilder; -public record AstStringRef(String reference) implements AstText { +public record AstStringRef(InputPosition inputPosition, String reference) implements AstText { @Override public String getText(SchematicsBuilder builder) { - return builder.getText(reference).getText(builder); + return builder.getText(this, reference).getText(builder); } @Override public InputPosition getTextPosition(SchematicsBuilder builder) { - return builder.getText(reference).getTextPosition(builder); + return builder.getText(this, reference).getTextPosition(builder); } @Override public int getIndent(SchematicsBuilder builder) { - return builder.getText(reference).getIndent(builder); + return builder.getText(this, reference).getIndent(builder); } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstUnitCommandReference.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstUnitCommandReference.java index 6458f55cd..43eb9215b 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstUnitCommandReference.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstUnitCommandReference.java @@ -1,4 +1,6 @@ package info.teksol.schemacode.ast; -public record AstUnitCommandReference(String item) implements AstConfiguration { +import info.teksol.mindcode.InputPosition; + +public record AstUnitCommandReference(InputPosition inputPosition, String item) implements AstConfiguration { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstUnitReference.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstUnitReference.java index d4aaa6632..3a6076657 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstUnitReference.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstUnitReference.java @@ -1,4 +1,6 @@ package info.teksol.schemacode.ast; -public record AstUnitReference(String unit) implements AstConfiguration { +import info.teksol.mindcode.InputPosition; + +public record AstUnitReference(InputPosition inputPosition, String unit) implements AstConfiguration { } diff --git a/schemacode/src/main/java/info/teksol/schemacode/ast/AstVirtual.java b/schemacode/src/main/java/info/teksol/schemacode/ast/AstVirtual.java index 49993a1ac..ddf4f6be2 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/ast/AstVirtual.java +++ b/schemacode/src/main/java/info/teksol/schemacode/ast/AstVirtual.java @@ -1,5 +1,7 @@ package info.teksol.schemacode.ast; -public enum AstVirtual implements AstConfiguration { - VIRTUAL +import info.teksol.mindcode.InputPosition; + +public record AstVirtual(InputPosition inputPosition) implements AstConfiguration { + } diff --git a/schemacode/src/main/java/info/teksol/schemacode/mindustry/ProcessorConfiguration.java b/schemacode/src/main/java/info/teksol/schemacode/mindustry/ProcessorConfiguration.java index 25a8e6e85..19611b2a9 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/mindustry/ProcessorConfiguration.java +++ b/schemacode/src/main/java/info/teksol/schemacode/mindustry/ProcessorConfiguration.java @@ -116,20 +116,20 @@ public static ProcessorConfiguration fromAstConfiguration(SchematicsBuilder buil Map> linksByName = links.stream().collect(Collectors.groupingBy(Link::name)); linksByName.values().stream() .filter(v -> v.size() > 1) - .forEachOrdered(l -> builder.error("Block link name '%s' used more than once.", l.get(0).name())); + .forEachOrdered(l -> builder.error(processor, "Block link name '%s' used more than once.", l.get(0).name())); // Detect blocks linked more than once Map> linksByPosition = links.stream().collect(Collectors.groupingBy(Link::position)); linksByPosition.entrySet().stream() .filter(e -> e.getValue().size() > 1) - .forEachOrdered(l -> builder.error("Multiple links for block at position %s: '%s'.", + .forEachOrdered(l -> builder.error(processor, "Multiple links for block at position %s: '%s'.", l.getKey().toStringAbsolute(), l.getValue().stream().map(Link::name).collect(Collectors.joining("', '")))); // Verify link names links.stream() .filter(l -> !compatibleLinkName(builder, l)) - .forEachOrdered(l -> builder.error("Incompatible link name '%s' for block type '%s'.", l.name, + .forEachOrdered(l -> builder.error(processor, "Incompatible link name '%s' for block type '%s'.", l.name, builder.getBlockPosition(l.position).blockType().name())); String mlog = convertToMlog(builder, processor); @@ -160,7 +160,7 @@ private static String convertToMlog(SchematicsBuilder builder, AstProcessor proc CompilerOutput output = CompilerFacade.compile(mindcode, compilerProfile); output.messages().forEach(builder::addMessage); if (output.hasErrors()) { - builder.error("Compile errors in Mindcode source code."); + builder.error(processor, "Compile errors in Mindcode source code."); yield ""; } diff --git a/schemacode/src/main/java/info/teksol/schemacode/mindustry/SchematicsIO.java b/schemacode/src/main/java/info/teksol/schemacode/mindustry/SchematicsIO.java index b48c965fa..57d33192c 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/mindustry/SchematicsIO.java +++ b/schemacode/src/main/java/info/teksol/schemacode/mindustry/SchematicsIO.java @@ -1,5 +1,6 @@ package info.teksol.schemacode.mindustry; +import info.teksol.mindcode.InputPosition; import info.teksol.mindcode.Tuple2; import info.teksol.mindcode.mimex.BlockType; import info.teksol.schemacode.SchematicsInternalError; @@ -127,7 +128,7 @@ public static Schematic readMsch(InputStream input) throws IOException { Direction direction = Direction.convert(stream.readByte()); if (!"@air".equals(blockType.name())) { Configuration config = convert(blockType, position, raw); - blocks.add(new Block(index++, List.of(), blockType, position, direction, config)); + blocks.add(new Block(InputPosition.EMPTY, index++, List.of(), blockType, position, direction, config)); } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/schematics/Block.java b/schemacode/src/main/java/info/teksol/schemacode/schematics/Block.java index ea1fd53e3..fba26531a 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/schematics/Block.java +++ b/schemacode/src/main/java/info/teksol/schemacode/schematics/Block.java @@ -1,5 +1,6 @@ package info.teksol.schemacode.schematics; +import info.teksol.mindcode.InputPosition; import info.teksol.mindcode.mimex.BlockType; import info.teksol.schemacode.config.Configuration; import info.teksol.schemacode.config.PositionArray; @@ -10,6 +11,7 @@ import java.util.function.UnaryOperator; public record Block( + InputPosition inputPosition, int index, List labels, BlockType blockType, @@ -18,10 +20,10 @@ public record Block( Configuration configuration) implements BlockPosition { public Block remap(UnaryOperator mapping) { - return new Block(index, labels, blockType, mapping.apply(position), direction, configuration.remap(mapping)); + return new Block(inputPosition, index, labels, blockType, mapping.apply(position), direction, configuration.remap(mapping)); } public Block withConnections(PositionArray connections) { - return new Block(index, labels, blockType, position, direction, connections); + return new Block(inputPosition, index, labels, blockType, position, direction, connections); } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/schematics/BlockPositionMap.java b/schemacode/src/main/java/info/teksol/schemacode/schematics/BlockPositionMap.java index 8a1738696..871653b74 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/schematics/BlockPositionMap.java +++ b/schemacode/src/main/java/info/teksol/schemacode/schematics/BlockPositionMap.java @@ -1,8 +1,8 @@ package info.teksol.schemacode.schematics; import info.teksol.mindcode.MindcodeMessage; +import info.teksol.mindcode.ToolMessage; import info.teksol.mindcode.Tuple2; -import info.teksol.schemacode.SchemacodeCompilerMessage; import info.teksol.schemacode.mindustry.Position; import java.util.*; @@ -47,10 +47,10 @@ private static BlockPositionMap build(Consumer messageListener.accept(SchemacodeCompilerMessage.error( - "Overlapping blocks: #%d '%s' at %s and #%d '%s' at %s.".formatted( - t.e1().index(), t.e1().name(), t.e1().area(), - t.e2().index(), t.e2().name(), t.e2().area())))); + collisions.forEach(t -> messageListener.accept(ToolMessage.error( + "Overlapping blocks: #%d '%s' at %s and #%d '%s' at %s.", + t.e1().index(), t.e1().name(), t.e1().area(), + t.e2().index(), t.e2().name(), t.e2().area()))); } return new BlockPositionMap<>(blockMap, positionMap); diff --git a/schemacode/src/main/java/info/teksol/schemacode/schematics/BlockPositionResolver.java b/schemacode/src/main/java/info/teksol/schemacode/schematics/BlockPositionResolver.java index 07dc2c6ea..737c6832a 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/schematics/BlockPositionResolver.java +++ b/schemacode/src/main/java/info/teksol/schemacode/schematics/BlockPositionResolver.java @@ -1,8 +1,8 @@ package info.teksol.schemacode.schematics; import info.teksol.mindcode.MindcodeMessage; +import info.teksol.mindcode.ToolMessage; import info.teksol.mindcode.mimex.BlockType; -import info.teksol.schemacode.SchemacodeCompilerMessage; import info.teksol.schemacode.SchematicsInternalError; import info.teksol.schemacode.ast.AstBlock; import info.teksol.schemacode.mindustry.Position; @@ -37,7 +37,7 @@ public Map resolveAllBlocks(List blocks) { } private void error(@PrintFormat String format, Object... args) { - messageListener.accept(SchemacodeCompilerMessage.error(String.format(format, args))); + messageListener.accept(ToolMessage.error(format, args)); } private BlockPosition resolve(Map blocks, RelativeBlockPosition block) { diff --git a/schemacode/src/main/java/info/teksol/schemacode/schematics/BridgeSolver.java b/schemacode/src/main/java/info/teksol/schemacode/schematics/BridgeSolver.java index 99303ed22..ab856cb2c 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/schematics/BridgeSolver.java +++ b/schemacode/src/main/java/info/teksol/schemacode/schematics/BridgeSolver.java @@ -42,23 +42,21 @@ private void processConnectedBlock(Block block, boolean orthogonal) { if (orthogonal) { if (!block.position().orthogonal(linked.position())) { - builder.error("Block '%s' at %s has a connection leading to %s, which is neither horizontal nor vertical.", + builder.error(block.inputPosition(), "Block '%s' at %s has a connection leading to %s, which is neither horizontal nor vertical.", block.name(), block.position().toStringAbsolute(), linked.position().toStringAbsolute()); - } else if (!inRange(block, linked)) { - builder.error("Block '%s' at %s has an out-of-range connection to %s.", + } else if (outOfRange(block, linked)) { + builder.error(block.inputPosition(),"Block '%s' at %s has an out-of-range connection to %s.", block.name(), block.position().toStringAbsolute(), linked.position().toStringAbsolute()); } else { Block next = getLinkedBlock(linked, false); if (next == block && linkBacks.add(Tuple2.of(Math.min(block.index(), linked.index()), Math.max(block.index(), linked.index())))) { - builder.error("Two '%s' blocks at %s and %s connect to each other.", + builder.error(block.inputPosition(),"Two '%s' blocks at %s and %s connect to each other.", block.name(), block.position().toStringAbsolute(), linked.position().toStringAbsolute()); } } - } else { - if (!inRange(block, linked)) { - builder.error("Block '%s' at %s has an out-of-range connection to %s.", - block.name(), block.position().toStringAbsolute(), linked.position().toStringAbsolute()); - } + } else if (outOfRange(block, linked)) { + builder.error(block.inputPosition(),"Block '%s' at %s has an out-of-range connection to %s.", + block.name(), block.position().toStringAbsolute(), linked.position().toStringAbsolute()); } } @@ -67,7 +65,7 @@ private Block getLinkedBlock(Block block, boolean reportErrors) { if (links.size() == 0) { return null; } else if (links.size() > 1 && reportErrors) { - builder.error("Block '%s' at %s has more than one connection.", block.name(), block.position().toStringAbsolute()); + builder.error(block.inputPosition(),"Block '%s' at %s has more than one connection.", block.name(), block.position().toStringAbsolute()); } Block linked = positionMap.at(links.get(0)); @@ -77,13 +75,13 @@ private Block getLinkedBlock(Block block, boolean reportErrors) { if (!linked.blockType().equals(block.blockType())) { if (reportErrors) { - builder.error("Block '%s' at %s has a connection leading to a different block type '%s' at %s.", + builder.error(block.inputPosition(),"Block '%s' at %s has a connection leading to a different block type '%s' at %s.", block.name(), block.position().toStringAbsolute(), linked.name(), linked.position().toStringAbsolute()); } return null; } else if (linked == block) { if (reportErrors) { - builder.error("Block '%s' at %s has a connection to self.", block.name(), block.position().toStringAbsolute()); + builder.error(block.inputPosition(),"Block '%s' at %s has a connection to self.", block.name(), block.position().toStringAbsolute()); } return null; } @@ -91,15 +89,15 @@ private Block getLinkedBlock(Block block, boolean reportErrors) { return linked; } - private boolean inRange(Block from, Block to) { + private boolean outOfRange(Block from, Block to) { if (from.x() == to.x()) { - return Math.abs(from.y() - to.y()) < from.blockType().range() + 0.4f; + return Math.abs(from.y() - to.y()) >= from.blockType().range() + 0.4f; } else if (from.y() == to.y()) { - return Math.abs(from.x() - to.x()) < from.blockType().range() + 0.4f; + return Math.abs(from.x() - to.x()) >= from.blockType().range() + 0.4f; } else { double distX = from.x() - to.x(); double distY = from.y() - to.y(); - return distX * distX + distY * distY < from.blockType().range() * from.blockType().range(); + return distX * distX + distY * distY >= from.blockType().range() * from.blockType().range(); } } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/schematics/PowerGridSolver.java b/schemacode/src/main/java/info/teksol/schemacode/schematics/PowerGridSolver.java index 69ce4c24e..24f672c5d 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/schematics/PowerGridSolver.java +++ b/schemacode/src/main/java/info/teksol/schemacode/schematics/PowerGridSolver.java @@ -45,7 +45,7 @@ private List solve() { // Report overloaded nodes powerNodes.entrySet().stream().filter(e -> e.getKey().blockType().maxNodes() < e.getValue().size()) .map(Map.Entry::getKey) - .forEachOrdered(b -> builder.error("Block '%s' at %s has more than %d connection(s).", + .forEachOrdered(b -> builder.error(b.inputPosition(), "Block '%s' at %s has more than %d connection(s).", b.name(), b.position().toStringAbsolute(), b.blockType().maxNodes())); // Rebuild block list @@ -63,22 +63,22 @@ private Set collectLinks(Block powerNodeBlock) { Block linkedBlock = positionMap.at(pos); if (linkedBlock == null) { - builder.warn("Block '%s' at %s has a connection to a nonexistent block at %s.", + builder.warn(powerNodeBlock.inputPosition(), "Block '%s' at %s has a connection to a nonexistent block at %s.", powerNodeBlock.name(), powerNodeBlock.position().toStringAbsolute(), pos.toStringAbsolute()); } else if (linkedBlock == powerNodeBlock) { - builder.error("Block '%s' at %s has a connection to self.", + builder.error(powerNodeBlock.inputPosition(), "Block '%s' at %s has a connection to self.", powerNodeBlock.name(), powerNodeBlock.position().toStringAbsolute()); } else if (!linkedBlock.blockType().hasPower()) { - builder.error("Block '%s' at %s has an invalid connection to a non-powered block '%s' at %s.", + builder.error(powerNodeBlock.inputPosition(), "Block '%s' at %s has an invalid connection to a non-powered block '%s' at %s.", powerNodeBlock.name(), powerNodeBlock.position().toStringAbsolute(), linkedBlock.name(), pos.toStringAbsolute()); - } else if (!inRange(powerNodeBlock, linkedBlock) && !inRange(linkedBlock, powerNodeBlock)) { - builder.error("Block '%s' at %s has an out-of-range connection to block '%s' at %s.", + } else if (outOfRange(powerNodeBlock, linkedBlock) && outOfRange(linkedBlock, powerNodeBlock)) { + builder.error(powerNodeBlock.inputPosition(), "Block '%s' at %s has an out-of-range connection to block '%s' at %s.", powerNodeBlock.name(), powerNodeBlock.position().toStringAbsolute(), linkedBlock.name(), pos.toStringAbsolute()); } else { if (!linkedBlocks.add(linkedBlock)) { - builder.warn("Block '%s' at %s has multiple connections to block '%s' at %s.", + builder.warn(powerNodeBlock.inputPosition(), "Block '%s' at %s has multiple connections to block '%s' at %s.", powerNodeBlock.name(), powerNodeBlock.position().toStringAbsolute(), linkedBlock.name(), pos.toStringAbsolute()); } @@ -95,16 +95,16 @@ private boolean isPowerNode(Block block) { }; } - private boolean inRange(Block from, Block to) { + private boolean outOfRange(Block from, Block to) { if (!isPowerNode(from)) { - return false; + return true; } else { double x = from.position().x() + from.size() / 2d; double y = from.position().y() + from.size() / 2d; double distX = (x < to.x() ? to.x() : to.x() + to.size()) - x; double distY = (y < to.y() ? to.y() : to.y() + to.size()) - y; - return distX * distX + distY * distY < from.blockType().range() * from.blockType().range(); + return distX * distX + distY * distY >= from.blockType().range() * from.blockType().range(); } } } diff --git a/schemacode/src/main/java/info/teksol/schemacode/schematics/SchematicsBuilder.java b/schemacode/src/main/java/info/teksol/schemacode/schematics/SchematicsBuilder.java index 003f4c700..4075ba0e2 100644 --- a/schemacode/src/main/java/info/teksol/schemacode/schematics/SchematicsBuilder.java +++ b/schemacode/src/main/java/info/teksol/schemacode/schematics/SchematicsBuilder.java @@ -1,11 +1,13 @@ package info.teksol.schemacode.schematics; +import info.teksol.mindcode.AstElement; import info.teksol.mindcode.InputFile; import info.teksol.mindcode.MindcodeMessage; +import info.teksol.mindcode.ToolMessage; import info.teksol.mindcode.compiler.CompilerProfile; +import info.teksol.mindcode.compiler.generator.AbstractMessageEmitter; import info.teksol.mindcode.mimex.BlockType; import info.teksol.mindcode.mimex.Icons; -import info.teksol.schemacode.SchemacodeCompilerMessage; import info.teksol.schemacode.SchematicsInternalError; import info.teksol.schemacode.ast.*; import info.teksol.schemacode.config.*; @@ -20,11 +22,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class SchematicsBuilder { +public class SchematicsBuilder extends AbstractMessageEmitter { private final CompilerProfile compilerProfile; - private final Consumer messageListener; private final AstDefinitions astDefinitions; - private final InputFile inputFile; private final Path basePath; private AstSchematic astSchematic; @@ -33,12 +33,11 @@ public class SchematicsBuilder { private BlockPositionMap astPositionMap; private BlockPositionMap positionMap; - public SchematicsBuilder(CompilerProfile compilerProfile, Consumer messageListener, AstDefinitions astDefinitions, + public SchematicsBuilder(CompilerProfile compilerProfile, Consumer messageConsumer, AstDefinitions astDefinitions, InputFile inputFile, Path basePath) { + super(messageConsumer); this.compilerProfile = compilerProfile; - this.messageListener = messageListener; this.astDefinitions = astDefinitions; - this.inputFile = inputFile; this.basePath = basePath; } @@ -47,20 +46,16 @@ public static SchematicsBuilder create(CompilerProfile compilerProfile, AstDefin return new SchematicsBuilder(compilerProfile, messageListener, definitions, inputFile, basePath); } - public void addMessage(MindcodeMessage message) { - messageListener.accept(message); - } - public void error(@PrintFormat String message, Object... args) { - messageListener.accept(SchemacodeCompilerMessage.error(args.length == 0 ? message : message.formatted(args))); + addMessage(ToolMessage.error(message, args)); } public void warn(@PrintFormat String message, Object... args) { - messageListener.accept(SchemacodeCompilerMessage.warn(args.length == 0 ? message : message.formatted(args))); + addMessage(ToolMessage.warn(message, args)); } public void info(@PrintFormat String message, Object... args) { - messageListener.accept(SchemacodeCompilerMessage.info(args.length == 0 ? message : message.formatted(args))); + addMessage(ToolMessage.info(message, args)); } public CompilerProfile getCompilerProfile() { @@ -84,10 +79,10 @@ public Schematic buildSchematics() { .toList(); if (schematicsList.isEmpty()) { - error("No schematic defined."); + addMessage(ToolMessage.error("No schematic defined.")); return null; } else if (schematicsList.size() > 1) { - error("More than one schematic defined."); + addMessage(ToolMessage.error("More than one schematic defined.")); return null; } @@ -100,21 +95,21 @@ public Schematic buildSchematics() { labelCounts.entrySet().stream() .filter(e -> e.getValue() > 1) - .forEachOrdered(c -> error("Multiple definitions of block label '%s'.", c.getKey())); + .forEachOrdered(c -> addMessage(ToolMessage.error("Multiple definitions of block label '%s'.", c.getKey()))); astSchematic.blocks().stream().filter(astBlock -> !BlockType.isNameValid(astBlock.type())) - .forEachOrdered(astBlock -> error("Unknown block type '%s'.", astBlock.type())); + .forEachOrdered(astBlock -> error(astBlock, "Unknown block type '%s'.", astBlock.type())); List astBlocks = astSchematic.blocks().stream() .filter(astBlock -> BlockType.isNameValid(astBlock.type())).toList(); // Here are absolute positions of all blocks, stored as "#" + index // Labeled blocks are additionally stored under all their labels - BlockPositionResolver positionResolver = new BlockPositionResolver(messageListener); + BlockPositionResolver positionResolver = new BlockPositionResolver(messageConsumer); astLabelMap = positionResolver.resolveAllBlocks(astBlocks); List blockPositions = astLabelMap.values().stream().distinct().toList(); - astPositionMap = BlockPositionMap.forBuilder(messageListener, blockPositions); + astPositionMap = BlockPositionMap.forBuilder(messageConsumer, blockPositions); List blocks = new ArrayList<>(); for (int index = 0; index < astBlocks.size(); index++) { @@ -125,7 +120,7 @@ public Schematic buildSchematics() { ? Direction.EAST : Direction.valueOf(astBlock.direction().direction().toUpperCase()); Configuration configuration = convertAstConfiguration(blockPos, astBlock.configuration()); - blocks.add(new Block(index, astBlock.labels(), type, blockPos.position(), direction, + blocks.add(new Block(astBlock.inputPosition(), index, astBlock.labels(), type, blockPos.position(), direction, configuration.as(ConfigurationType.fromBlockType(type).getBuilderConfigurationClass()))); } @@ -200,11 +195,10 @@ private void extractConstants() { .map(AstStringConstant.class::cast) .collect(Collectors.groupingBy(AstStringConstant::name)); - List redefinition = astConstantLists.entrySet().stream() - .filter(e -> e.getValue().size() > 1).map(Map.Entry::getKey).toList(); - if (!redefinition.isEmpty()) { - redefinition.forEach(id -> error("Identifier '%s' defined more than once.", id)); - } + astConstantLists.entrySet().stream() + .filter(e -> e.getValue().size() > 1) + .flatMap(e -> e.getValue().stream().skip(1)) + .forEachOrdered(node -> error(node, "Identifier '%s' already defined.", node.name())); Map astConstants = astConstantLists.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0))); @@ -224,10 +218,10 @@ private AstText resolveConstant(Map constantLists, Se throw new SchematicsInternalError("Identifier '%s': unexpected null value.", value.name()); } else if (text instanceof AstStringRef ref) { if (!visited.add(ref.reference())) { - error("Circular definition of identifier '%s'.", ref.reference()); + error(ref, "Circular definition of identifier '%s'.", ref.reference()); return AstStringLiteral.fromText(""); } else if (!constantLists.containsKey(ref.reference())) { - error("Undefined identifier '%s'.", ref.reference()); + error(ref, "Undefined identifier '%s'.", ref.reference()); return AstStringLiteral.fromText(""); } return resolveConstant(constantLists, visited, constantLists.get(ref.reference())); @@ -240,7 +234,7 @@ private Configuration convertAstConfiguration(BlockPosition blockPos, AstConfigu Configuration configuration = getConfiguration(blockPos, astConfiguration); if (!blockPos.configurationType().isCompatible(configuration)) { - error("Unexpected configuration type for block '%s' at %s: expected %s, found %s.", + error(astConfiguration, "Unexpected configuration type for block '%s' at %s: expected %s, found %s.", blockPos.blockType().name(), blockPos.position().toStringAbsolute(), blockPos.configurationType(), ConfigurationType.fromInstance(configuration)); return EmptyConfiguration.EMPTY; // Ignore wrong configuration but keep processing the block @@ -251,32 +245,32 @@ private Configuration convertAstConfiguration(BlockPosition blockPos, AstConfigu private Configuration getConfiguration(BlockPosition blockPos, AstConfiguration astConfiguration) { if (astConfiguration == null) return EmptyConfiguration.EMPTY; - if (astConfiguration instanceof AstBlockReference r) return verifyValue(blockPos, BlockConfiguration.forName(r.item()), r.item(), "block"); + if (astConfiguration instanceof AstBlockReference r) return verifyValue(r, blockPos, BlockConfiguration.forName(r.item()), r.item(), "block"); if (astConfiguration instanceof AstBoolean b) return BooleanConfiguration.of(b.value()); if (astConfiguration instanceof AstConnection c) return c.evaluate(this, blockPos.position()); if (astConfiguration instanceof AstConnections c) return new PositionArray(c.connections().stream().map(p -> p.evaluate(this, blockPos.position())).toList()); - if (astConfiguration instanceof AstItemReference r) return verifyValue(blockPos, ItemConfiguration.forName(r.item()), r.item(), "item"); - if (astConfiguration instanceof AstLiquidReference r) return verifyValue(blockPos, LiquidConfiguration.forName(r.liquid()), r.liquid(), "liquid"); + if (astConfiguration instanceof AstItemReference r) return verifyValue(r, blockPos, ItemConfiguration.forName(r.item()), r.item(), "item"); + if (astConfiguration instanceof AstLiquidReference r) return verifyValue(r, blockPos, LiquidConfiguration.forName(r.liquid()), r.liquid(), "liquid"); if (astConfiguration instanceof AstProcessor p) return ProcessorConfiguration.fromAstConfiguration(this, p, blockPos.position()); if (astConfiguration instanceof AstRgbaValue rgb) return convertToRgbValue(blockPos, rgb); if (astConfiguration instanceof AstText t) return new TextConfiguration(t.getText(this)); - if (astConfiguration instanceof AstUnitCommandReference r) return verifyValue(blockPos, UnitCommandConfiguration.forName(r.item()), r.item(), "command"); + if (astConfiguration instanceof AstUnitCommandReference r) return verifyValue(r, blockPos, UnitCommandConfiguration.forName(r.item()), r.item(), "command"); if (astConfiguration instanceof AstUnitReference r) return decodeUnitConfiguration(blockPos, r); return EmptyConfiguration.EMPTY; } private Color convertToRgbValue(BlockPosition blockPos, AstRgbaValue rgb) { return new Color( - clamp(blockPos, "red", rgb.red()), - clamp(blockPos, "green", rgb.green()), - clamp(blockPos, "blue", rgb.blue()), - clamp(blockPos, "alpha", rgb.alpha()) + clamp(rgb, blockPos, "red", rgb.red()), + clamp(rgb, blockPos, "green", rgb.green()), + clamp(rgb, blockPos, "blue", rgb.blue()), + clamp(rgb, blockPos, "alpha", rgb.alpha()) ); } - private Configuration verifyValue(BlockPosition blockPos, Configuration value, String strValue, String valueName) { + private Configuration verifyValue(AstElement element, BlockPosition blockPos, Configuration value, String strValue, String valueName) { if (value == null) { - error("Block '%s' at %s: unknown or unsupported %s '%s'.", + error(element, "Block '%s' at %s: unknown or unsupported %s '%s'.", blockPos.name(), blockPos.position().toStringAbsolute(), valueName, strValue); return EmptyConfiguration.EMPTY; // Ignore wrong configuration but keep processing the block } else { @@ -284,9 +278,9 @@ private Configuration verifyValue(BlockPosition blockPos, Configuration value, S } } - private int clamp(BlockPosition blockPos, String component, int value) { + private int clamp(AstElement element, BlockPosition blockPos, String component, int value) { if (value < 0 || value > 255) { - error("Block '%s' at %s: value %d of color component '%s' outside valid range <0, 255>.", + error(element, "Block '%s' at %s: value %d of color component '%s' outside valid range <0, 255>.", blockPos.name(), blockPos.position().toStringAbsolute(), value, component); } return Math.max(Math.min(value, 255), 0); @@ -294,18 +288,18 @@ private int clamp(BlockPosition blockPos, String component, int value) { private Configuration decodeUnitConfiguration(BlockPosition blockPos, AstUnitReference ref) { return switch (blockPos.configurationType()) { - case UNIT_OR_BLOCK -> verifyValue(blockPos, UnitConfiguration.forName(ref.unit()), ref.unit(), "ref"); + case UNIT_OR_BLOCK -> verifyValue(ref, blockPos, UnitConfiguration.forName(ref.unit()), ref.unit(), "ref"); case UNIT_PLAN -> { if (blockPos.blockType().unitPlans().contains(ref.unit())) { yield new UnitPlan(ref.unit()); } else { - error("Block '%s' at %s: unknown or unsupported unit type '%s'.", + error(ref, "Block '%s' at %s: unknown or unsupported unit type '%s'.", blockPos.name(), blockPos.position().toStringAbsolute(), ref.unit()); yield EmptyConfiguration.EMPTY; } } default -> { - error("Block '%s' at %s: unknown or unsupported configuration type '%s'.", + error(ref, "Block '%s' at %s: unknown or unsupported configuration type '%s'.", blockPos.name(), blockPos.position().toStringAbsolute(), blockPos.configurationType()); yield EmptyConfiguration.EMPTY; } @@ -317,7 +311,7 @@ private T getAttribute(String name, Class expectedType) { if (list.isEmpty()) { return null; } else if (list.size() > 1) { - error("Multiple definitions of attribute '%s'.", name); + list.stream().skip(1).forEach(a -> error(a, "Attribute '%s' is already defined.", name)); } if (!expectedType.isInstance(list.get(0).value())) { @@ -364,10 +358,10 @@ static String unwrap(String text) { return sbr.toString(); } - public AstText getText(String reference) { + public AstText getText(AstElement element, String reference) { AstText result = constants.get(reference); if (result == null) { - error("Undefined identifier '%s'.", reference); + error(element, "Undefined identifier '%s'.", reference); return AstStringLiteral.fromText(""); } return result; @@ -378,12 +372,12 @@ public BlockPosition getBlockPosition(int index) { () -> new SchematicsInternalError("Invalid block index %d.", index)); } - public BlockPosition getBlockPosition(String name) { + public BlockPosition getBlockPosition(AstElement element, String name) { BlockPosition blockPosition = astLabelMap.get(name); if (blockPosition != null) { return blockPosition; } - error("Unknown block label '%s'", name); + error(element, "Unknown block label '%s'", name); return new AstBlockPosition(0, BlockType.forName("@air"), Position.INVALID); } public Map getAstLabelMap() { diff --git a/schemacode/src/test/java/info/teksol/schemacode/AbstractSchematicsTest.java b/schemacode/src/test/java/info/teksol/schemacode/AbstractSchematicsTest.java index 9c3b5cb22..f93d1bbc0 100644 --- a/schemacode/src/test/java/info/teksol/schemacode/AbstractSchematicsTest.java +++ b/schemacode/src/test/java/info/teksol/schemacode/AbstractSchematicsTest.java @@ -1,7 +1,6 @@ package info.teksol.schemacode; import info.teksol.mindcode.InputFile; -import info.teksol.mindcode.MessageLevel; import info.teksol.mindcode.MindcodeMessage; import info.teksol.mindcode.compiler.CompilerProfile; import info.teksol.mindcode.mimex.BlockType; @@ -13,14 +12,13 @@ import info.teksol.schemacode.mindustry.Position; import info.teksol.schemacode.schematics.Block; import info.teksol.schemacode.schematics.Schematic; -import org.intellij.lang.annotations.Language; +import info.teksol.util.ExpectedMessages; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; +import static info.teksol.mindcode.InputPosition.EMPTY; public abstract class AbstractSchematicsTest { public static final Position P0_0 = Position.ORIGIN; @@ -40,11 +38,11 @@ public static PositionArray pa(Position... positions) { private int index = 0; public Block block(List labels, String blockType, Position position, Direction direction, Configuration configuration) { - return new Block(index++, labels, BlockType.forName(blockType), position, direction, configuration); + return new Block(EMPTY, index++, labels, BlockType.forName(blockType), position, direction, configuration); } public Block block(String blockType, Position position, Direction direction, Configuration configuration) { - return new Block(index++, List.of(), BlockType.forName(blockType), position, direction, configuration); + return new Block(EMPTY, index++, List.of(), BlockType.forName(blockType), position, direction, configuration); } /** @@ -63,13 +61,15 @@ private Consumer messageListener(String methodName) { } protected DefinitionsContext parseSchematics(String definition) { - return SchemacodeCompiler.parseSchematics(InputFile.createSourceFile(definition), messageListener("parseSchematics")); + return SchemacodeCompiler.parseSchematics(InputFile.createSourceFile(definition), + messageListener("parseSchematics")); } - protected void parseSchematicsExpectingError(String definition, @Language("RegExp") String regex) { + protected void parseSchematicsExpectingMessages(ExpectedMessages expectedMessages, String definition) { + expectedMessages.addRegex("Created schematic '.*' with dimensions .*").ignored(); List messages = new ArrayList<>(); SchemacodeCompiler.parseSchematics(InputFile.createSourceFile(definition), messages::add); - assertRegex(MessageLevel.ERROR, regex, messages); + expectedMessages.validate(messages); } protected AstDefinitions createDefinitions(String definition) { @@ -84,29 +84,21 @@ protected Schematic buildSchematics(String definition) { InputFile.EMPTY, null); } - protected void buildSchematicsExpectingError(String definition, @Language("RegExp") String regex) { + protected void assertGeneratesErrors(ExpectedMessages expectedMessages, String definition) { + expectedMessages.addRegex("Created schematic '.*' with dimensions .*").ignored(); List messages = new ArrayList<>(); AstDefinitions definitions = createDefinitions(definition); CompilerProfile compilerProfile = CompilerProfile.fullOptimizations(false); SchemacodeCompiler.buildSchematic(definitions, compilerProfile, messages::add, InputFile.EMPTY, null); - assertRegex(MessageLevel.ERROR, regex, messages); + expectedMessages.validate(messages); } - protected void buildSchematicsExpectingWarning(String definition, @Language("RegExp") String regex) { + protected void assertGeneratesWarnings(ExpectedMessages expectedMessages, String definition) { + expectedMessages.addRegex("Created schematic '.*' with dimensions .*").ignored(); List messages = new ArrayList<>(); AstDefinitions definitions = createDefinitions(definition); CompilerProfile compilerProfile = CompilerProfile.fullOptimizations(false); SchemacodeCompiler.buildSchematic(definitions, compilerProfile, messages::add, InputFile.EMPTY, null); - assertRegex(MessageLevel.WARNING, regex, messages); - } - - private void assertRegex(MessageLevel expectedLevel, String expectedRegex, List messages) { - List list = messages.stream().filter(m -> m.level() == expectedLevel).map(MindcodeMessage::message).toList(); - if (list.stream().anyMatch(s -> s.matches(expectedRegex))) { - assertTrue(true); - } else { - fail("No message matched expected expression.\nExpected expression: %s, found messages: %s" - .formatted(expectedRegex, String.join("\n", list))); - } + expectedMessages.validate(messages); } } diff --git a/schemacode/src/test/java/info/teksol/schemacode/ast/AstSchematicBuilderTest.java b/schemacode/src/test/java/info/teksol/schemacode/ast/AstSchematicBuilderTest.java index e127e0443..bd8377884 100644 --- a/schemacode/src/test/java/info/teksol/schemacode/ast/AstSchematicBuilderTest.java +++ b/schemacode/src/test/java/info/teksol/schemacode/ast/AstSchematicBuilderTest.java @@ -1,22 +1,22 @@ package info.teksol.schemacode.ast; -import info.teksol.mindcode.InputFile; -import info.teksol.mindcode.InputPosition; import info.teksol.schemacode.AbstractSchematicsTest; import info.teksol.schemacode.mindustry.Position; import info.teksol.schemacode.schematics.Language; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import java.util.List; +import static info.teksol.mindcode.InputPosition.EMPTY; import static org.junit.jupiter.api.Assertions.*; @Order(99) class AstSchematicBuilderTest extends AbstractSchematicsTest { protected AstDefinitions definitionWithBlocks(AstBlock... blocks) { - return new AstDefinitions(List.of(new AstSchematic(List.of(), List.of(blocks)))); + return new AstDefinitions(EMPTY, List.of(new AstSchematic(EMPTY, List.of(), List.of(blocks)))); } @Test @@ -49,29 +49,29 @@ public void parsesBasicSchematics() { """ ); - AstDefinitions expected = new AstDefinitions( + AstDefinitions expected = new AstDefinitions(EMPTY, List.of( - new AstSchematic( + new AstSchematic(EMPTY, List.of( - new AstSchemaAttribute("name", AstStringLiteral.fromText("On/off switch", 2, 13)), - new AstSchemaAttribute("description", AstStringLiteral.fromText("Description", 3, 20)), - new AstSchemaAttribute("label", AstStringLiteral.fromText("label1", 4, 12)), - new AstSchemaAttribute("label", AstStringLiteral.fromText("label2", 5, 12)), - new AstSchemaAttribute("dimensions", new AstCoordinates(2, 1)) + new AstSchemaAttribute(EMPTY, "name", AstStringLiteral.fromText("On/off switch", 2, 13)), + new AstSchemaAttribute(EMPTY, "description", AstStringLiteral.fromText("Description", 3, 20)), + new AstSchemaAttribute(EMPTY, "label", AstStringLiteral.fromText("label1", 4, 12)), + new AstSchemaAttribute(EMPTY, "label", AstStringLiteral.fromText("label2", 5, 12)), + new AstSchemaAttribute(EMPTY, "dimensions", new AstCoordinates(EMPTY, 2, 1)) ), List.of( - new AstBlock( + new AstBlock(EMPTY, List.of("switch1"), "@switch", - new AstCoordinates(0, 0), - new AstDirection("south"), + new AstCoordinates(EMPTY, 0, 0), + new AstDirection(EMPTY, "south"), null ), - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(1, 0), - new AstDirection("south"), + new AstCoordinates(EMPTY, 1, 0), + new AstDirection(EMPTY, "south"), null ) ) @@ -90,10 +90,9 @@ public void parsesTextBlock() { ''' """); - AstDefinitions expected = new AstDefinitions( + AstDefinitions expected = new AstDefinitions(EMPTY, List.of( - new AstStringConstant("value", new AstStringBlock("text\nblock\n", - new InputPosition(InputFile.EMPTY, 2, 1), 4) + new AstStringConstant(EMPTY, "value", new AstStringBlock(EMPTY, "text\nblock\n", 4) ) ) ); @@ -143,11 +142,11 @@ public void parsesNameLiteral() { """ ); - AstDefinitions expected = new AstDefinitions( + AstDefinitions expected = new AstDefinitions(EMPTY, List.of( - new AstSchematic( + new AstSchematic(EMPTY, List.of( - new AstSchemaAttribute("name", AstStringLiteral.fromText("Name", 2, 13)) + new AstSchemaAttribute(EMPTY, "name", AstStringLiteral.fromText("Name", 2, 13)) ), List.of() ) @@ -167,15 +166,15 @@ public void parsesNameRef() { """ ); - AstDefinitions expected = new AstDefinitions( + AstDefinitions expected = new AstDefinitions(EMPTY, List.of( - new AstSchematic( + new AstSchematic(EMPTY, List.of( - new AstSchemaAttribute("name", new AstStringRef("str_Name")) + new AstSchemaAttribute(EMPTY, "name", new AstStringRef(EMPTY, "str_Name")) ), List.of() ), - new AstStringConstant("str_Name", AstStringLiteral.fromText("Name", 5, 13)) + new AstStringConstant(EMPTY, "str_Name", AstStringLiteral.fromText("Name", 5, 13)) ) ); @@ -191,11 +190,11 @@ public void parsesDimensions() { """ ); - AstDefinitions expected = new AstDefinitions( + AstDefinitions expected = new AstDefinitions(EMPTY, List.of( - new AstSchematic( + new AstSchematic(EMPTY, List.of( - new AstSchemaAttribute("dimensions", new AstCoordinates(4, 5)) + new AstSchemaAttribute(EMPTY, "dimensions", new AstCoordinates(EMPTY, 4, 5)) ), List.of() ) @@ -207,23 +206,29 @@ public void parsesDimensions() { @Test public void refusesRelativeDimensions() { - parseSchematicsExpectingError(""" - schematic - name = "Reactor Control" - dimensions = +(16, 11) - end - """, - "Syntax error: .* extraneous input '.+' expecting '\\('"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: extraneous input '+' expecting '('"), + """ + schematic + name = "Reactor Control" + dimensions = +(16, 11) + end + """ + ); } @Test public void refusesNonRefBlock() { - parseSchematicsExpectingError(""" - schematic - conveyor at (0, 0) - end - """, - "Syntax error: .* mismatched input '.+' expecting \\{':', ','}"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: 'at': mismatched input 'at' expecting {':', ','}"), + """ + schematic + conveyor at (0, 0) + end + """ + ); } @Test @@ -236,10 +241,10 @@ public void parsesBlockAtAbsoluteCoordinates() { ); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@switch", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, null ) @@ -258,10 +263,10 @@ public void parsesBlockAtRelativeCoordinates() { ); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@switch", - new AstCoordinates(1, 1, true), + new AstCoordinates(EMPTY, 1, 1, true), null, null ) @@ -280,10 +285,10 @@ public void parsesBlockAtRelativeToCoordinates() { ); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@switch", - new AstCoordinates(new Position(1, 1), true, "block1"), + new AstCoordinates(EMPTY, new Position(1, 1), true, "block1"), null, null ) @@ -294,12 +299,15 @@ public void parsesBlockAtRelativeToCoordinates() { @Test public void requiresPosition() { - parseSchematicsExpectingError(""" - schematic - label1: @switch facing south - end - """, - "Syntax error: .* mismatched input '.+' expecting 'at'"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: 'facing': mismatched input 'facing' expecting 'at'"), + """ + schematic + label1: @switch facing south + end + """ + ); } @Test @@ -313,10 +321,10 @@ public void parsesBlockWithLabels() { ); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of("label1", "label2", "label3"), "@switch", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, null ) @@ -338,10 +346,10 @@ public void parsesAllDirections() { ); AstDefinitions expected = definitionWithBlocks( - new AstBlock(List.of(), "@conveyor", new AstCoordinates(0, 0), new AstDirection("south"),null), - new AstBlock(List.of(), "@conveyor", new AstCoordinates(1, 0), new AstDirection("north"),null), - new AstBlock(List.of(), "@conveyor", new AstCoordinates(2, 0), new AstDirection("east"),null), - new AstBlock(List.of(), "@conveyor", new AstCoordinates(3, 0), new AstDirection("west"),null) + new AstBlock(EMPTY, List.of(), "@conveyor", new AstCoordinates(EMPTY, 0, 0), new AstDirection(EMPTY, "south"), null), + new AstBlock(EMPTY, List.of(), "@conveyor", new AstCoordinates(EMPTY, 1, 0), new AstDirection(EMPTY, "north"), null), + new AstBlock(EMPTY, List.of(), "@conveyor", new AstCoordinates(EMPTY, 2, 0), new AstDirection(EMPTY, "east"), null), + new AstBlock(EMPTY, List.of(), "@conveyor", new AstCoordinates(EMPTY, 3, 0), new AstDirection(EMPTY, "west"), null) ); assertEquals(expected, actual); @@ -349,12 +357,16 @@ public void parsesAllDirections() { @Test public void refusesInvalidDirection() { - parseSchematicsExpectingError(""" - schematic - @conveyor at ( 0, 0) facing middle - end - """, - ("Syntax error: .* missing \\{'north', 'south', 'east', 'west'} at 'middle'")); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: missing {'north', 'south', 'east', 'west'} at 'middle'") + .add("Parse error: 'end': mismatched input 'end' expecting {':', ','}"), + """ + schematic + @conveyor at ( 0, 0) facing middle + end + """ + ); } @Test @@ -367,12 +379,12 @@ public void parsesVirtualConfiguration() { ); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@switch", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - AstVirtual.VIRTUAL + new AstVirtual(EMPTY) ) ); @@ -389,12 +401,12 @@ public void parsesConnectedToAbsolute() { ); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@power-node", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstConnections(new AstConnection(1, 1)) + new AstConnections(EMPTY, new AstConnection(EMPTY, 1, 1)) ) ); @@ -410,12 +422,12 @@ public void parsesConnectedToRelative() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@power-node", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstConnections(new AstConnection(1, 1, true)) + new AstConnections(EMPTY, new AstConnection(EMPTY, 1, 1, true)) ) ); @@ -431,14 +443,14 @@ public void parsesConnectedToMixed() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@power-node", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstConnections( - new AstConnection(1, 1, true), - new AstConnection(2, 2) + new AstConnections(EMPTY, + new AstConnection(EMPTY, 1, 1, true), + new AstConnection(EMPTY, 2, 2) ) ) ); @@ -448,12 +460,15 @@ public void parsesConnectedToMixed() { @Test public void refusesConnectedToRelativeTo() { - parseSchematicsExpectingError(""" - schematic - @power-node at (0, 0) connected to block1 + (1, 1) - end - """, - "Syntax error: .* extraneous input '.+' expecting \\{'description', 'dimensions', 'end', 'name', 'tag', Id, Ref}"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: extraneous input '+' expecting {'description', 'dimensions', 'end', 'name', 'tag', Id, Ref}"), + """ + schematic + @power-node at (0, 0) connected to block1 + (1, 1) + end + """ + ); } @Test @@ -465,12 +480,12 @@ public void parsesItemRef() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@sorter", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstItemReference("@coal") + new AstItemReference(EMPTY, "@coal") ) ); @@ -479,12 +494,16 @@ public void parsesItemRef() { @Test public void refusesItemNonRef() { - parseSchematicsExpectingError(""" - schematic - @sorter at (0, 0) item coal - end - """, - "Syntax error: .* missing Ref at 'coal'"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: missing Ref at 'coal'") + .add("Parse error: 'end': mismatched input 'end' expecting {':', ','}"), + """ + schematic + @sorter at (0, 0) item coal + end + """ + ); } @Test @@ -496,12 +515,12 @@ public void parsesLiquidRef() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@liquid-source", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstLiquidReference("@water") + new AstLiquidReference(EMPTY, "@water") ) ); @@ -510,12 +529,16 @@ public void parsesLiquidRef() { @Test public void refusesLiquidNonRef() { - parseSchematicsExpectingError(""" - schematic - @liquid-source at (0, 0) liquid water - end - """, - "Syntax error: .* missing Ref at 'water'"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: missing Ref at 'water'") + .add("Parse error: 'end': mismatched input 'end' expecting {':', ','}"), + """ + schematic + @liquid-source at (0, 0) liquid water + end + """ + ); } @Test @@ -527,10 +550,10 @@ public void parsesTextConfigurationLiteral() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@message", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, AstStringLiteral.fromText("message", 2, 30) ) @@ -552,13 +575,12 @@ public void parsesTextConfigurationBlock() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@message", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstStringBlock("message1\nmessage2\n", - new InputPosition(InputFile.EMPTY, 3, 1), 0) + new AstStringBlock(EMPTY, "message1\nmessage2\n", 0) ) ); @@ -574,12 +596,12 @@ public void parsesTextConfigurationRef() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@message", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstStringRef("something") + new AstStringRef(EMPTY, "something") ) ); @@ -595,12 +617,12 @@ public void parsesConfigurationEnabled() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@switch", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstBoolean(true) + new AstBoolean(EMPTY, true) ) ); @@ -616,12 +638,12 @@ public void parsesConfigurationDisabled() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@switch", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstBoolean(false) + new AstBoolean(EMPTY, false) ) ); @@ -638,12 +660,12 @@ public void parsesEmptyProcessor() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), null, Language.NONE) + new AstProcessor(EMPTY, List.of(), null, Language.NONE) ) ); @@ -661,12 +683,12 @@ public void parsesProcessorEmptyLinks() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), null, Language.NONE) + new AstProcessor(EMPTY, List.of(), null, Language.NONE) ) ); @@ -684,12 +706,12 @@ public void parsesProcessorLinkPattern() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPattern("*-p-*")), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPattern(EMPTY, "*-p-*")), null, Language.NONE) ) ); @@ -707,12 +729,12 @@ public void parsesProcessorLinkReference() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection("cell1"), null, false)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, "cell1"), null, false)), null, Language.NONE) ) ); @@ -721,14 +743,17 @@ public void parsesProcessorLinkReference() { @Test public void refusesProcessorVirtualLinkReference() { - parseSchematicsExpectingError(""" - schematic - @micro-processor at (0, 0) processor - links * virtual end - end - end - """, - "Syntax error: .* extraneous input 'virtual' expecting \\{'end', '-', '\\+', '\\(', Id, Pattern}"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: extraneous input 'virtual' expecting {'end', '-', '+', '(', Id, Pattern}"), + """ + schematic + @micro-processor at (0, 0) processor + links * virtual end + end + end + """ + ); } @Test @@ -742,12 +767,12 @@ public void parsesProcessorLinkNamedReference() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection("cell1"), "cell2", false)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, "cell1"), "cell2", false)), null, Language.NONE) ) ); @@ -765,12 +790,12 @@ public void parsesProcessorLinkNamedReferenceVirtual() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection("cell1"), "cell2", true)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, "cell1"), "cell2", true)), null, Language.NONE) ) ); @@ -788,13 +813,13 @@ public void parsesProcessorLinkReferenceVirtual() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor( - List.of(new AstLinkPos(new AstConnection(-1, -1, true), "cell1", true)), + new AstProcessor(EMPTY, + List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, -1, -1, true), "cell1", true)), null, Language.NONE) ) ); @@ -813,12 +838,12 @@ public void parsesProcessorLinkPositionAbsolute() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection(1, 1), null, false)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, 1, 1), null, false)), null, Language.NONE) ) ); @@ -836,12 +861,12 @@ public void parsesProcessorLinkPositionRelative() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection(1, 1, true), null, false)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, 1, 1, true), null, false)), null, Language.NONE) ) ); @@ -859,12 +884,12 @@ public void parsesProcessorLinkPositionAbsoluteNamed() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection(1, 1), "switch1", false)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, 1, 1), "switch1", false)), null, Language.NONE) ) ); @@ -882,12 +907,12 @@ public void parsesProcessorLinkPositionRelativeNamed() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection(1, 1, true), "switch1", false)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, 1, 1, true), "switch1", false)), null, Language.NONE) ) ); @@ -905,12 +930,12 @@ public void parsesProcessorLinkPositionAbsoluteNamedVirtual() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection(1, 1), "switch1", true)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, 1, 1), "switch1", true)), null, Language.NONE) ) ); @@ -928,12 +953,12 @@ public void parsesProcessorLinkPositionRelativeNamedVirtual() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(new AstLinkPos(new AstConnection(1, 1, true), "switch1", true)), null, Language.NONE) + new AstProcessor(EMPTY, List.of(new AstLinkPos(EMPTY, new AstConnection(EMPTY, 1, 1, true), "switch1", true)), null, Language.NONE) ) ); @@ -942,26 +967,32 @@ public void parsesProcessorLinkPositionRelativeNamedVirtual() { @Test public void refusesProcessorLinkPositionAbsoluteUnnamedVirtual() { - parseSchematicsExpectingError(""" - schematic - @micro-processor at (0, 0) processor - links (1, 1) virtual end - end - end - """, - "Syntax error: .* extraneous input 'virtual' expecting \\{'end', '-', '\\+', '\\(', Id, Pattern}"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: extraneous input 'virtual' expecting {'end', '-', '+', '(', Id, Pattern}"), + """ + schematic + @micro-processor at (0, 0) processor + links (1, 1) virtual end + end + end + """ + ); } @Test public void refusesProcessorLinkPositionRelativeUnnamedVirtual() { - parseSchematicsExpectingError(""" - schematic - @micro-processor at (0, 0) processor - links +(1, 1) virtual end - end - end - """, - "Syntax error: .* extraneous input 'virtual' expecting \\{'end', '-', '\\+', '\\(', Id, Pattern}"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: extraneous input 'virtual' expecting {'end', '-', '+', '(', Id, Pattern}"), + """ + schematic + @micro-processor at (0, 0) processor + links +(1, 1) virtual end + end + end + """ + ); } @Test @@ -982,18 +1013,18 @@ public void parsesProcessorLinks() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of( - new AstLinkPattern("p1-*"), - new AstLinkPos(new AstConnection("switch1"), null, false), - new AstLinkPos(new AstConnection("cell1"), "cell2", false), - new AstLinkPos(new AstConnection(1, 1), null, false), - new AstLinkPos(new AstConnection(2, 2, true), "message1", false), - new AstLinkPos(new AstConnection(-1, -1, true), "display1", true) + new AstProcessor(EMPTY, List.of( + new AstLinkPattern(EMPTY, "p1-*"), + new AstLinkPos(EMPTY, new AstConnection(EMPTY, "switch1"), null, false), + new AstLinkPos(EMPTY, new AstConnection(EMPTY, "cell1"), "cell2", false), + new AstLinkPos(EMPTY, new AstConnection(EMPTY, 1, 1), null, false), + new AstLinkPos(EMPTY, new AstConnection(EMPTY, 2, 2, true), "message1", false), + new AstLinkPos(EMPTY, new AstConnection(EMPTY, -1, -1, true), "display1", true) ), null, Language.NONE) ) ); @@ -1012,14 +1043,14 @@ public void parsesProcessorCodeMlogInline() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetText(AstStringLiteral.fromText("program", 3, 17)) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetText(EMPTY, AstStringLiteral.fromText("program", 3, 17)) ), Language.MLOG) ) ); @@ -1038,14 +1069,14 @@ public void parsesProcessorCodeMlogIndirect() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetText(new AstStringRef("mlog_program")) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetText(EMPTY, new AstStringRef(EMPTY, "mlog_program")) ), Language.MLOG) ) ); @@ -1064,14 +1095,14 @@ public void parsesProcessorCodeMlogFileInline() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetFile(AstStringLiteral.fromText("file", 3, 22)) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetFile(EMPTY, AstStringLiteral.fromText("file", 3, 22)) ), Language.MLOG) ) ); @@ -1090,14 +1121,14 @@ public void parsesProcessorCodeMlogFileIndirect() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetFile(new AstStringRef("my_file")) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetFile(EMPTY, new AstStringRef(EMPTY, "my_file")) ), Language.MLOG) ) ); @@ -1116,14 +1147,14 @@ public void parsesProcessorCodeMindcodeInline() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetText(AstStringLiteral.fromText("program", 3, 21)) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetText(EMPTY, AstStringLiteral.fromText("program", 3, 21)) ), Language.MINDCODE) ) ); @@ -1142,14 +1173,14 @@ public void parsesProcessorCodeMindcodeIndirect() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetText(new AstStringRef("mindcode_program")) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetText(EMPTY, new AstStringRef(EMPTY, "mindcode_program")) ), Language.MINDCODE) ) ); @@ -1168,14 +1199,14 @@ public void parsesProcessorCodeMindcodeFileInline() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetFile(AstStringLiteral.fromText("file", 3, 26)) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetFile(EMPTY, AstStringLiteral.fromText("file", 3, 26)) ), Language.MINDCODE) ) ); @@ -1194,14 +1225,14 @@ public void parsesProcessorCodeMindcodeFileIndirect() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetFile(new AstStringRef("my_file")) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetFile(EMPTY, new AstStringRef(EMPTY, "my_file")) ), Language.MINDCODE) ) ); @@ -1221,17 +1252,17 @@ public void parsesProcessorCodeMindcodeMultipleSnippets() { """); AstDefinitions expected = definitionWithBlocks( - new AstBlock( + new AstBlock(EMPTY, List.of(), "@micro-processor", - new AstCoordinates(0, 0), + new AstCoordinates(EMPTY, 0, 0), null, - new AstProcessor(List.of(), - new AstProgram( - new AstProgramSnippetText(AstStringLiteral.fromText("program", 3, 21)), - new AstProgramSnippetFile(AstStringLiteral.fromText("file", 3, 38)), - new AstProgramSnippetText(new AstStringRef("my_program")), - new AstProgramSnippetFile(new AstStringRef("my_file")) + new AstProcessor(EMPTY, List.of(), + new AstProgram(EMPTY, + new AstProgramSnippetText(EMPTY, AstStringLiteral.fromText("program", 3, 21)), + new AstProgramSnippetFile(EMPTY, AstStringLiteral.fromText("file", 3, 38)), + new AstProgramSnippetText(EMPTY, new AstStringRef(EMPTY, "my_program")), + new AstProgramSnippetFile(EMPTY, new AstStringRef(EMPTY, "my_file")) ), Language.MINDCODE) ) ); @@ -1241,11 +1272,14 @@ public void parsesProcessorCodeMindcodeMultipleSnippets() { @Test public void refusesUnknownConfiguration() { - parseSchematicsExpectingError(""" - schematic - @switch at (0, 0) fluffyBunny - end - """, - "Syntax error: .* mismatched input 'end' expecting \\{':', ','}"); + parseSchematicsExpectingMessages( + ExpectedMessages.create() + .add("Parse error: 'end': mismatched input 'end' expecting {':', ','}"), + """ + schematic + @switch at (0, 0) fluffyBunny + end + """ + ); } } diff --git a/schemacode/src/test/java/info/teksol/schemacode/schematics/BlockPositionMapTest.java b/schemacode/src/test/java/info/teksol/schemacode/schematics/BlockPositionMapTest.java index b947490fb..322e7b880 100644 --- a/schemacode/src/test/java/info/teksol/schemacode/schematics/BlockPositionMapTest.java +++ b/schemacode/src/test/java/info/teksol/schemacode/schematics/BlockPositionMapTest.java @@ -5,6 +5,7 @@ import info.teksol.schemacode.mindustry.Direction; import info.teksol.schemacode.mindustry.ProcessorConfiguration; import info.teksol.schemacode.mindustry.ProcessorConfiguration.Link; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -17,35 +18,44 @@ class BlockPositionMapTest extends AbstractSchematicsTest { @Test void refusesOverlappingBlocks1x1() { - buildSchematicsExpectingError(""" - schematic - @message at (0, 0) - @switch at (0, 0) - end - """, - "Overlapping blocks: #0 '@message' at \\(0, 0\\) and #1 '@switch' at \\(0, 0\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Overlapping blocks: #0 '@message' at \\(0, 0\\) and #1 '@switch' at \\(0, 0\\)\\."), + """ + schematic + @message at (0, 0) + @switch at (0, 0) + end + """ + ); } @Test void refusesOverlappingBlocks2x2() { - buildSchematicsExpectingError(""" - schematic - @power-node-large at (0, 0) - @kiln at (1, 1) - end - """, - "Overlapping blocks: #0 '@power-node-large' at \\(0, 0\\) - \\(1, 1\\) and #1 '@kiln' at \\(1, 1\\) - \\(2, 2\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Overlapping blocks: #0 '@power-node-large' at \\(0, 0\\) - \\(1, 1\\) and #1 '@kiln' at \\(1, 1\\) - \\(2, 2\\)\\."), + """ + schematic + @power-node-large at (0, 0) + @kiln at (1, 1) + end + """ + ); } @Test void refusesOverlappingBlocks3x3() { - buildSchematicsExpectingError(""" - schematic - @battery-large at (0, 0) - @switch at (2, 2) - end - """, - "Overlapping blocks: #0 '@battery-large' at \\(0, 0\\) - \\(2, 2\\) and #1 '@switch' at \\(2, 2\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Overlapping blocks: #0 '@battery-large' at \\(0, 0\\) - \\(2, 2\\) and #1 '@switch' at \\(2, 2\\)\\."), + """ + schematic + @battery-large at (0, 0) + @switch at (2, 2) + end + """ + ); } @Test @@ -59,7 +69,7 @@ void supportsLinkingNodesByAnyTile() { Schematic expected = new Schematic("", "", List.of(), 5, 3, List.of( - block("@power-node", P0_0, Direction.EAST, pa(P2_0)), + block("@power-node", P0_0, Direction.EAST, pa(P2_0)), block("@battery-large", P2_0, Direction.EAST, EmptyConfiguration.EMPTY) ) ); @@ -81,9 +91,9 @@ void supportsLinkingProcessorsByAnyTile() { Schematic expected = new Schematic("", "", List.of(), 5, 3, List.of( block("@micro-processor", P0_0, Direction.EAST, - new ProcessorConfiguration(List.of(new Link("battery1", 2, 0)),"") + new ProcessorConfiguration(List.of(new Link("battery1", 2, 0)), "") ), - block("@battery-large", P2_0, Direction.EAST, EmptyConfiguration.EMPTY) + block("@battery-large", P2_0, Direction.EAST, EmptyConfiguration.EMPTY) ) ); diff --git a/schemacode/src/test/java/info/teksol/schemacode/schematics/BridgeSolverTest.java b/schemacode/src/test/java/info/teksol/schemacode/schematics/BridgeSolverTest.java index 560207ea0..eedc11e89 100644 --- a/schemacode/src/test/java/info/teksol/schemacode/schematics/BridgeSolverTest.java +++ b/schemacode/src/test/java/info/teksol/schemacode/schematics/BridgeSolverTest.java @@ -2,6 +2,7 @@ import info.teksol.mindcode.mimex.BlockType; import info.teksol.schemacode.AbstractSchematicsTest; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -25,7 +26,7 @@ public List refusesConnectionsToSelfFactory() { "payload-mass-driver", "phase-conduit", "phase-conveyor"); - + for (final String blockType : blockTypes) { result.add(DynamicTest.dynamicTest(blockType, null, () -> refusesConnectionToSelf(blockType))); } @@ -34,12 +35,15 @@ public List refusesConnectionsToSelfFactory() { } private void refusesConnectionToSelf(String blockType) { - buildSchematicsExpectingError(""" - schematic - @%s at (0, 0) connected to (0, 0) - end - """.formatted(blockType), - "Block '@%s' at \\(\\s*0,\\s*0\\) has a connection to self\\.".formatted(blockType)); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@%s' at \\(\\s*0,\\s*0\\) has a connection to self\\.".formatted(blockType)), + """ + schematic + @%s at (0, 0) connected to (0, 0) + end + """.formatted(blockType) + ); } @TestFactory @@ -59,13 +63,16 @@ public List refusesNonOrthogonalConnections() { } private void refusesNonOrthogonalConnection(String blockType) { - buildSchematicsExpectingError(""" - schematic - @%1$s at (0, 0) connected to (5, 5) - @%1$s at (5, 5) - end - """.formatted(blockType), - "Block '@%s' at \\(\\s*0,\\s*0\\) has a connection leading to \\(\\s*5,\\s*5\\), which is neither horizontal nor vertical\\.".formatted(blockType)); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@%s' at \\(\\s*0,\\s*0\\) has a connection leading to \\(\\s*5,\\s*5\\), which is neither horizontal nor vertical\\.".formatted(blockType)), + """ + schematic + @%1$s at (0, 0) connected to (5, 5) + @%1$s at (5, 5) + end + """.formatted(blockType) + ); } @TestFactory @@ -85,57 +92,72 @@ public List refusesOutOfRangeConnections() { } private void refusesOutOfRangeConnection(BlockType blockType) { - buildSchematicsExpectingError(""" - schematic - %s at (0, 0) - %s at (0, %d) connected to (0, 0) - end - """.formatted(blockType.name(), blockType.name(), (int) (blockType.range() + 1.1)), - "Block '%s' at \\(\\s*0,\\s*\\d+\\) has an out-of-range connection to \\(\\s*0,\\s*0\\)\\.".formatted(blockType.name())); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '%s' at \\(\\s*0,\\s*\\d+\\) has an out-of-range connection to \\(\\s*0,\\s*0\\)\\.".formatted(blockType.name())), + """ + schematic + %s at (0, 0) + %s at (0, %d) connected to (0, 0) + end + """.formatted(blockType.name(), blockType.name(), (int) (blockType.range() + 1.1)) + ); } @Test public void refusesMassDriverOutOfRangeConnection() { - buildSchematicsExpectingError(""" - schematic - @mass-driver at (0, 0) - @mass-driver at (39, 39) connected to (0, 0) - end - """, - "Block '@mass-driver' at \\(\\s*\\d+,\\s*\\d+\\) has an out-of-range connection to \\(\\s*0,\\s*0\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@mass-driver' at \\(\\s*\\d+,\\s*\\d+\\) has an out-of-range connection to \\(\\s*0,\\s*0\\)\\."), + """ + schematic + @mass-driver at (0, 0) + @mass-driver at (39, 39) connected to (0, 0) + end + """ + ); } @Test public void refusesBackConnections() { - buildSchematicsExpectingError(""" - schematic - @bridge-conveyor at (0, 0) connected to (2, 0) - @bridge-conveyor at (2, 0) connected to (0, 0) - end - """, - "Two '@bridge-conveyor' blocks at \\(\\s*0,\\s*0\\) and \\(\\s*2,\\s*0\\) connect to each other\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Two '@bridge-conveyor' blocks at \\(\\s*0,\\s*0\\) and \\(\\s*2,\\s*0\\) connect to each other\\."), + """ + schematic + @bridge-conveyor at (0, 0) connected to (2, 0) + @bridge-conveyor at (2, 0) connected to (0, 0) + end + """ + ); } @Test public void refusesTooManyConnections() { - buildSchematicsExpectingError(""" - schematic - @bridge-conveyor at (0, 0) connected to (1, 0), (2, 0) - @bridge-conveyor at (1, 0) - @bridge-conveyor at (2, 0) - end - """, - "Block '@bridge-conveyor' at \\(\\s*0,\\s*0\\) has more than one connection\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@bridge-conveyor' at \\(\\s*0,\\s*0\\) has more than one connection\\."), + """ + schematic + @bridge-conveyor at (0, 0) connected to (1, 0), (2, 0) + @bridge-conveyor at (1, 0) + @bridge-conveyor at (2, 0) + end + """ + ); } @Test public void refusesConnectionsToDifferentType() { - buildSchematicsExpectingError(""" - schematic - @bridge-conveyor at (0, 0) connected to (1, 0) - @phase-conveyor at (1, 0) - end - """, - "Block '@bridge-conveyor' at \\(\\s*0,\\s*0\\) has a connection leading to a different block type '@phase-conveyor' at \\(\\s*1,\\s*0\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@bridge-conveyor' at \\(\\s*0,\\s*0\\) has a connection leading to a different block type '@phase-conveyor' at \\(\\s*1,\\s*0\\)\\."), + """ + schematic + @bridge-conveyor at (0, 0) connected to (1, 0) + @phase-conveyor at (1, 0) + end + """ + ); } } diff --git a/schemacode/src/test/java/info/teksol/schemacode/schematics/PowerGridSolverTest.java b/schemacode/src/test/java/info/teksol/schemacode/schematics/PowerGridSolverTest.java index fd8a22af0..967819bab 100644 --- a/schemacode/src/test/java/info/teksol/schemacode/schematics/PowerGridSolverTest.java +++ b/schemacode/src/test/java/info/teksol/schemacode/schematics/PowerGridSolverTest.java @@ -3,6 +3,7 @@ import info.teksol.schemacode.AbstractSchematicsTest; import info.teksol.schemacode.mindustry.Direction; import info.teksol.schemacode.mindustry.Position; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -18,33 +19,42 @@ class PowerGridSolverTest extends AbstractSchematicsTest { @Test void warnsAboutNonexistentLinkedBlock() { - buildSchematicsExpectingWarning(""" - schematic - @power-node at (0, 0) connected to (1, 1) - end - """, - "Block '@power-node' at \\(\\s*0,\\s*0\\) has a connection to a nonexistent block at \\(\\s*1,\\s*1\\)\\."); + assertGeneratesWarnings( + ExpectedMessages.create() + .addRegex("Block '@power-node' at \\(\\s*0,\\s*0\\) has a connection to a nonexistent block at \\(\\s*1,\\s*1\\)\\."), + """ + schematic + @power-node at (0, 0) connected to (1, 1) + end + """ + ); } @Test void refusesLinksToSelf() { - buildSchematicsExpectingError(""" - schematic - @power-node at (0, 0) connected to (0, 0) - end - """, - "Block '@power-node' at \\(\\s*0,\\s*0\\) has a connection to self\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@power-node' at \\(\\s*0,\\s*0\\) has a connection to self\\."), + """ + schematic + @power-node at (0, 0) connected to (0, 0) + end + """ + ); } @Test void refusesLinksToNoPoweredBlocks() { - buildSchematicsExpectingError(""" - schematic - @power-node at (0, 0) connected to (1, 1) - @conveyor at (1, 1) - end - """, - "Block '@power-node' at \\(\\s*0,\\s*0\\) has an invalid connection to a non-powered block '@conveyor' at \\(\\s*1,\\s*1\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@power-node' at \\(\\s*0,\\s*0\\) has an invalid connection to a non-powered block '@conveyor' at \\(\\s*1,\\s*1\\)\\."), + """ + schematic + @power-node at (0, 0) connected to (1, 1) + @conveyor at (1, 1) + end + """ + ); } @Test @@ -85,7 +95,7 @@ void buildsInRangeLinks() { assertEquals(expected, actual); } - + @Test void buildsInRangeLinksLarge() { Schematic actual = buildSchematics(""" @@ -111,23 +121,23 @@ void buildsInRangeLinksLarge() { Schematic expected = new Schematic("", "", List.of(), 16, 16, List.of( block("@power-node-large", P0_0, Direction.EAST, pa( - p( 1, 15), p( 6, 15), p( 7, 14), p( 8, 14), p( 9, 13), p(10, 12), p(11, 12), - p(12, 11), p(12, 10), p(13, 9), p(14, 8), p(14, 7), p(15, 6), p(15, 1)) + p(1, 15), p(6, 15), p(7, 14), p(8, 14), p(9, 13), p(10, 12), p(11, 12), + p(12, 11), p(12, 10), p(13, 9), p(14, 8), p(14, 7), p(15, 6), p(15, 1)) ), - block("@power-node", p( 1, 15), Direction.EAST, pa(P0_0)), - block("@power-node", p( 6, 15), Direction.EAST, pa(P0_0)), - block("@power-node", p( 7, 14), Direction.EAST, pa(P0_0)), - block("@power-node", p( 8, 14), Direction.EAST, pa(P0_0)), - block("@power-node", p( 9, 13), Direction.EAST, pa(P0_0)), + block("@power-node", p(1, 15), Direction.EAST, pa(P0_0)), + block("@power-node", p(6, 15), Direction.EAST, pa(P0_0)), + block("@power-node", p(7, 14), Direction.EAST, pa(P0_0)), + block("@power-node", p(8, 14), Direction.EAST, pa(P0_0)), + block("@power-node", p(9, 13), Direction.EAST, pa(P0_0)), block("@power-node", p(10, 12), Direction.EAST, pa(P0_0)), block("@power-node", p(11, 12), Direction.EAST, pa(P0_0)), block("@power-node", p(12, 11), Direction.EAST, pa(P0_0)), block("@power-node", p(12, 10), Direction.EAST, pa(P0_0)), - block("@power-node", p(13, 9), Direction.EAST, pa(P0_0)), - block("@power-node", p(14, 8), Direction.EAST, pa(P0_0)), - block("@power-node", p(14, 7), Direction.EAST, pa(P0_0)), - block("@power-node", p(15, 6), Direction.EAST, pa(P0_0)), - block("@power-node", p(15, 1), Direction.EAST, pa(P0_0)) + block("@power-node", p(13, 9), Direction.EAST, pa(P0_0)), + block("@power-node", p(14, 8), Direction.EAST, pa(P0_0)), + block("@power-node", p(14, 7), Direction.EAST, pa(P0_0)), + block("@power-node", p(15, 6), Direction.EAST, pa(P0_0)), + block("@power-node", p(15, 1), Direction.EAST, pa(P0_0)) ) ); @@ -186,13 +196,16 @@ List refusesOutOfRangeLinks() { } private void refusesOutOfRangeLink(Position position) { - buildSchematicsExpectingError(""" - schematic - @power-node at (0, 0) - @power-node at %s connected to (0, 0) - end - """.formatted(position.toStringAbsolute()), - "Block '@power-node' at \\(\\s*\\d+,\\s*\\d+\\) has an out-of-range connection to block '@power-node' at \\(\\s*0,\\s*0\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@power-node' at \\(\\s*\\d+,\\s*\\d+\\) has an out-of-range connection to block '@power-node' at \\(\\s*0,\\s*0\\)\\."), + """ + schematic + @power-node at (0, 0) + @power-node at %s connected to (0, 0) + end + """.formatted(position.toStringAbsolute()) + ); } @TestFactory @@ -211,44 +224,53 @@ List refusesOutOfRangeLinksLarge() { } private void refusesOutOfRangeLinkLarge(Position position) { - buildSchematicsExpectingError(""" - schematic - @power-node-large at (0, 0) - @power-node at %s connected to (0, 0) - end - """.formatted(position.toStringAbsolute()), - "Block '@power-node' at \\(\\s*\\d+,\\s*\\d+\\) has an out-of-range connection to block '@power-node-large' at \\(\\s*0,\\s*0\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@power-node' at \\(\\s*\\d+,\\s*\\d+\\) has an out-of-range connection to block '@power-node-large' at \\(\\s*0,\\s*0\\)\\."), + """ + schematic + @power-node-large at (0, 0) + @power-node at %s connected to (0, 0) + end + """.formatted(position.toStringAbsolute()) + ); } @Test void warnsAboutMultiplyLinkedBlock() { - buildSchematicsExpectingWarning(""" - schematic - @power-node at (0, 0) connected to (1, 1), (1, 1) - @power-node at (1, 1) - end - """, - "Block '@power-node' at \\(\\s*0,\\s*0\\) has multiple connections to block '@power-node' at \\(\\s*1,\\s*1\\)\\."); + assertGeneratesWarnings( + ExpectedMessages.create() + .addRegex("Block '@power-node' at \\(\\s*0,\\s*0\\) has multiple connections to block '@power-node' at \\(\\s*1,\\s*1\\)\\."), + """ + schematic + @power-node at (0, 0) connected to (1, 1), (1, 1) + @power-node at (1, 1) + end + """ + ); } @Test void refusesTooManyLinks() { - buildSchematicsExpectingError(""" - schematic - @power-node at ( 0, 0) - @power-node at ( 0, 6) connected to (0, 0) - @power-node at ( 1, 6) connected to (0, 0) - @power-node at ( 2, 6) connected to (0, 0) - @power-node at ( 3, 5) connected to (0, 0) - @power-node at ( 4, 5) connected to (0, 0) - @power-node at ( 5, 3) connected to (0, 0) - @power-node at ( 5, 4) connected to (0, 0) - @power-node at ( 6, 0) connected to (0, 0) - @power-node at ( 6, 1) connected to (0, 0) - @power-node at ( 6, 2) connected to (0, 0) - @power-node at ( 1, 1) connected to (0, 0) - end - """, - "Block '@power-node' at \\(\\s*0,\\s*0\\) has more than 10 connection\\(s\\)\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@power-node' at \\(\\s*0,\\s*0\\) has more than 10 connection\\(s\\)\\."), + """ + schematic + @power-node at ( 0, 0) + @power-node at ( 0, 6) connected to (0, 0) + @power-node at ( 1, 6) connected to (0, 0) + @power-node at ( 2, 6) connected to (0, 0) + @power-node at ( 3, 5) connected to (0, 0) + @power-node at ( 4, 5) connected to (0, 0) + @power-node at ( 5, 3) connected to (0, 0) + @power-node at ( 5, 4) connected to (0, 0) + @power-node at ( 6, 0) connected to (0, 0) + @power-node at ( 6, 1) connected to (0, 0) + @power-node at ( 6, 2) connected to (0, 0) + @power-node at ( 1, 1) connected to (0, 0) + end + """ + ); } } diff --git a/schemacode/src/test/java/info/teksol/schemacode/schematics/SchematicsBuilderTest.java b/schemacode/src/test/java/info/teksol/schemacode/schematics/SchematicsBuilderTest.java index f352cd5cb..05e7d45c4 100644 --- a/schemacode/src/test/java/info/teksol/schemacode/schematics/SchematicsBuilderTest.java +++ b/schemacode/src/test/java/info/teksol/schemacode/schematics/SchematicsBuilderTest.java @@ -8,6 +8,7 @@ import info.teksol.schemacode.config.TextConfiguration; import info.teksol.schemacode.mindustry.*; import info.teksol.schemacode.mindustry.ProcessorConfiguration.Link; +import info.teksol.util.ExpectedMessages; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -57,15 +58,21 @@ void unwrapsStringBlockRemovingEmptyLines() { @Test void refusesMissingSchematics() { - buildSchematicsExpectingError(""" + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("No schematic defined."), + """ constant = "foo" - """, - "No schematic defined."); + """ + ); } @Test void refusesMultipleSchematics() { - buildSchematicsExpectingError(""" + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("More than one schematic defined."), + """ schematic name = "First" end @@ -73,47 +80,61 @@ void refusesMultipleSchematics() { schematic name = "Second" end - """, - "More than one schematic defined."); + """ + ); } @Test void refusesConstantRedefinitions() { - buildSchematicsExpectingError(""" + assertGeneratesErrors( + ExpectedMessages.create() + .add("Identifier 'constant' already defined."), + """ schematic name = "Schematics" end constant = "foo" constant = "bar" - """, - "Identifier 'constant' defined more than once."); + """ + ); } @Test void refusesUndefinedConstants() { - buildSchematicsExpectingError(""" + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Undefined identifier 'foo'."), + """ schematic name = foo end - """, - "Undefined identifier 'foo'."); + """ + ); } @Test void refusesReusedBlockLabels() { - buildSchematicsExpectingError(""" + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Multiple definitions of block label 'switch1'."), + """ schematic switch1: @switch at (0, 0) switch1: @switch at (1, 0) end - """, - "Multiple definitions of block label 'switch1'."); + """ + ); } @Test void refusesCircularPositionDefinition() { - buildSchematicsExpectingError(""" + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Overlapping blocks: #\\d '@switch' at \\(0, 0\\) and #\\d '@switch' at \\(0, 0\\).").repeat(2) + .addRegex("Circular definition of block 'switch2' position.") + .addRegex("Circular definition of block 'switch3' position."), + """ schematic switch1: @switch at (0, 0) @@ -122,21 +143,25 @@ void refusesCircularPositionDefinition() { switch3: @switch at switch2 - (1, 0) end - """, - "Circular definition of block 'switch2' position."); + """ + ); } @Test void refusesUnknownBlockReference() { - buildSchematicsExpectingError(""" + assertGeneratesErrors( + ExpectedMessages.create() + .add("Unknown block name 'switch3'.").atLeast(1) + .add("Overlapping blocks: #0 '@switch' at (0, 0) and #1 '@switch' at (0, 0)."), + """ schematic switch1: @switch at (0, 0) switch2: @switch at switch3 + (1, 0) end - """, - "Unknown block name 'switch3'."); + """ + ); } @Test @@ -170,13 +195,16 @@ void buildsSchematicsWithIndirectName() { @Test void refuseMultipleNames() { - buildSchematicsExpectingError(""" - schematic - name = "Name" - name = "Another" - end - """, - "Multiple definitions of attribute 'name'."); + assertGeneratesErrors( + ExpectedMessages.create() + .add("Attribute 'name' is already defined."), + """ + schematic + name = "Name" + name = "Another" + end + """ + ); } @Test @@ -244,7 +272,7 @@ void buildsSchematicsWithDimensions() { Schematic expected = new Schematic("", "", List.of(), 5, 7, List.of( - block("@message", P0_0, Direction.EAST, TextConfiguration.EMPTY), + block("@message", P0_0, Direction.EAST, TextConfiguration.EMPTY), block("@message", p(4, 6), Direction.EAST, TextConfiguration.EMPTY) ) ); @@ -262,7 +290,7 @@ void buildsSchematicsWithPositiveOrigin() { Schematic expected = new Schematic("", "", List.of(), 1, 1, List.of( - block("@message", P0_0, Direction.EAST, TextConfiguration.EMPTY) + block("@message", P0_0, Direction.EAST, TextConfiguration.EMPTY) ) ); @@ -279,7 +307,7 @@ void buildsSchematicsWithNegativeOrigin() { Schematic expected = new Schematic("", "", List.of(), 1, 1, List.of( - block("@message", P0_0, Direction.EAST, TextConfiguration.EMPTY) + block("@message", P0_0, Direction.EAST, TextConfiguration.EMPTY) ) ); @@ -617,7 +645,7 @@ void buildsSchematicsWithNamedBridgeConnection() { Schematic expected = new Schematic("", "", List.of(), 2, 1, List.of( - block(List.of(), "@bridge-conveyor", P0_0, Direction.EAST, pa(P1_0)), + block(List.of(), "@bridge-conveyor", P0_0, Direction.EAST, pa(P1_0)), block(List.of("bridge1"), "@bridge-conveyor", P1_0, Direction.EAST, pa()) ) ); @@ -645,12 +673,15 @@ void buildsSchematicsWithUnloaderCoal() { @Test void refusesSchematicsWithInvalidItemConfiguration() { - buildSchematicsExpectingError(""" - schematic - @unloader at (0, 0) item @fluffyBunny - end - """, - "Block '@unloader' at \\(\\s*0,\\s*0\\): unknown or unsupported item '@fluffyBunny'\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@unloader' at \\(\\s*0,\\s*0\\): unknown or unsupported item '@fluffyBunny'\\."), + """ + schematic + @unloader at (0, 0) item @fluffyBunny + end + """ + ); } @Test @@ -673,12 +704,15 @@ void buildsSchematicsWithLiquidSourceCryofluid() { @Test void refusesSchematicsWithInvalidLiquidConfiguration() { - buildSchematicsExpectingError(""" - schematic - @liquid-source at (0, 0) liquid @fluffyBunny - end - """, - "Block '@liquid-source' at \\(\\s*0,\\s*0\\): unknown or unsupported liquid '@fluffyBunny'\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@liquid-source' at \\(\\s*0,\\s*0\\): unknown or unsupported liquid '@fluffyBunny'\\."), + """ + schematic + @liquid-source at (0, 0) liquid @fluffyBunny + end + """ + ); } @Test @@ -725,23 +759,27 @@ void buildsSchematicsWithMicroProcessorLinks() { @Test void refusesWrongLinkNames() { - buildSchematicsExpectingError(""" - schematic - dimensions = (1, 1) - @micro-processor at (0, 0) processor - links - switch1 as message1 - message1 as switch1 - end - mlog = "" - end - switch1: - @switch at +(1, 0) - message1: - @message at +(1, 0) - end - """, - "Incompatible link name 'message1' for block type '@switch'."); + assertGeneratesErrors( + ExpectedMessages.create() + .add("Incompatible link name 'message1' for block type '@switch'.") + .add("Incompatible link name 'switch1' for block type '@message'."), + """ + schematic + dimensions = (3, 1) + @micro-processor at (0, 0) processor + links + switch1 as message1 + message1 as switch1 + end + mlog = "" + end + switch1: + @switch at +(1, 0) + message1: + @message at +(1, 0) + end + """ + ); } @Test @@ -830,12 +868,15 @@ void buildsSchematicsWithAirFactoryMono() { @Test void refusesUnsupportedUnitConfiguration() { - buildSchematicsExpectingError(""" - schematic - @air-factory at (0, 0) unit @poly - end - """, - "Block '@air-factory' at \\(\\s*0,\\s*0\\): unknown or unsupported unit type '@poly'\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@air-factory' at \\(\\s*0,\\s*0\\): unknown or unsupported unit type '@poly'\\."), + """ + schematic + @air-factory at (0, 0) unit @poly + end + """ + ); } @Test @@ -857,64 +898,83 @@ void buildsIlluminatorWithColor() { @Test void refusesWrongColorValueRed() { - buildSchematicsExpectingError(""" - schematic - @illuminator at (0, 0) color rgba(256, 0, 0, 127) - end - """, - "Block '@illuminator' at \\(\\s*0,\\s*0\\): value 256 of color component 'red' outside valid range <0, 255>\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@illuminator' at \\(\\s*0,\\s*0\\): value 256 of color component 'red' outside valid range <0, 255>\\."), + """ + schematic + @illuminator at (0, 0) color rgba(256, 0, 0, 127) + end + """ + ); } @Test void refusesWrongColorValueGreen() { - buildSchematicsExpectingError(""" - schematic - @illuminator at (0, 0) color rgba(255, -1, 0, 127) - end - """, - "Block '@illuminator' at \\(\\s*0,\\s*0\\): value -1 of color component 'green' outside valid range <0, 255>\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@illuminator' at \\(\\s*0,\\s*0\\): value -1 of color component 'green' outside valid range <0, 255>\\."), + """ + schematic + @illuminator at (0, 0) color rgba(255, -1, 0, 127) + end + """ + ); } @Test void refusesWrongColorValueBlue() { - buildSchematicsExpectingError(""" - schematic - @illuminator at (0, 0) color rgba(255, 0, 99999, 127) - end - """, - "Block '@illuminator' at \\(\\s*0,\\s*0\\): value 99999 of color component 'blue' outside valid range <0, 255>\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@illuminator' at \\(\\s*0,\\s*0\\): value 99999 of color component 'blue' outside valid range <0, 255>\\."), + """ + schematic + @illuminator at (0, 0) color rgba(255, 0, 99999, 127) + end + """ + ); } @Test void refusesWrongColorValueAlpha() { - buildSchematicsExpectingError(""" - schematic - @illuminator at (0, 0) color rgba(255, 0, 0, 256) - end - """, - "Block '@illuminator' at \\(\\s*0,\\s*0\\): value 256 of color component 'alpha' outside valid range <0, 255>\\."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Block '@illuminator' at \\(\\s*0,\\s*0\\): value 256 of color component 'alpha' outside valid range <0, 255>\\."), + """ + schematic + @illuminator at (0, 0) color rgba(255, 0, 0, 256) + end + """ + ); } @Test void refusesUnknownBlocks() { - buildSchematicsExpectingError(""" - schematic - dimensions = (1, 1) - @fluffyBunny at (0, 0) - end - """, - "Unknown block type '@fluffyBunny'."); + assertGeneratesErrors( + ExpectedMessages.create() + .add("Unknown block type '@fluffyBunny'.") + .add("Actual schematic dimensions ( 0, 0) are smaller than specified dimensions ( 1, 1)."), + """ + schematic + dimensions = (1, 1) + @fluffyBunny at (0, 0) + end + """ + ); } @Test void refusesWrongConfiguration() { - buildSchematicsExpectingError(""" - schematic - dimensions = (1, 1) - @message at (0, 0) enabled - end - """, - "Unexpected configuration type for block '@message' at \\(\\s*0,\\s*0\\): expected TEXT, found BOOLEAN."); + assertGeneratesErrors( + ExpectedMessages.create() + .addRegex("Unexpected configuration type for block '@message' at \\(\\s*0,\\s*0\\): expected TEXT, found BOOLEAN."), + """ + schematic + dimensions = (1, 1) + @message at (0, 0) enabled + end + """ + ); } @Test diff --git a/toolapp/src/main/java/info/teksol/mindcode/cmdline/ActionHandler.java b/toolapp/src/main/java/info/teksol/mindcode/cmdline/ActionHandler.java index 1ad21c2b4..2c52ecfba 100644 --- a/toolapp/src/main/java/info/teksol/mindcode/cmdline/ActionHandler.java +++ b/toolapp/src/main/java/info/teksol/mindcode/cmdline/ActionHandler.java @@ -1,6 +1,7 @@ package info.teksol.mindcode.cmdline; import info.teksol.mindcode.InputFile; +import info.teksol.mindcode.InputPosition; import info.teksol.mindcode.compiler.*; import info.teksol.mindcode.compiler.optimization.Optimization; import info.teksol.mindcode.compiler.optimization.OptimizationLevel; @@ -20,6 +21,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.List; +import java.util.function.Function; import java.util.stream.Collectors; abstract class ActionHandler { @@ -250,8 +252,8 @@ static void writeOutput(File outputFile, List data, boolean useErrorOutp } } - static void writeOutput(File outputFile, String data, boolean useErrorOutput) { - writeOutput(outputFile, List.of(data), useErrorOutput); + static void writeOutput(File outputFile, String data) { + writeOutput(outputFile, List.of(data), false); } static void writeOutput(File outputFile, byte[] data) { @@ -267,4 +269,19 @@ static void writeToClipboard(String string) { StringSelection data = new StringSelection(string); c.setContents(data, data); } + + static void outputMessages(CompilerOutput result, File outputFile, File logFile, Function positionFormatter) { + // If mlog gets written to stdout, write log to stderr + if (isStdInOut(logFile)) { + boolean alwaysErr = isStdInOut(outputFile); + result.messages().forEach(m -> (alwaysErr || m.isErrorOrWarning() ? System.err : System.out).println(m.formatMessage(positionFormatter))); + } else { + writeOutput(logFile, result.texts(m -> m.formatMessage(positionFormatter)), isStdInOut(outputFile)); + // Print errors and warnings to stderr anyway + result.messages().stream() + .filter(m -> m.isErrorOrWarning() || m.isInfo()) + .forEach(m -> (m.isErrorOrWarning() ? System.err : System.out).println(m.formatMessage(positionFormatter))); + } + + } } diff --git a/toolapp/src/main/java/info/teksol/mindcode/cmdline/CompileMindcodeAction.java b/toolapp/src/main/java/info/teksol/mindcode/cmdline/CompileMindcodeAction.java index 789505ace..1a6b3668f 100644 --- a/toolapp/src/main/java/info/teksol/mindcode/cmdline/CompileMindcodeAction.java +++ b/toolapp/src/main/java/info/teksol/mindcode/cmdline/CompileMindcodeAction.java @@ -117,13 +117,12 @@ void handle(Namespace arguments) { final CompilerOutput result = compile(inputFiles, compilerProfile); - File output = resolveOutputFile(arguments.get("input"), arguments.get("output"), ".mlog"); - File logFile = resolveOutputFile(arguments.get("input"), arguments.get("log"), ".log"); - boolean mlogToStdErr = isStdInOut(output); - Function positionFormatter = InputPosition::formatForIde; + final File output = resolveOutputFile(arguments.get("input"), arguments.get("output"), ".mlog"); + final File logFile = resolveOutputFile(arguments.get("input"), arguments.get("log"), ".log"); + final Function positionFormatter = InputPosition::formatForIde; if (!result.hasErrors()) { - writeOutput(output, result.output(), false); + writeOutput(output, result.output()); if (arguments.getBoolean("clipboard")) { writeToClipboard(result.output()); @@ -148,17 +147,7 @@ void handle(Namespace arguments) { } } - // If mlog gets written to stdout, write log to stderr - if (isStdInOut(logFile)) { - boolean alwaysErr = isStdInOut(output); - result.messages().forEach(m -> (alwaysErr || m.isErrorOrWarning() ? System.err : System.out).println(m.formatMessage(positionFormatter))); - } else { - writeOutput(logFile, result.texts(m -> m.formatMessage(positionFormatter)), mlogToStdErr); - // Print errors and warnings to stderr anyway - result.messages().stream() - .filter(m -> m.isErrorOrWarning() || m.isInfo()) - .forEach(m -> (m.isErrorOrWarning() ? System.err : System.out).println(m.formatMessage(positionFormatter))); - } + outputMessages(result, output, logFile, positionFormatter); } else { // Errors: print just them into stderr List errors = result.errors(m -> m.formatMessage(positionFormatter)); diff --git a/toolapp/src/main/java/info/teksol/mindcode/cmdline/CompileSchemacodeAction.java b/toolapp/src/main/java/info/teksol/mindcode/cmdline/CompileSchemacodeAction.java index d7e756d62..e12f39ca0 100644 --- a/toolapp/src/main/java/info/teksol/mindcode/cmdline/CompileSchemacodeAction.java +++ b/toolapp/src/main/java/info/teksol/mindcode/cmdline/CompileSchemacodeAction.java @@ -2,6 +2,7 @@ import info.teksol.mindcode.InputFile; import info.teksol.mindcode.InputPosition; +import info.teksol.mindcode.ToolMessage; import info.teksol.mindcode.cmdline.Main.Action; import info.teksol.mindcode.compiler.CompilerOutput; import info.teksol.mindcode.compiler.CompilerProfile; @@ -70,41 +71,33 @@ Subparser appendSubparser(Subparsers subparsers, FileArgumentType inputFileType, void handle(Namespace arguments) { CompilerProfile compilerProfile = createCompilerProfile(arguments); compilerProfile.setAdditionalTags(arguments.get("add_tag")); - File file = arguments.get("input"); - InputFile inputFile = readFile(file, true); - Path basePath = isStdInOut(file) ? Paths.get("") : file.toPath().toAbsolutePath().getParent(); + final File file = arguments.get("input"); + final InputFile inputFile = readFile(file, true); + final Path basePath = isStdInOut(file) ? Paths.get("") : file.toPath().toAbsolutePath().getParent(); - CompilerOutput result = SchemacodeCompiler.compile(inputFile , compilerProfile, basePath); + final CompilerOutput result = SchemacodeCompiler.compile(inputFile , compilerProfile, basePath); - Function positionFormatter = InputPosition::formatForIde; - - File output = resolveOutputFile(file, arguments.get("output"), ".msch"); - File logFile = resolveOutputFile(file, arguments.get("log"), ".log"); + final File output = resolveOutputFile(file, arguments.get("output"), ".msch"); + final File logFile = resolveOutputFile(file, arguments.get("log"), ".log"); + final boolean mlogToStdErr = isStdInOut(output); + final Function positionFormatter = InputPosition::formatForIde; if (!result.hasErrors()) { writeOutput(output, result.output()); - List allTexts = result.texts(m -> m.formatMessage(positionFormatter)); if (arguments.getBoolean("clipboard")) { writeToClipboard(Base64.getEncoder().encodeToString(result.output())); - allTexts.add(""); - allTexts.add("Created schematic was copied to the clipboard."); + result.addMessage(ToolMessage.info("\nCreated schematic was copied to the clipboard.")); } - writeOutput(logFile, allTexts, false); - - // Print errors and warnings to console anyway - if (!isStdInOut(logFile)) { - result.messages().stream() - .filter(m -> m.isError() || m.isWarning()) - .map(m -> m.formatMessage(positionFormatter)) - .forEach(System.out::println); - } + outputMessages(result, output, logFile, positionFormatter); } else { // Errors: print just them into stderr - result.errors(m -> m.formatMessage(positionFormatter)).forEach(System.err::println); + List errors = result.errors(m -> m.formatMessage(positionFormatter)); + + errors.forEach(System.err::println); if (!isStdInOut(logFile)) { - writeOutput(logFile, result.errors(m -> m.formatMessage(positionFormatter)), true); + writeOutput(logFile, errors, true); } System.exit(1); } diff --git a/toolapp/src/main/java/info/teksol/mindcode/cmdline/DecompileMlogAction.java b/toolapp/src/main/java/info/teksol/mindcode/cmdline/DecompileMlogAction.java index b1b714c7b..cbadcd387 100644 --- a/toolapp/src/main/java/info/teksol/mindcode/cmdline/DecompileMlogAction.java +++ b/toolapp/src/main/java/info/teksol/mindcode/cmdline/DecompileMlogAction.java @@ -38,6 +38,6 @@ void handle(Namespace arguments) { File output = resolveOutputFile(input, arguments.get("output"), ".dmnd"); String mlog = readInput(input); String decompiled = MlogDecompiler.decompile(mlog); - writeOutput(output, decompiled, false); + writeOutput(output, decompiled); } } diff --git a/toolapp/src/main/java/info/teksol/mindcode/cmdline/DecompileSchemacodeAction.java b/toolapp/src/main/java/info/teksol/mindcode/cmdline/DecompileSchemacodeAction.java index 014b1da87..56a59a17e 100644 --- a/toolapp/src/main/java/info/teksol/mindcode/cmdline/DecompileSchemacodeAction.java +++ b/toolapp/src/main/java/info/teksol/mindcode/cmdline/DecompileSchemacodeAction.java @@ -101,7 +101,7 @@ void handle(Namespace arguments) { decompiler.setDirectionLevel(arguments.get("direction")); String schemaDefinition = decompiler.buildCode(); - writeOutput(output, schemaDefinition, false); + writeOutput(output, schemaDefinition); } catch (IOException e) { throw new info.teksol.mindcode.cmdline.ProcessingException(e, "Error reading file '%s': %s", input.getPath(), e.getMessage()); }