Skip to content

Commit 138100b

Browse files
committed
Добавлен линтер
1 parent 840f049 commit 138100b

File tree

7 files changed

+171
-7
lines changed

7 files changed

+171
-7
lines changed

src/com/annimon/ownlang/Main.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.annimon.ownlang.exceptions.LexerException;
55
import com.annimon.ownlang.parser.Beautifier;
66
import com.annimon.ownlang.parser.Lexer;
7+
import com.annimon.ownlang.parser.Linter;
78
import com.annimon.ownlang.parser.Parser;
89
import com.annimon.ownlang.parser.SourceLoader;
910
import com.annimon.ownlang.parser.Token;
@@ -34,13 +35,15 @@ public static void main(String[] args) throws IOException {
3435
options.showAst = true;
3536
options.showTokens = true;
3637
options.showMeasurements = true;
38+
options.lintMode = true;
3739
run(SourceLoader.readSource("program.own"), options);
3840
} catch (IOException ioe) {
3941
System.out.println("OwnLang version " + VERSION + "\n\n" +
4042
"Usage: ownlang [options]\n" +
4143
" options:\n" +
4244
" -f, --file [input] Run program file. Required.\n" +
4345
" -r, --repl Enter to a REPL mode\n" +
46+
" -l, --lint Find bugs in code\n" +
4447
" -b, --beautify Beautify source code\n" +
4548
" -a, --showast Show AST of program\n" +
4649
" -t, --showtokens Show lexical tokens\n" +
@@ -78,6 +81,11 @@ public static void main(String[] args) throws IOException {
7881
case "--repl":
7982
repl();
8083
return;
84+
85+
case "-l":
86+
case "--lint":
87+
options.lintMode = true;
88+
return;
8189

8290
case "-f":
8391
case "--file":
@@ -113,6 +121,7 @@ private static void createOwnLangArgs(String[] javaArgs, int index) {
113121
}
114122

115123
private static void run(String input, Options options) {
124+
options.validate();
116125
final TimeMeasurement measurement = new TimeMeasurement();
117126
measurement.start("Tokenize time");
118127
final List<Token> tokens = Lexer.tokenize(input);
@@ -134,6 +143,10 @@ private static void run(String input, Options options) {
134143
System.out.println(parser.getParseErrors());
135144
return;
136145
}
146+
if (options.lintMode) {
147+
Linter.lint(program);
148+
return;
149+
}
137150
program.accept(new FunctionAdder());
138151
try {
139152
measurement.start("Execution time");
@@ -189,11 +202,21 @@ private static void repl() {
189202

190203
private static class Options {
191204
boolean showTokens, showAst, showMeasurements;
205+
boolean lintMode;
192206

193207
public Options() {
194208
showTokens = false;
195209
showAst = false;
196210
showMeasurements = false;
211+
lintMode = false;
212+
}
213+
214+
public void validate() {
215+
if (lintMode == true) {
216+
showTokens = false;
217+
showAst = false;
218+
showMeasurements = false;
219+
}
197220
}
198221
}
199222
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.annimon.ownlang.parser;
2+
3+
import com.annimon.ownlang.lib.Functions;
4+
import com.annimon.ownlang.lib.Variables;
5+
import com.annimon.ownlang.parser.ast.Statement;
6+
import com.annimon.ownlang.parser.ast.Visitor;
7+
import com.annimon.ownlang.parser.visitors.*;
8+
9+
public final class Linter {
10+
11+
public static void lint(Statement program) {
12+
new Linter(program).execute();
13+
}
14+
15+
private final Statement program;
16+
17+
private Linter(Statement program) {
18+
this.program = program;
19+
}
20+
21+
public void execute() {
22+
final Visitor[] validators = new Visitor[] {
23+
new UseWithNonStringValueValidator(),
24+
new AssignValidator(),
25+
new DefaultFunctionsOverrideValidator()
26+
};
27+
resetState();
28+
for (Visitor validator : validators) {
29+
program.accept(validator);
30+
resetState();
31+
}
32+
System.out.println("Lint validation complete!");
33+
}
34+
35+
private void resetState() {
36+
Variables.clear();
37+
Functions.getFunctions().clear();
38+
}
39+
}

src/com/annimon/ownlang/parser/ast/IncludeStatement.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.annimon.ownlang.parser.SourceLoader;
66
import com.annimon.ownlang.parser.Token;
77
import com.annimon.ownlang.parser.visitors.FunctionAdder;
8+
import java.io.IOException;
89
import java.util.List;
910

1011
/**
@@ -22,18 +23,26 @@ public IncludeStatement(Expression expression) {
2223
@Override
2324
public void execute() {
2425
try {
25-
final String input = SourceLoader.readSource(expression.eval().asString());
26-
final List<Token> tokens = Lexer.tokenize(input);
27-
final Parser parser = new Parser(tokens);
28-
final Statement program = parser.parse();
29-
if (!parser.getParseErrors().hasErrors()) {
26+
final Statement program = loadProgram(expression.eval().asString());
27+
if (program != null) {
3028
program.accept(new FunctionAdder());
3129
program.execute();
3230
}
3331
} catch (Exception ex) {
3432
throw new RuntimeException(ex);
3533
}
3634
}
35+
36+
public Statement loadProgram(String path) throws IOException {
37+
final String input = SourceLoader.readSource(path);
38+
final List<Token> tokens = Lexer.tokenize(input);
39+
final Parser parser = new Parser(tokens);
40+
final Statement program = parser.parse();
41+
if (parser.getParseErrors().hasErrors()) {
42+
return null;
43+
}
44+
return program;
45+
}
3746

3847
@Override
3948
public void accept(Visitor visitor) {
Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
11
package com.annimon.ownlang.parser.visitors;
22

3+
import com.annimon.ownlang.Console;
34
import com.annimon.ownlang.lib.Variables;
45
import com.annimon.ownlang.parser.ast.*;
56

67
/**
78
*
89
* @author aNNiMON
910
*/
10-
public final class AssignValidator extends AbstractVisitor {
11+
public final class AssignValidator extends LintVisitor {
1112

1213
@Override
1314
public void visit(AssignmentExpression s) {
1415
super.visit(s);
1516
if (s.target instanceof VariableExpression) {
1617
final String variable = ((VariableExpression) s.target).name;
1718
if (Variables.isExists(variable)) {
18-
throw new RuntimeException("Cannot assign value to constant");
19+
Console.error(String.format(
20+
"Warning: variable \"%s\" overrides constant", variable));
1921
}
2022
}
2123
}
24+
25+
@Override
26+
public void visit(IncludeStatement st) {
27+
super.visit(st);
28+
applyVisitor(st, this);
29+
}
30+
31+
@Override
32+
public void visit(UseStatement st) {
33+
super.visit(st);
34+
st.execute();
35+
}
2236
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.annimon.ownlang.parser.visitors;
2+
3+
import com.annimon.ownlang.Console;
4+
import com.annimon.ownlang.lib.Functions;
5+
import com.annimon.ownlang.parser.ast.*;
6+
7+
public final class DefaultFunctionsOverrideValidator extends LintVisitor {
8+
9+
@Override
10+
public void visit(FunctionDefineStatement s) {
11+
super.visit(s);
12+
if (Functions.isExists(s.name)) {
13+
Console.error(String.format(
14+
"Warning: function \"%s\" overrides default module function", s.name));
15+
}
16+
}
17+
18+
@Override
19+
public void visit(IncludeStatement st) {
20+
super.visit(st);
21+
applyVisitor(st, this);
22+
}
23+
24+
@Override
25+
public void visit(UseStatement st) {
26+
super.visit(st);
27+
st.execute();
28+
}
29+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.annimon.ownlang.parser.visitors;
2+
3+
import com.annimon.ownlang.parser.ast.IncludeStatement;
4+
import com.annimon.ownlang.parser.ast.Statement;
5+
import com.annimon.ownlang.parser.ast.ValueExpression;
6+
import com.annimon.ownlang.parser.ast.Visitor;
7+
import java.io.IOException;
8+
9+
public abstract class LintVisitor extends AbstractVisitor {
10+
11+
protected void applyVisitor(IncludeStatement s, Visitor visitor) {
12+
if (!(s.expression instanceof ValueExpression)) return;
13+
try {
14+
final Statement program = s.loadProgram(s.expression.eval().asString());
15+
program.accept(visitor);
16+
} catch (IOException ex) {
17+
}
18+
}
19+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.annimon.ownlang.parser.visitors;
2+
3+
import com.annimon.ownlang.Console;
4+
import com.annimon.ownlang.lib.Types;
5+
import com.annimon.ownlang.lib.Value;
6+
import com.annimon.ownlang.parser.ast.*;
7+
8+
public final class UseWithNonStringValueValidator extends LintVisitor {
9+
10+
@Override
11+
public void visit(IncludeStatement st) {
12+
super.visit(st);
13+
applyVisitor(st, this);
14+
}
15+
16+
@Override
17+
public void visit(UseStatement st) {
18+
super.visit(st);
19+
if (!(st.expression instanceof ValueExpression)) {
20+
Console.error(String.format(
21+
"Warning: `use` with %s, not ValueExpression", st.expression.getClass().getSimpleName()));
22+
return;
23+
}
24+
25+
final Value value = ((ValueExpression) st.expression).value;
26+
if (value.type() != Types.STRING) {
27+
Console.error(String.format(
28+
"Warning: `use` with %s - %s, not string", Types.typeToString(value.type()), value.asString()));
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)