From ce423971e6ea3b4126806eea4b6874daee9f07b2 Mon Sep 17 00:00:00 2001 From: Uko Kokņevičs Date: Mon, 19 Aug 2024 01:15:51 +0800 Subject: Added a checker module. NOTE: I think I should look at guava or commons for some sort of sealable Map instead of the current Scope :D --- .../main/java/lv/enes/orang/checker/Checker.java | 146 +++++++++++++++++++++ .../lv/enes/orang/checker/CheckerException.java | 9 ++ 2 files changed, 155 insertions(+) create mode 100644 checker/src/main/java/lv/enes/orang/checker/Checker.java create mode 100644 checker/src/main/java/lv/enes/orang/checker/CheckerException.java (limited to 'checker/src/main/java/lv') 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 @@ +package lv.enes.orang.checker; + +import lv.enes.orang.ast.*; +import lv.enes.orang.core.ImmutableScope; +import lv.enes.orang.core.Scope; + +import java.util.HashMap; +import java.util.Map; + +public class Checker implements ExpressionVisitor, StatementVisitor { + private final Scope definitions; + + public static Checker of(Map builtins) { + var boolMap = builtins.keySet() + .stream() + .>collect( + HashMap::new, + (map, elem) -> map.put(elem, true), + Map::putAll + ); + return new Checker(ImmutableScope.of(boolMap)); + } + + private Checker(Scope definitions) { + this.definitions = definitions; + } + + @Override + public Void visitArray(ArrayExpression array) throws CheckerException { + for (var expr : array.items()) { + visit(expr); + } + return null; + } + + @Override + public Void visitBoolean(BooleanLiteral expr) { + // Always ok + return null; + } + + @Override + public Void visitBinaryExpression(BinaryExpression expr) throws CheckerException { + visit(expr.lhs()); + visit(expr.rhs()); + return null; + } + + @Override + public Void visitCallExpression(CallExpression expr) throws CheckerException { + visit(expr.callee()); + visit(expr.arg()); + return null; + } + + @Override + public Checker visitDefinition(Definition def) throws CheckerException { + if (definitions.hasDefinition(def.name())) { + throw new CheckerException(STR."Top-level definition '\{def.name()}' redefined!"); + } + return new Checker(ImmutableScope.of(definitions, def.name(), true)); + } + + @Override + public Void visitDoExpression(DoExpression expr) throws CheckerException { + for (var child : expr.body()) { + visit(child); + } + return null; + } + + @Override + public Checker visitExpression(ExpressionStatement expr) throws CheckerException { + visit(expr.expr()); + return this; + } + + @Override + public Void visitFnExpression(FnExpression expr) throws CheckerException { + var args = new HashMap(); + for (var arg : expr.args()) { + args.put(arg.name, true); + } + new Checker(ImmutableScope.of(definitions, args)).visit(expr.body()); + return null; + } + + @Override + public Void visitIfElseExpression(IfElseExpression expr) throws CheckerException { + visit(expr.condition()); + visit(expr.trueBranch()); + visit(expr.falseBranch()); + return null; + } + + @Override + public Void visitIntLiteral(IntLiteral expr) { + // Always ok + return null; + } + + @Override + public Void visitLetInExpression(LetInExpression expr) throws CheckerException { + var locals = new HashMap(); + for (var local : expr.bindings()) { + locals.put(local.name(), true); + } + new Checker(ImmutableScope.of(definitions, locals)).visit(expr.body()); + return null; + } + + @Override + public Checker visitProgram(Program program) throws CheckerException { + var checker = this; + for (var stmt : program.statements()) { + checker = checker.visit(stmt); + } + return checker; + } + + @Override + public Void visitStringLiteral(StringLiteral expr) { + // Always ok + return null; + } + + @Override + public Void visitUnaryExpression(UnaryExpression expr) throws CheckerException { + visit(expr.child()); + return null; + } + + @Override + public Void visitVariable(VariableExpression expr) throws CheckerException { + if (!definitions.hasDefinition(expr.name())) { + throw new CheckerException(STR."Variable named '\{expr.name()}' not defined!"); + } + return null; + } + + @Override + public Void visitVoidExpression() { + // Always ok + return null; + } +} 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 @@ +package lv.enes.orang.checker; + +import lv.enes.orang.core.OrangException; + +public class CheckerException extends OrangException { + public CheckerException(String message) { + super(message); + } +} -- cgit v1.2.3