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.exceptions.LexerException;
|
||||||
import com.annimon.ownlang.parser.Beautifier;
|
import com.annimon.ownlang.parser.Beautifier;
|
||||||
import com.annimon.ownlang.parser.Lexer;
|
import com.annimon.ownlang.parser.Lexer;
|
||||||
|
import com.annimon.ownlang.parser.Linter;
|
||||||
import com.annimon.ownlang.parser.Parser;
|
import com.annimon.ownlang.parser.Parser;
|
||||||
import com.annimon.ownlang.parser.SourceLoader;
|
import com.annimon.ownlang.parser.SourceLoader;
|
||||||
import com.annimon.ownlang.parser.Token;
|
import com.annimon.ownlang.parser.Token;
|
||||||
@ -34,6 +35,7 @@ public final class Main {
|
|||||||
options.showAst = true;
|
options.showAst = true;
|
||||||
options.showTokens = true;
|
options.showTokens = true;
|
||||||
options.showMeasurements = true;
|
options.showMeasurements = true;
|
||||||
|
options.lintMode = true;
|
||||||
run(SourceLoader.readSource("program.own"), options);
|
run(SourceLoader.readSource("program.own"), options);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
System.out.println("OwnLang version " + VERSION + "\n\n" +
|
System.out.println("OwnLang version " + VERSION + "\n\n" +
|
||||||
@ -41,6 +43,7 @@ public final class Main {
|
|||||||
" options:\n" +
|
" options:\n" +
|
||||||
" -f, --file [input] Run program file. Required.\n" +
|
" -f, --file [input] Run program file. Required.\n" +
|
||||||
" -r, --repl Enter to a REPL mode\n" +
|
" -r, --repl Enter to a REPL mode\n" +
|
||||||
|
" -l, --lint Find bugs in code\n" +
|
||||||
" -b, --beautify Beautify source code\n" +
|
" -b, --beautify Beautify source code\n" +
|
||||||
" -a, --showast Show AST of program\n" +
|
" -a, --showast Show AST of program\n" +
|
||||||
" -t, --showtokens Show lexical tokens\n" +
|
" -t, --showtokens Show lexical tokens\n" +
|
||||||
@ -79,6 +82,11 @@ public final class Main {
|
|||||||
repl();
|
repl();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case "-l":
|
||||||
|
case "--lint":
|
||||||
|
options.lintMode = true;
|
||||||
|
return;
|
||||||
|
|
||||||
case "-f":
|
case "-f":
|
||||||
case "--file":
|
case "--file":
|
||||||
if (i + 1 < args.length) {
|
if (i + 1 < args.length) {
|
||||||
@ -113,6 +121,7 @@ public final class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void run(String input, Options options) {
|
private static void run(String input, Options options) {
|
||||||
|
options.validate();
|
||||||
final TimeMeasurement measurement = new TimeMeasurement();
|
final TimeMeasurement measurement = new TimeMeasurement();
|
||||||
measurement.start("Tokenize time");
|
measurement.start("Tokenize time");
|
||||||
final List<Token> tokens = Lexer.tokenize(input);
|
final List<Token> tokens = Lexer.tokenize(input);
|
||||||
@ -134,6 +143,10 @@ public final class Main {
|
|||||||
System.out.println(parser.getParseErrors());
|
System.out.println(parser.getParseErrors());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (options.lintMode) {
|
||||||
|
Linter.lint(program);
|
||||||
|
return;
|
||||||
|
}
|
||||||
program.accept(new FunctionAdder());
|
program.accept(new FunctionAdder());
|
||||||
try {
|
try {
|
||||||
measurement.start("Execution time");
|
measurement.start("Execution time");
|
||||||
@ -189,11 +202,21 @@ public final class Main {
|
|||||||
|
|
||||||
private static class Options {
|
private static class Options {
|
||||||
boolean showTokens, showAst, showMeasurements;
|
boolean showTokens, showAst, showMeasurements;
|
||||||
|
boolean lintMode;
|
||||||
|
|
||||||
public Options() {
|
public Options() {
|
||||||
showTokens = false;
|
showTokens = false;
|
||||||
showAst = false;
|
showAst = false;
|
||||||
showMeasurements = 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.SourceLoader;
|
||||||
import com.annimon.ownlang.parser.Token;
|
import com.annimon.ownlang.parser.Token;
|
||||||
import com.annimon.ownlang.parser.visitors.FunctionAdder;
|
import com.annimon.ownlang.parser.visitors.FunctionAdder;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,11 +23,8 @@ public final class IncludeStatement implements Statement {
|
|||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
try {
|
try {
|
||||||
final String input = SourceLoader.readSource(expression.eval().asString());
|
final Statement program = loadProgram(expression.eval().asString());
|
||||||
final List<Token> tokens = Lexer.tokenize(input);
|
if (program != null) {
|
||||||
final Parser parser = new Parser(tokens);
|
|
||||||
final Statement program = parser.parse();
|
|
||||||
if (!parser.getParseErrors().hasErrors()) {
|
|
||||||
program.accept(new FunctionAdder());
|
program.accept(new FunctionAdder());
|
||||||
program.execute();
|
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
|
@Override
|
||||||
public void accept(Visitor visitor) {
|
public void accept(Visitor visitor) {
|
||||||
visitor.visit(this);
|
visitor.visit(this);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.annimon.ownlang.parser.visitors;
|
package com.annimon.ownlang.parser.visitors;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.Console;
|
||||||
import com.annimon.ownlang.lib.Variables;
|
import com.annimon.ownlang.lib.Variables;
|
||||||
import com.annimon.ownlang.parser.ast.*;
|
import com.annimon.ownlang.parser.ast.*;
|
||||||
|
|
||||||
@ -7,7 +8,7 @@ import com.annimon.ownlang.parser.ast.*;
|
|||||||
*
|
*
|
||||||
* @author aNNiMON
|
* @author aNNiMON
|
||||||
*/
|
*/
|
||||||
public final class AssignValidator extends AbstractVisitor {
|
public final class AssignValidator extends LintVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(AssignmentExpression s) {
|
public void visit(AssignmentExpression s) {
|
||||||
@ -15,8 +16,21 @@ public final class AssignValidator extends AbstractVisitor {
|
|||||||
if (s.target instanceof VariableExpression) {
|
if (s.target instanceof VariableExpression) {
|
||||||
final String variable = ((VariableExpression) s.target).name;
|
final String variable = ((VariableExpression) s.target).name;
|
||||||
if (Variables.isExists(variable)) {
|
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