diff --git a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java index 22d5c98..1ddc74e 100644 --- a/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java +++ b/ownlang-core/src/main/java/com/annimon/ownlang/util/ErrorsLocationFormatterStage.java @@ -17,8 +17,11 @@ public class ErrorsLocationFormatterStage implements Stage 0) { var positions = stagesData.getOrDefault(TAG_POSITIONS, HashSet::new); positions.add(range); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java index 1c8417f..cf8522f 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/Parser.java @@ -135,10 +135,10 @@ public final class Parser { return doWhileStatement(); } if (match(TokenType.BREAK)) { - return new BreakStatement(); + return new BreakStatement(getRange(index - 1, index - 1)); } if (match(TokenType.CONTINUE)) { - return new ContinueStatement(); + return new ContinueStatement(getRange(index - 1, index - 1)); } if (match(TokenType.RETURN)) { return new ReturnStatement(expression()); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java index 925a7c9..7813855 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/BreakStatement.java @@ -1,12 +1,24 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public final class BreakStatement extends RuntimeException implements Statement { +public final class BreakStatement extends RuntimeException implements Statement, SourceLocation { + private final Range range; + + public BreakStatement(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } @Override public Value eval() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java index d8ea631..311c97a 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/ast/ContinueStatement.java @@ -1,12 +1,24 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.util.Range; +import com.annimon.ownlang.util.SourceLocation; /** * * @author aNNiMON */ -public final class ContinueStatement extends RuntimeException implements Statement { +public final class ContinueStatement extends RuntimeException implements Statement, SourceLocation { + private final Range range; + + public ContinueStatement(Range range) { + this.range = range; + } + + @Override + public Range getRange() { + return range; + } @Override public Value eval() { diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java index 5fee252..d5727f2 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/AssignValidator.java @@ -35,8 +35,8 @@ final class AssignValidator extends LintVisitor { } @Override - public void visit(UseStatement st) { - super.visit(st); - moduleConstants.addAll(st.loadConstants().keySet()); + public void visit(UseStatement s) { + super.visit(s); + moduleConstants.addAll(s.loadConstants().keySet()); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java index 12c765c..129ea42 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/DefaultFunctionsOverrideValidator.java @@ -22,14 +22,14 @@ final class DefaultFunctionsOverrideValidator extends LintVisitor { } @Override - public void visit(IncludeStatement st) { - super.visit(st); - applyVisitor(st, this); + public void visit(IncludeStatement s) { + super.visit(s); + applyVisitor(s, this); } @Override - public void visit(UseStatement st) { - super.visit(st); - moduleFunctions.addAll(st.loadFunctions().keySet()); + public void visit(UseStatement s) { + super.visit(s); + moduleFunctions.addAll(s.loadFunctions().keySet()); } } diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java index b815778..61044c6 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LinterStage.java @@ -27,6 +27,7 @@ public class LinterStage implements Stage { final LinterResults results = new LinterResults(); final List validators = new ArrayList<>(); validators.add(new IncludeSourceValidator(results)); + validators.add(new LoopStatementsValidator(results)); if (mode == Mode.SEMANTIC) { validators.forEach(input::accept); diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LoopStatementsValidator.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LoopStatementsValidator.java new file mode 100644 index 0000000..f464f37 --- /dev/null +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/linters/LoopStatementsValidator.java @@ -0,0 +1,76 @@ +package com.annimon.ownlang.parser.linters; + +import com.annimon.ownlang.parser.ast.*; +import java.util.ArrayDeque; +import java.util.Deque; + +final class LoopStatementsValidator extends LintVisitor { + private enum LoopScope { FOR, WHILE, DO_WHILE, FOREACH_ARR, FOREACH_MAP }; + + private final Deque loopScope; + + LoopStatementsValidator(LinterResults results) { + super(results); + loopScope = new ArrayDeque<>(10); + } + + @Override + public void visit(ForStatement s) { + s.initialization.accept(this); + s.termination.accept(this); + s.increment.accept(this); + loopScope.push(LoopScope.FOR); + s.statement.accept(this); + loopScope.remove(); + } + + @Override + public void visit(DoWhileStatement s) { + s.condition.accept(this); + loopScope.push(LoopScope.DO_WHILE); + s.statement.accept(this); + loopScope.remove(); + } + + @Override + public void visit(ForeachArrayStatement s) { + s.container.accept(this); + loopScope.push(LoopScope.FOREACH_ARR); + s.body.accept(this); + loopScope.remove(); + } + + @Override + public void visit(ForeachMapStatement s) { + s.container.accept(this); + loopScope.push(LoopScope.FOREACH_MAP); + s.body.accept(this); + loopScope.remove(); + } + + @Override + public void visit(WhileStatement s) { + s.condition.accept(this); + loopScope.push(LoopScope.WHILE); + s.statement.accept(this); + loopScope.remove(); + } + + @Override + public void visit(BreakStatement s) { + if (loopScope.isEmpty()) { + results.add(LinterResult.error( + "break statement shouldn't be placed outside the loop body", + s.getRange())); + } + } + + @Override + public void visit(ContinueStatement s) { + if (loopScope.isEmpty()) { + results.add(LinterResult.error( + "continue statement shouldn't be placed outside the loop body", + s.getRange())); + } + } +} diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index f2bac0d..c352b37 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -185,13 +185,13 @@ public abstract class AbstractVisitor implements Visitor { } @Override - public void visit(WhileStatement st) { - st.condition.accept(this); - st.statement.accept(this); + public void visit(WhileStatement s) { + s.condition.accept(this); + s.statement.accept(this); } @Override - public void visit(UseStatement st) { + public void visit(UseStatement s) { } } \ No newline at end of file diff --git a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java index d83020f..7b8e3df 100644 --- a/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java +++ b/ownlang-parser/src/main/java/com/annimon/ownlang/parser/visitors/ModuleDetector.java @@ -19,8 +19,8 @@ public class ModuleDetector extends AbstractVisitor { } @Override - public void visit(UseStatement st) { - modules.addAll(st.modules); - super.visit(st); + public void visit(UseStatement s) { + modules.addAll(s.modules); + super.visit(s); } }