diff options
Diffstat (limited to 'checker/src/main')
| -rw-r--r-- | checker/src/main/java/lv/enes/orang/checker/Checker.java | 146 | ||||
| -rw-r--r-- | checker/src/main/java/lv/enes/orang/checker/CheckerException.java | 9 | ||||
| -rw-r--r-- | checker/src/main/java/module-info.java | 9 |
3 files changed, 164 insertions, 0 deletions
diff --git a/checker/src/main/java/lv/enes/orang/checker/Checker.java b/checker/src/main/java/lv/enes/orang/checker/Checker.java new file mode 100644 index 0000000..446d3f1 --- /dev/null +++ b/checker/src/main/java/lv/enes/orang/checker/Checker.java | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | package lv.enes.orang.checker; | ||
| 2 | |||
| 3 | import lv.enes.orang.ast.*; | ||
| 4 | import lv.enes.orang.core.ImmutableScope; | ||
| 5 | import lv.enes.orang.core.Scope; | ||
| 6 | |||
| 7 | import java.util.HashMap; | ||
| 8 | import java.util.Map; | ||
| 9 | |||
| 10 | public class Checker implements ExpressionVisitor<Void, CheckerException>, StatementVisitor<Checker, CheckerException> { | ||
| 11 | private final Scope<Boolean> definitions; | ||
| 12 | |||
| 13 | public static <E> Checker of(Map<String, E> builtins) { | ||
| 14 | var boolMap = builtins.keySet() | ||
| 15 | .stream() | ||
| 16 | .<Map<String, Boolean>>collect( | ||
| 17 | HashMap::new, | ||
| 18 | (map, elem) -> map.put(elem, true), | ||
| 19 | Map::putAll | ||
| 20 | ); | ||
| 21 | return new Checker(ImmutableScope.of(boolMap)); | ||
| 22 | } | ||
| 23 | |||
| 24 | private Checker(Scope<Boolean> definitions) { | ||
| 25 | this.definitions = definitions; | ||
| 26 | } | ||
| 27 | |||
| 28 | @Override | ||
| 29 | public Void visitArray(ArrayExpression array) throws CheckerException { | ||
| 30 | for (var expr : array.items()) { | ||
| 31 | visit(expr); | ||
| 32 | } | ||
| 33 | return null; | ||
| 34 | } | ||
| 35 | |||
| 36 | @Override | ||
| 37 | public Void visitBoolean(BooleanLiteral expr) { | ||
| 38 | // Always ok | ||
| 39 | return null; | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public Void visitBinaryExpression(BinaryExpression expr) throws CheckerException { | ||
| 44 | visit(expr.lhs()); | ||
| 45 | visit(expr.rhs()); | ||
| 46 | return null; | ||
| 47 | } | ||
| 48 | |||
| 49 | @Override | ||
| 50 | public Void visitCallExpression(CallExpression expr) throws CheckerException { | ||
| 51 | visit(expr.callee()); | ||
| 52 | visit(expr.arg()); | ||
| 53 | return null; | ||
| 54 | } | ||
| 55 | |||
| 56 | @Override | ||
| 57 | public Checker visitDefinition(Definition def) throws CheckerException { | ||
| 58 | if (definitions.hasDefinition(def.name())) { | ||
| 59 | throw new CheckerException(STR."Top-level definition '\{def.name()}' redefined!"); | ||
| 60 | } | ||
| 61 | return new Checker(ImmutableScope.of(definitions, def.name(), true)); | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | ||
| 65 | public Void visitDoExpression(DoExpression expr) throws CheckerException { | ||
| 66 | for (var child : expr.body()) { | ||
| 67 | visit(child); | ||
| 68 | } | ||
| 69 | return null; | ||
| 70 | } | ||
| 71 | |||
| 72 | @Override | ||
| 73 | public Checker visitExpression(ExpressionStatement expr) throws CheckerException { | ||
| 74 | visit(expr.expr()); | ||
| 75 | return this; | ||
| 76 | } | ||
| 77 | |||
| 78 | @Override | ||
| 79 | public Void visitFnExpression(FnExpression expr) throws CheckerException { | ||
| 80 | var args = new HashMap<String, Boolean>(); | ||
| 81 | for (var arg : expr.args()) { | ||
| 82 | args.put(arg.name, true); | ||
| 83 | } | ||
| 84 | new Checker(ImmutableScope.of(definitions, args)).visit(expr.body()); | ||
| 85 | return null; | ||
| 86 | } | ||
| 87 | |||
| 88 | @Override | ||
| 89 | public Void visitIfElseExpression(IfElseExpression expr) throws CheckerException { | ||
| 90 | visit(expr.condition()); | ||
| 91 | visit(expr.trueBranch()); | ||
| 92 | visit(expr.falseBranch()); | ||
| 93 | return null; | ||
| 94 | } | ||
| 95 | |||
| 96 | @Override | ||
| 97 | public Void visitIntLiteral(IntLiteral expr) { | ||
| 98 | // Always ok | ||
| 99 | return null; | ||
| 100 | } | ||
| 101 | |||
| 102 | @Override | ||
| 103 | public Void visitLetInExpression(LetInExpression expr) throws CheckerException { | ||
| 104 | var locals = new HashMap<String, Boolean>(); | ||
| 105 | for (var local : expr.bindings()) { | ||
| 106 | locals.put(local.name(), true); | ||
| 107 | } | ||
| 108 | new Checker(ImmutableScope.of(definitions, locals)).visit(expr.body()); | ||
| 109 | return null; | ||
| 110 | } | ||
| 111 | |||
| 112 | @Override | ||
| 113 | public Checker visitProgram(Program program) throws CheckerException { | ||
| 114 | var checker = this; | ||
| 115 | for (var stmt : program.statements()) { | ||
| 116 | checker = checker.visit(stmt); | ||
| 117 | } | ||
| 118 | return checker; | ||
| 119 | } | ||
| 120 | |||
| 121 | @Override | ||
| 122 | public Void visitStringLiteral(StringLiteral expr) { | ||
| 123 | // Always ok | ||
| 124 | return null; | ||
| 125 | } | ||
| 126 | |||
| 127 | @Override | ||
| 128 | public Void visitUnaryExpression(UnaryExpression expr) throws CheckerException { | ||
| 129 | visit(expr.child()); | ||
| 130 | return null; | ||
| 131 | } | ||
| 132 | |||
| 133 | @Override | ||
| 134 | public Void visitVariable(VariableExpression expr) throws CheckerException { | ||
| 135 | if (!definitions.hasDefinition(expr.name())) { | ||
| 136 | throw new CheckerException(STR."Variable named '\{expr.name()}' not defined!"); | ||
| 137 | } | ||
| 138 | return null; | ||
| 139 | } | ||
| 140 | |||
| 141 | @Override | ||
| 142 | public Void visitVoidExpression() { | ||
| 143 | // Always ok | ||
| 144 | return null; | ||
| 145 | } | ||
| 146 | } | ||
diff --git a/checker/src/main/java/lv/enes/orang/checker/CheckerException.java b/checker/src/main/java/lv/enes/orang/checker/CheckerException.java new file mode 100644 index 0000000..9354f27 --- /dev/null +++ b/checker/src/main/java/lv/enes/orang/checker/CheckerException.java | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | package lv.enes.orang.checker; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangException; | ||
| 4 | |||
| 5 | public class CheckerException extends OrangException { | ||
| 6 | public CheckerException(String message) { | ||
| 7 | super(message); | ||
| 8 | } | ||
| 9 | } | ||
diff --git a/checker/src/main/java/module-info.java b/checker/src/main/java/module-info.java new file mode 100644 index 0000000..6e5fa47 --- /dev/null +++ b/checker/src/main/java/module-info.java | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | module lv.enes.orang.checker { | ||
| 2 | exports lv.enes.orang.checker; | ||
| 3 | |||
| 4 | requires lv.enes.orang.ast; | ||
| 5 | requires lv.enes.orang.core; | ||
| 6 | requires lv.enes.orang.utils; | ||
| 7 | |||
| 8 | requires static lombok; | ||
| 9 | } \ No newline at end of file | ||