Show linter results after validation

This commit is contained in:
aNNiMON 2023-10-01 14:42:27 +03:00 committed by Victor Melnik
parent 2578f0a6b4
commit ce14581bf4
9 changed files with 96 additions and 65 deletions

View File

@ -4,6 +4,7 @@ import com.annimon.ownlang.exceptions.OwnLangParserException;
import com.annimon.ownlang.exceptions.StoppedException;
import com.annimon.ownlang.parser.*;
import com.annimon.ownlang.parser.ast.Statement;
import com.annimon.ownlang.parser.linters.LinterStage;
import com.annimon.ownlang.stages.*;
import com.annimon.ownlang.utils.Repl;
import com.annimon.ownlang.utils.Sandbox;

View File

@ -1,38 +0,0 @@
package com.annimon.ownlang.parser;
import com.annimon.ownlang.Console;
import com.annimon.ownlang.lib.ScopeHandler;
import com.annimon.ownlang.parser.ast.Statement;
import com.annimon.ownlang.parser.ast.Visitor;
import com.annimon.ownlang.parser.linters.AssignValidator;
import com.annimon.ownlang.parser.linters.DefaultFunctionsOverrideValidator;
public final class Linter {
public static void lint(Statement program) {
new Linter(program).execute();
}
private final Statement program;
private Linter(Statement program) {
this.program = program;
}
public void execute() {
final Visitor[] validators = new Visitor[] {
new AssignValidator(),
new DefaultFunctionsOverrideValidator()
};
resetState();
for (Visitor validator : validators) {
program.accept(validator);
resetState();
}
Console.println("Lint validation complete!");
}
private void resetState() {
ScopeHandler.resetScope();
}
}

View File

@ -1,5 +1,6 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.Function;
import com.annimon.ownlang.lib.ModuleLoader;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.modules.Module;
@ -35,6 +36,15 @@ public final class UseStatement extends InterruptableNode implements Statement {
return result;
}
public Map<String, Function> loadFunctions() {
final var result = new LinkedHashMap<String, Function>();
for (String moduleName : modules) {
final Module module = ModuleLoader.load(moduleName);
result.putAll(module.functions());
}
return result;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);

View File

@ -1,23 +1,31 @@
package com.annimon.ownlang.parser.linters;
import com.annimon.ownlang.Console;
import com.annimon.ownlang.lib.ScopeHandler;
import com.annimon.ownlang.parser.ast.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author aNNiMON
*/
public final class AssignValidator extends LintVisitor {
final class AssignValidator extends LintVisitor {
private final Set<String> moduleConstants = new HashSet<>();
AssignValidator(Collection<LinterResult> results) {
super(results);
}
@Override
public void visit(AssignmentExpression s) {
super.visit(s);
if (s.target instanceof VariableExpression varExpr) {
final String variable = varExpr.name;
if (ScopeHandler.isConstantExists(variable)) {
Console.error(String.format(
"Warning: variable \"%s\" overrides constant", variable));
if (moduleConstants.contains(variable)) {
results.add(new LinterResult(LinterResult.Severity.WARNING,
String.format("Variable \"%s\" overrides constant", variable)));
}
}
}
@ -31,6 +39,6 @@ public final class AssignValidator extends LintVisitor {
@Override
public void visit(UseStatement st) {
super.visit(st);
st.execute();
moduleConstants.addAll(st.loadConstants().keySet());
}
}

View File

@ -1,17 +1,24 @@
package com.annimon.ownlang.parser.linters;
import com.annimon.ownlang.Console;
import com.annimon.ownlang.lib.ScopeHandler;
import com.annimon.ownlang.parser.ast.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public final class DefaultFunctionsOverrideValidator extends LintVisitor {
final class DefaultFunctionsOverrideValidator extends LintVisitor {
private final Set<String> moduleFunctions = new HashSet<>();
DefaultFunctionsOverrideValidator(Collection<LinterResult> results) {
super(results);
}
@Override
public void visit(FunctionDefineStatement s) {
super.visit(s);
if (ScopeHandler.isFunctionExists(s.name)) {
Console.error(String.format(
"Warning: function \"%s\" overrides default module function", s.name));
if (moduleFunctions.contains(s.name)) {
results.add(new LinterResult(LinterResult.Severity.WARNING,
String.format("Function \"%s\" overrides default module function", s.name)));
}
}
@ -24,6 +31,6 @@ public final class DefaultFunctionsOverrideValidator extends LintVisitor {
@Override
public void visit(UseStatement st) {
super.visit(st);
st.execute();
moduleFunctions.addAll(st.loadFunctions().keySet());
}
}

View File

@ -5,8 +5,14 @@ import com.annimon.ownlang.parser.ast.Statement;
import com.annimon.ownlang.parser.ast.Visitor;
import com.annimon.ownlang.parser.visitors.AbstractVisitor;
import com.annimon.ownlang.parser.visitors.VisitorUtils;
import java.util.Collection;
public abstract class LintVisitor extends AbstractVisitor {
abstract class LintVisitor extends AbstractVisitor {
protected final Collection<LinterResult> results;
LintVisitor(Collection<LinterResult> results) {
this.results = results;
}
protected void applyVisitor(IncludeStatement s, Visitor visitor) {
final Statement program = VisitorUtils.includeProgram(s);

View File

@ -0,0 +1,11 @@
package com.annimon.ownlang.parser.linters;
record LinterResult(Severity severity, String message) {
enum Severity { ERROR, WARNING }
@Override
public String toString() {
return severity.name() + ": " + message;
}
}

View File

@ -0,0 +1,39 @@
package com.annimon.ownlang.parser.linters;
import com.annimon.ownlang.Console;
import com.annimon.ownlang.lib.ScopeHandler;
import com.annimon.ownlang.parser.ast.Statement;
import com.annimon.ownlang.parser.ast.Visitor;
import com.annimon.ownlang.stages.Stage;
import com.annimon.ownlang.stages.StagesData;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class LinterStage implements Stage<Statement, Statement> {
@Override
public Statement perform(StagesData stagesData, Statement input) {
final List<LinterResult> results = new ArrayList<>();
final Visitor[] validators = new Visitor[] {
new AssignValidator(results),
new DefaultFunctionsOverrideValidator(results)
};
ScopeHandler.resetScope();
for (Visitor validator : validators) {
input.accept(validator);
ScopeHandler.resetScope();
}
results.sort(Comparator.comparing(LinterResult::severity));
Console.println(String.format("Lint validation completed. %d results found!", results.size()));
for (LinterResult r : results) {
switch (r.severity()) {
case ERROR -> Console.error(r.toString());
case WARNING -> Console.println(r.toString());
}
}
return input;
}
}

View File

@ -1,13 +0,0 @@
package com.annimon.ownlang.stages;
import com.annimon.ownlang.parser.Linter;
import com.annimon.ownlang.parser.ast.Statement;
public class LinterStage implements Stage<Statement, Statement> {
@Override
public Statement perform(StagesData stagesData, Statement input) {
Linter.lint(input);
return input;
}
}