mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Add lint validation for break/continue statement outside the loop body
This commit is contained in:
parent
ec04e5faca
commit
cef845f0f8
@ -17,8 +17,11 @@ public class ErrorsLocationFormatterStage implements Stage<Iterable<? extends So
|
||||
for (SourceLocatedError error : input) {
|
||||
sb.append(Console.newline());
|
||||
sb.append(error);
|
||||
sb.append(Console.newline());
|
||||
final Range range = error.getRange();
|
||||
if (range != null) {
|
||||
sb.append(' ').append(range.format());
|
||||
}
|
||||
sb.append(Console.newline());
|
||||
if (range != null && lines.length > 0) {
|
||||
var positions = stagesData.getOrDefault(TAG_POSITIONS, HashSet::new);
|
||||
positions.add(range);
|
||||
|
@ -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());
|
||||
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ public class LinterStage implements Stage<Node, Node> {
|
||||
final LinterResults results = new LinterResults();
|
||||
final List<Visitor> validators = new ArrayList<>();
|
||||
validators.add(new IncludeSourceValidator(results));
|
||||
validators.add(new LoopStatementsValidator(results));
|
||||
|
||||
if (mode == Mode.SEMANTIC) {
|
||||
validators.forEach(input::accept);
|
||||
|
@ -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> 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()));
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user