mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 08:44:20 +03:00
Добавлен линтер
This commit is contained in:
parent
840f049e15
commit
138100bfe0
@ -4,6 +4,7 @@ import com.annimon.ownlang.utils.TimeMeasurement;
|
||||
import com.annimon.ownlang.exceptions.LexerException;
|
||||
import com.annimon.ownlang.parser.Beautifier;
|
||||
import com.annimon.ownlang.parser.Lexer;
|
||||
import com.annimon.ownlang.parser.Linter;
|
||||
import com.annimon.ownlang.parser.Parser;
|
||||
import com.annimon.ownlang.parser.SourceLoader;
|
||||
import com.annimon.ownlang.parser.Token;
|
||||
@ -34,6 +35,7 @@ public final class Main {
|
||||
options.showAst = true;
|
||||
options.showTokens = true;
|
||||
options.showMeasurements = true;
|
||||
options.lintMode = true;
|
||||
run(SourceLoader.readSource("program.own"), options);
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("OwnLang version " + VERSION + "\n\n" +
|
||||
@ -41,6 +43,7 @@ public final class Main {
|
||||
" options:\n" +
|
||||
" -f, --file [input] Run program file. Required.\n" +
|
||||
" -r, --repl Enter to a REPL mode\n" +
|
||||
" -l, --lint Find bugs in code\n" +
|
||||
" -b, --beautify Beautify source code\n" +
|
||||
" -a, --showast Show AST of program\n" +
|
||||
" -t, --showtokens Show lexical tokens\n" +
|
||||
@ -79,6 +82,11 @@ public final class Main {
|
||||
repl();
|
||||
return;
|
||||
|
||||
case "-l":
|
||||
case "--lint":
|
||||
options.lintMode = true;
|
||||
return;
|
||||
|
||||
case "-f":
|
||||
case "--file":
|
||||
if (i + 1 < args.length) {
|
||||
@ -113,6 +121,7 @@ public final class Main {
|
||||
}
|
||||
|
||||
private static void run(String input, Options options) {
|
||||
options.validate();
|
||||
final TimeMeasurement measurement = new TimeMeasurement();
|
||||
measurement.start("Tokenize time");
|
||||
final List<Token> tokens = Lexer.tokenize(input);
|
||||
@ -134,6 +143,10 @@ public final class Main {
|
||||
System.out.println(parser.getParseErrors());
|
||||
return;
|
||||
}
|
||||
if (options.lintMode) {
|
||||
Linter.lint(program);
|
||||
return;
|
||||
}
|
||||
program.accept(new FunctionAdder());
|
||||
try {
|
||||
measurement.start("Execution time");
|
||||
@ -189,11 +202,21 @@ public final class Main {
|
||||
|
||||
private static class Options {
|
||||
boolean showTokens, showAst, showMeasurements;
|
||||
boolean lintMode;
|
||||
|
||||
public Options() {
|
||||
showTokens = false;
|
||||
showAst = false;
|
||||
showMeasurements = false;
|
||||
lintMode = false;
|
||||
}
|
||||
|
||||
public void validate() {
|
||||
if (lintMode == true) {
|
||||
showTokens = false;
|
||||
showAst = false;
|
||||
showMeasurements = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
39
src/com/annimon/ownlang/parser/Linter.java
Normal file
39
src/com/annimon/ownlang/parser/Linter.java
Normal file
@ -0,0 +1,39 @@
|
||||
package com.annimon.ownlang.parser;
|
||||
|
||||
import com.annimon.ownlang.lib.Functions;
|
||||
import com.annimon.ownlang.lib.Variables;
|
||||
import com.annimon.ownlang.parser.ast.Statement;
|
||||
import com.annimon.ownlang.parser.ast.Visitor;
|
||||
import com.annimon.ownlang.parser.visitors.*;
|
||||
|
||||
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 UseWithNonStringValueValidator(),
|
||||
new AssignValidator(),
|
||||
new DefaultFunctionsOverrideValidator()
|
||||
};
|
||||
resetState();
|
||||
for (Visitor validator : validators) {
|
||||
program.accept(validator);
|
||||
resetState();
|
||||
}
|
||||
System.out.println("Lint validation complete!");
|
||||
}
|
||||
|
||||
private void resetState() {
|
||||
Variables.clear();
|
||||
Functions.getFunctions().clear();
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import com.annimon.ownlang.parser.Parser;
|
||||
import com.annimon.ownlang.parser.SourceLoader;
|
||||
import com.annimon.ownlang.parser.Token;
|
||||
import com.annimon.ownlang.parser.visitors.FunctionAdder;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -22,11 +23,8 @@ public final class IncludeStatement implements Statement {
|
||||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
final String input = SourceLoader.readSource(expression.eval().asString());
|
||||
final List<Token> tokens = Lexer.tokenize(input);
|
||||
final Parser parser = new Parser(tokens);
|
||||
final Statement program = parser.parse();
|
||||
if (!parser.getParseErrors().hasErrors()) {
|
||||
final Statement program = loadProgram(expression.eval().asString());
|
||||
if (program != null) {
|
||||
program.accept(new FunctionAdder());
|
||||
program.execute();
|
||||
}
|
||||
@ -35,6 +33,17 @@ public final class IncludeStatement implements Statement {
|
||||
}
|
||||
}
|
||||
|
||||
public Statement loadProgram(String path) throws IOException {
|
||||
final String input = SourceLoader.readSource(path);
|
||||
final List<Token> tokens = Lexer.tokenize(input);
|
||||
final Parser parser = new Parser(tokens);
|
||||
final Statement program = parser.parse();
|
||||
if (parser.getParseErrors().hasErrors()) {
|
||||
return null;
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.annimon.ownlang.parser.visitors;
|
||||
|
||||
import com.annimon.ownlang.Console;
|
||||
import com.annimon.ownlang.lib.Variables;
|
||||
import com.annimon.ownlang.parser.ast.*;
|
||||
|
||||
@ -7,7 +8,7 @@ import com.annimon.ownlang.parser.ast.*;
|
||||
*
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public final class AssignValidator extends AbstractVisitor {
|
||||
public final class AssignValidator extends LintVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(AssignmentExpression s) {
|
||||
@ -15,8 +16,21 @@ public final class AssignValidator extends AbstractVisitor {
|
||||
if (s.target instanceof VariableExpression) {
|
||||
final String variable = ((VariableExpression) s.target).name;
|
||||
if (Variables.isExists(variable)) {
|
||||
throw new RuntimeException("Cannot assign value to constant");
|
||||
Console.error(String.format(
|
||||
"Warning: variable \"%s\" overrides constant", variable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IncludeStatement st) {
|
||||
super.visit(st);
|
||||
applyVisitor(st, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UseStatement st) {
|
||||
super.visit(st);
|
||||
st.execute();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.annimon.ownlang.parser.visitors;
|
||||
|
||||
import com.annimon.ownlang.Console;
|
||||
import com.annimon.ownlang.lib.Functions;
|
||||
import com.annimon.ownlang.parser.ast.*;
|
||||
|
||||
public final class DefaultFunctionsOverrideValidator extends LintVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(FunctionDefineStatement s) {
|
||||
super.visit(s);
|
||||
if (Functions.isExists(s.name)) {
|
||||
Console.error(String.format(
|
||||
"Warning: function \"%s\" overrides default module function", s.name));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IncludeStatement st) {
|
||||
super.visit(st);
|
||||
applyVisitor(st, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UseStatement st) {
|
||||
super.visit(st);
|
||||
st.execute();
|
||||
}
|
||||
}
|
19
src/com/annimon/ownlang/parser/visitors/LintVisitor.java
Normal file
19
src/com/annimon/ownlang/parser/visitors/LintVisitor.java
Normal file
@ -0,0 +1,19 @@
|
||||
package com.annimon.ownlang.parser.visitors;
|
||||
|
||||
import com.annimon.ownlang.parser.ast.IncludeStatement;
|
||||
import com.annimon.ownlang.parser.ast.Statement;
|
||||
import com.annimon.ownlang.parser.ast.ValueExpression;
|
||||
import com.annimon.ownlang.parser.ast.Visitor;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class LintVisitor extends AbstractVisitor {
|
||||
|
||||
protected void applyVisitor(IncludeStatement s, Visitor visitor) {
|
||||
if (!(s.expression instanceof ValueExpression)) return;
|
||||
try {
|
||||
final Statement program = s.loadProgram(s.expression.eval().asString());
|
||||
program.accept(visitor);
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.annimon.ownlang.parser.visitors;
|
||||
|
||||
import com.annimon.ownlang.Console;
|
||||
import com.annimon.ownlang.lib.Types;
|
||||
import com.annimon.ownlang.lib.Value;
|
||||
import com.annimon.ownlang.parser.ast.*;
|
||||
|
||||
public final class UseWithNonStringValueValidator extends LintVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(IncludeStatement st) {
|
||||
super.visit(st);
|
||||
applyVisitor(st, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(UseStatement st) {
|
||||
super.visit(st);
|
||||
if (!(st.expression instanceof ValueExpression)) {
|
||||
Console.error(String.format(
|
||||
"Warning: `use` with %s, not ValueExpression", st.expression.getClass().getSimpleName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final Value value = ((ValueExpression) st.expression).value;
|
||||
if (value.type() != Types.STRING) {
|
||||
Console.error(String.format(
|
||||
"Warning: `use` with %s - %s, not string", Types.typeToString(value.type()), value.asString()));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user