diff options
| author | 2024-08-22 22:55:13 +0800 | |
|---|---|---|
| committer | 2024-08-22 22:55:13 +0800 | |
| commit | 5f4b45ec7634f2728693e93f862419e495db6979 (patch) | |
| tree | 300f9acaddcfa4a63365bec4952dcb585be0a282 /evaluator | |
| parent | Get rid of that Scope mess (diff) | |
| download | orang-5f4b45ec7634f2728693e93f862419e495db6979.tar.gz orang-5f4b45ec7634f2728693e93f862419e495db6979.tar.xz orang-5f4b45ec7634f2728693e93f862419e495db6979.zip | |
Move Evaluator to a new module
Diffstat (limited to 'evaluator')
14 files changed, 660 insertions, 0 deletions
diff --git a/evaluator/build.gradle.kts b/evaluator/build.gradle.kts new file mode 100644 index 0000000..9172259 --- /dev/null +++ b/evaluator/build.gradle.kts | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | plugins { | ||
| 2 | id("orang.java-conventions") | ||
| 3 | } | ||
| 4 | |||
| 5 | dependencies { | ||
| 6 | implementation(project(":ast")) | ||
| 7 | implementation(project(":core")) | ||
| 8 | implementation(project(":utils")) | ||
| 9 | } \ No newline at end of file | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java new file mode 100644 index 0000000..4c76eff --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Array.java | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 4 | |||
| 5 | import java.util.ArrayList; | ||
| 6 | import java.util.Collections; | ||
| 7 | import java.util.List; | ||
| 8 | |||
| 9 | public record Array(List<Value> items) implements Value { | ||
| 10 | @Override | ||
| 11 | public String typeName() { | ||
| 12 | return "Array"; | ||
| 13 | } | ||
| 14 | |||
| 15 | @Override | ||
| 16 | public String stringify() { | ||
| 17 | if (items.isEmpty()) { | ||
| 18 | return "[]"; | ||
| 19 | } | ||
| 20 | var sb = new StringBuilder("["); | ||
| 21 | sb.append(items.getFirst().stringify()); | ||
| 22 | for (int i = 1; i < items.size(); i++) { | ||
| 23 | sb.append(", ").append(items.get(i).stringify()); | ||
| 24 | } | ||
| 25 | sb.append("]"); | ||
| 26 | return sb.toString(); | ||
| 27 | } | ||
| 28 | |||
| 29 | @Override | ||
| 30 | public String display() { | ||
| 31 | if (items.isEmpty()) { | ||
| 32 | return ""; | ||
| 33 | } | ||
| 34 | var sb = new StringBuilder(); | ||
| 35 | sb.append(items.getFirst().display()); | ||
| 36 | for (var i = 1; i < items.size(); i++) { | ||
| 37 | sb.append(" ").append(items.get(i).display()); | ||
| 38 | } | ||
| 39 | return sb.toString(); | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public Value add(Value rhs) throws OrangRuntimeException { | ||
| 44 | if (rhs instanceof Array(var rhsi)) { | ||
| 45 | var newItems = new ArrayList<>(this.items); | ||
| 46 | newItems.addAll(rhsi); | ||
| 47 | return new Array(Collections.unmodifiableList(newItems)); | ||
| 48 | } else { | ||
| 49 | throw new OrangRuntimeException(STR."add not implemented for Array and \{rhs.typeName()}"); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | @Override | ||
| 54 | public Value multiply(Value rhs) throws OrangRuntimeException { | ||
| 55 | if (rhs instanceof OrangInteger(var repeat)) { | ||
| 56 | var newItems = new ArrayList<Value>(items.size() * repeat); | ||
| 57 | for (var i = 0; i < repeat; i++) { | ||
| 58 | newItems.addAll(items); | ||
| 59 | } | ||
| 60 | return new Array(Collections.unmodifiableList(newItems)); | ||
| 61 | } else { | ||
| 62 | throw new OrangRuntimeException(STR."multiply not implemented for Array and \{rhs.typeName()}"); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/BuiltinFunction.java b/evaluator/src/main/java/lv/enes/orang/evaluator/BuiltinFunction.java new file mode 100644 index 0000000..1f37280 --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/BuiltinFunction.java | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 4 | |||
| 5 | import java.util.List; | ||
| 6 | |||
| 7 | public record BuiltinFunction(int argCount, Impl impl) implements Value { | ||
| 8 | @FunctionalInterface | ||
| 9 | public interface Impl { | ||
| 10 | Value apply(List<Value> args) throws OrangRuntimeException; | ||
| 11 | } | ||
| 12 | |||
| 13 | @Override | ||
| 14 | public String typeName() { | ||
| 15 | return "BuiltinFunction"; | ||
| 16 | } | ||
| 17 | |||
| 18 | @Override | ||
| 19 | public String stringify() { | ||
| 20 | return "#builtinFunction"; | ||
| 21 | } | ||
| 22 | |||
| 23 | @Override | ||
| 24 | public Value call(Value param) throws OrangRuntimeException { | ||
| 25 | if (argCount == 1) { | ||
| 26 | return impl.apply(List.of(param)); | ||
| 27 | } else { | ||
| 28 | return new PartialBuiltinFunction(argCount, List.of(param), impl); | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java new file mode 100644 index 0000000..6925bac --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Evaluator.java | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.ast.*; | ||
| 4 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 5 | |||
| 6 | import java.util.ArrayList; | ||
| 7 | import java.util.Collections; | ||
| 8 | import java.util.HashMap; | ||
| 9 | import java.util.Map; | ||
| 10 | |||
| 11 | public record Evaluator(Map<String, Value> scope, Value lastResult) implements ExpressionVisitor<Value, OrangRuntimeException>, StatementVisitor<Evaluator, OrangRuntimeException> { | ||
| 12 | public Evaluator(Map<String, Value> scope) { | ||
| 13 | this(scope, Undefined.INSTANCE); | ||
| 14 | } | ||
| 15 | |||
| 16 | @Override | ||
| 17 | public Value visitArray(ArrayExpression expr) throws OrangRuntimeException { | ||
| 18 | var values = new ArrayList<Value>(expr.items().size()); | ||
| 19 | for (var item : expr.items()) { | ||
| 20 | values.add(visit(item)); | ||
| 21 | } | ||
| 22 | values.trimToSize(); | ||
| 23 | return new Array(Collections.unmodifiableList(values)); | ||
| 24 | } | ||
| 25 | |||
| 26 | @Override | ||
| 27 | public Value visitBinaryExpression(BinaryExpression expr) throws OrangRuntimeException { | ||
| 28 | var lhs = visit(expr.lhs()); | ||
| 29 | var rhs = visit(expr.rhs()); | ||
| 30 | return switch (expr.operator()) { | ||
| 31 | case EQUALS -> lhs.orangEquals(rhs); | ||
| 32 | case NOT_EQUALS -> lhs.notEquals(rhs); | ||
| 33 | case GT -> lhs.greaterThan(rhs); | ||
| 34 | case GTE -> lhs.greaterThanOrEqual(rhs); | ||
| 35 | case LT -> lhs.lessThan(rhs); | ||
| 36 | case LTE -> lhs.lessThanOrEqual(rhs); | ||
| 37 | case ADD -> lhs.add(rhs); | ||
| 38 | case SUBTRACT -> lhs.subtract(rhs); | ||
| 39 | case MULTIPLY -> lhs.multiply(rhs); | ||
| 40 | case DIVIDE -> lhs.divide(rhs); | ||
| 41 | }; | ||
| 42 | } | ||
| 43 | |||
| 44 | @Override | ||
| 45 | public Value visitBoolean(BooleanLiteral expr) { | ||
| 46 | return OrangBoolean.of(expr.value()); | ||
| 47 | } | ||
| 48 | |||
| 49 | @Override | ||
| 50 | public Value visitCallExpression(CallExpression expr) throws OrangRuntimeException { | ||
| 51 | var arg = visit(expr.arg()); | ||
| 52 | return visit(expr.callee()).call(arg); | ||
| 53 | } | ||
| 54 | |||
| 55 | @Override | ||
| 56 | public Evaluator visitDefinition(Definition def) throws OrangRuntimeException { | ||
| 57 | var newScope = new HashMap<>(scope); | ||
| 58 | newScope.put(def.name(), Undefined.INSTANCE); | ||
| 59 | var newEvaluator = new Evaluator(newScope); | ||
| 60 | newScope.put(def.name(), newEvaluator.visit(def.body())); | ||
| 61 | return newEvaluator; | ||
| 62 | } | ||
| 63 | |||
| 64 | @Override | ||
| 65 | public Value visitDoExpression(DoExpression expr) throws OrangRuntimeException { | ||
| 66 | Value value = Undefined.INSTANCE; | ||
| 67 | for (var child : expr.body()) { | ||
| 68 | value = visit(child); | ||
| 69 | } | ||
| 70 | return value; | ||
| 71 | } | ||
| 72 | |||
| 73 | @Override | ||
| 74 | public Evaluator visitExpression(ExpressionStatement expr) throws OrangRuntimeException { | ||
| 75 | return new Evaluator(this.scope(), visit(expr.expr())); | ||
| 76 | } | ||
| 77 | |||
| 78 | @Override | ||
| 79 | public Value visitFnExpression(FnExpression expr) { | ||
| 80 | return new Function(this.scope, expr.args(), expr.body()); | ||
| 81 | } | ||
| 82 | |||
| 83 | @Override | ||
| 84 | public Value visitIfElseExpression(IfElseExpression expr) throws OrangRuntimeException { | ||
| 85 | var cond = visit(expr.condition()); | ||
| 86 | if (cond instanceof OrangBoolean value) { | ||
| 87 | if (value.value()) { | ||
| 88 | return visit(expr.trueBranch()); | ||
| 89 | } else { | ||
| 90 | return visit(expr.falseBranch()); | ||
| 91 | } | ||
| 92 | } else { | ||
| 93 | throw new OrangRuntimeException(STR."Condition in an if-else statement should be a boolean not a \{cond.typeName()}"); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | @Override | ||
| 98 | public Value visitIntLiteral(IntLiteral expr) { | ||
| 99 | return new OrangInteger(expr.value()); | ||
| 100 | } | ||
| 101 | |||
| 102 | @Override | ||
| 103 | public Value visitLetInExpression(LetInExpression expr) throws OrangRuntimeException { | ||
| 104 | var newScope = new HashMap<>(scope); | ||
| 105 | for (var binding : expr.bindings()) { | ||
| 106 | newScope.put(binding.name(), Undefined.INSTANCE); | ||
| 107 | } | ||
| 108 | var newEvaluator = new Evaluator(newScope); | ||
| 109 | for (var binding : expr.bindings()) { | ||
| 110 | newScope.put(binding.name(), newEvaluator.visit(binding.value())); | ||
| 111 | } | ||
| 112 | return newEvaluator.visit(expr.body()); | ||
| 113 | } | ||
| 114 | |||
| 115 | @Override | ||
| 116 | public Evaluator visitProgram(Program prog) throws OrangRuntimeException { | ||
| 117 | var evaluator = this; | ||
| 118 | for (var statement : prog.statements()) { | ||
| 119 | evaluator = evaluator.visit(statement); | ||
| 120 | } | ||
| 121 | return evaluator; | ||
| 122 | } | ||
| 123 | |||
| 124 | @Override | ||
| 125 | public Value visitUnaryExpression(UnaryExpression expr) throws OrangRuntimeException { | ||
| 126 | var child = visit(expr.child()); | ||
| 127 | return switch (expr.operator()) { | ||
| 128 | case PLUS -> child.plus(); | ||
| 129 | case NEGATE -> child.negate(); | ||
| 130 | case NOT -> child.not(); | ||
| 131 | }; | ||
| 132 | } | ||
| 133 | |||
| 134 | @Override | ||
| 135 | public Value visitStringLiteral(StringLiteral expr) { | ||
| 136 | return new OrangString(expr.value()); | ||
| 137 | } | ||
| 138 | |||
| 139 | @Override | ||
| 140 | public Value visitVariable(VariableExpression expr) throws OrangRuntimeException { | ||
| 141 | if (scope.containsKey(expr.name())) { | ||
| 142 | return scope.get(expr.name()); | ||
| 143 | } | ||
| 144 | |||
| 145 | throw new OrangRuntimeException(STR."Value named \{expr.name()} is not defined!"); | ||
| 146 | } | ||
| 147 | |||
| 148 | @Override | ||
| 149 | public Value visitVoidExpression() { | ||
| 150 | return Nothing.INSTANCE; | ||
| 151 | } | ||
| 152 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Function.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Function.java new file mode 100644 index 0000000..33102fb --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Function.java | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.ast.ArgSpec; | ||
| 4 | import lv.enes.orang.ast.Expression; | ||
| 5 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 6 | import lv.enes.orang.utils.NonEmptyList; | ||
| 7 | |||
| 8 | import java.util.Map; | ||
| 9 | |||
| 10 | public record Function(Map<String, Value> scope, NonEmptyList<ArgSpec> args, Expression body) implements Value { | ||
| 11 | @Override | ||
| 12 | public String typeName() { | ||
| 13 | return "Function"; | ||
| 14 | } | ||
| 15 | |||
| 16 | @Override | ||
| 17 | public String stringify() { | ||
| 18 | return "#function"; | ||
| 19 | } | ||
| 20 | |||
| 21 | @Override | ||
| 22 | public Value call(Value param) throws OrangRuntimeException { | ||
| 23 | return PartialFunction.of(scope, args, body, param); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Nothing.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Nothing.java new file mode 100644 index 0000000..c971649 --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Nothing.java | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lombok.EqualsAndHashCode; | ||
| 4 | |||
| 5 | @EqualsAndHashCode | ||
| 6 | public final class Nothing implements Value { | ||
| 7 | public static final Nothing INSTANCE = new Nothing(); | ||
| 8 | |||
| 9 | private Nothing() { | ||
| 10 | } | ||
| 11 | |||
| 12 | @Override | ||
| 13 | public String typeName() { | ||
| 14 | return "Nothing"; | ||
| 15 | } | ||
| 16 | |||
| 17 | @Override | ||
| 18 | public String stringify() { | ||
| 19 | return "()"; | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangBoolean.java b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangBoolean.java new file mode 100644 index 0000000..3564a9c --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangBoolean.java | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lombok.EqualsAndHashCode; | ||
| 4 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 5 | |||
| 6 | @EqualsAndHashCode | ||
| 7 | public final class OrangBoolean implements Value { | ||
| 8 | public final static OrangBoolean TRUE = new OrangBoolean(true); | ||
| 9 | public final static OrangBoolean FALSE = new OrangBoolean(false); | ||
| 10 | |||
| 11 | private final boolean value; | ||
| 12 | |||
| 13 | private OrangBoolean(boolean value) { | ||
| 14 | this.value = value; | ||
| 15 | } | ||
| 16 | |||
| 17 | public static OrangBoolean of(boolean value) { | ||
| 18 | if (value) { | ||
| 19 | return TRUE; | ||
| 20 | } | ||
| 21 | return FALSE; | ||
| 22 | } | ||
| 23 | |||
| 24 | public boolean value() { | ||
| 25 | return value; | ||
| 26 | } | ||
| 27 | |||
| 28 | @Override | ||
| 29 | public String typeName() { | ||
| 30 | return "Boolean"; | ||
| 31 | } | ||
| 32 | |||
| 33 | @Override | ||
| 34 | public String stringify() { | ||
| 35 | if (value) { | ||
| 36 | return "true"; | ||
| 37 | } else { | ||
| 38 | return "false"; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | @Override | ||
| 43 | public OrangBoolean not() { | ||
| 44 | return new OrangBoolean(!value); | ||
| 45 | } | ||
| 46 | |||
| 47 | @Override | ||
| 48 | public OrangBoolean or(Value rhs) throws OrangRuntimeException { | ||
| 49 | if (rhs instanceof OrangBoolean rhsb) { | ||
| 50 | return new OrangBoolean(value || rhsb.value); | ||
| 51 | } else { | ||
| 52 | throw new OrangRuntimeException(STR."or is not implemented for Boolean and \{rhs.typeName()}"); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangInteger.java b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangInteger.java new file mode 100644 index 0000000..90de0b5 --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangInteger.java | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 4 | |||
| 5 | public record OrangInteger(int value) implements Value { | ||
| 6 | @Override | ||
| 7 | public String typeName() { | ||
| 8 | return "Integer"; | ||
| 9 | } | ||
| 10 | |||
| 11 | @Override | ||
| 12 | public String stringify() { | ||
| 13 | return Integer.toString(value); | ||
| 14 | } | ||
| 15 | |||
| 16 | @Override | ||
| 17 | public OrangInteger negate() { | ||
| 18 | return new OrangInteger(-value); | ||
| 19 | } | ||
| 20 | |||
| 21 | @Override | ||
| 22 | public OrangInteger plus() { | ||
| 23 | return this; | ||
| 24 | } | ||
| 25 | |||
| 26 | @Override | ||
| 27 | public OrangInteger add(Value rhs) throws OrangRuntimeException { | ||
| 28 | if (rhs instanceof OrangInteger(int rhsi)) { | ||
| 29 | return new OrangInteger(value + rhsi); | ||
| 30 | } else { | ||
| 31 | throw new OrangRuntimeException(STR."add is not implemented for Integer and \{rhs.typeName()}"); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public OrangInteger divide(Value rhs) throws OrangRuntimeException { | ||
| 37 | if (rhs instanceof OrangInteger(int rhsi)) { | ||
| 38 | return new OrangInteger(value / rhsi); | ||
| 39 | } else { | ||
| 40 | throw new OrangRuntimeException(STR."divide is not implemented for Integer and \{rhs.typeName()}"); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | @Override | ||
| 45 | public OrangInteger multiply(Value rhs) throws OrangRuntimeException { | ||
| 46 | if (rhs instanceof OrangInteger(int rhsi)) { | ||
| 47 | return new OrangInteger(value * rhsi); | ||
| 48 | } else { | ||
| 49 | throw new OrangRuntimeException(STR."multiply is not implemented for Integer and \{rhs.typeName()}"); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | @Override | ||
| 54 | public OrangInteger subtract(Value rhs) throws OrangRuntimeException { | ||
| 55 | if (rhs instanceof OrangInteger(int rhsi)) { | ||
| 56 | return new OrangInteger(value - rhsi); | ||
| 57 | } else { | ||
| 58 | throw new OrangRuntimeException(STR."subtract is not implemented for Integer and \{rhs.typeName()}"); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | @Override | ||
| 63 | public OrangBoolean greaterThan(Value rhs) throws OrangRuntimeException { | ||
| 64 | if (rhs instanceof OrangInteger(int rhsi)) { | ||
| 65 | return OrangBoolean.of(value > rhsi); | ||
| 66 | } else { | ||
| 67 | throw new OrangRuntimeException(STR."greaterThan is not implemented for Integer and \{rhs.typeName()}"); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/OrangString.java b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangString.java new file mode 100644 index 0000000..5c38842 --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/OrangString.java | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 4 | |||
| 5 | public record OrangString(String value) implements Value { | ||
| 6 | @Override | ||
| 7 | public String typeName() { | ||
| 8 | return "String"; | ||
| 9 | } | ||
| 10 | |||
| 11 | @Override | ||
| 12 | public String stringify() { | ||
| 13 | var sb = new StringBuilder("\""); | ||
| 14 | var cps = value.codePoints().iterator(); | ||
| 15 | while (cps.hasNext()) { | ||
| 16 | var cp = cps.next(); | ||
| 17 | if (cp == '"') { | ||
| 18 | sb.append("\\\""); | ||
| 19 | } else { | ||
| 20 | sb.appendCodePoint(cp); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | sb.append('"'); | ||
| 24 | return sb.toString(); | ||
| 25 | } | ||
| 26 | |||
| 27 | @Override | ||
| 28 | public String display() { | ||
| 29 | return value; | ||
| 30 | } | ||
| 31 | |||
| 32 | @Override | ||
| 33 | public OrangString add(Value rhs) throws OrangRuntimeException { | ||
| 34 | if (rhs instanceof OrangString(String rhss)) { | ||
| 35 | return new OrangString(value + rhss); | ||
| 36 | } else { | ||
| 37 | throw new OrangRuntimeException(STR."add is not implemented for Integer and \{rhs.typeName()}"); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | public Value multiply(Value rhs) throws OrangRuntimeException { | ||
| 43 | if (rhs instanceof OrangInteger(var repeat)) { | ||
| 44 | return new OrangString(value.repeat(Math.max(0, repeat))); | ||
| 45 | } else { | ||
| 46 | throw new OrangRuntimeException(STR."multiply not implemented for Array and \{rhs.typeName()}"); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/PartialBuiltinFunction.java b/evaluator/src/main/java/lv/enes/orang/evaluator/PartialBuiltinFunction.java new file mode 100644 index 0000000..f1e8cc6 --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/PartialBuiltinFunction.java | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 4 | |||
| 5 | import java.util.ArrayList; | ||
| 6 | import java.util.Collections; | ||
| 7 | import java.util.List; | ||
| 8 | |||
| 9 | public record PartialBuiltinFunction(int argCount, List<Value> params, BuiltinFunction.Impl impl) implements Value { | ||
| 10 | @Override | ||
| 11 | public String typeName() { | ||
| 12 | return "BuiltinFunction"; | ||
| 13 | } | ||
| 14 | |||
| 15 | @Override | ||
| 16 | public String stringify() { | ||
| 17 | return "#builtinFunction"; | ||
| 18 | } | ||
| 19 | |||
| 20 | @Override | ||
| 21 | public Value call(Value param) throws OrangRuntimeException { | ||
| 22 | List<Value> newParams = new ArrayList<>(params); | ||
| 23 | newParams.add(param); | ||
| 24 | newParams = Collections.unmodifiableList(newParams); | ||
| 25 | |||
| 26 | if (newParams.size() == argCount) { | ||
| 27 | return impl.apply(newParams); | ||
| 28 | } else { | ||
| 29 | return new PartialBuiltinFunction(argCount, newParams, impl); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/PartialFunction.java b/evaluator/src/main/java/lv/enes/orang/evaluator/PartialFunction.java new file mode 100644 index 0000000..d0125f6 --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/PartialFunction.java | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.ast.ArgSpec; | ||
| 4 | import lv.enes.orang.ast.Expression; | ||
| 5 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 6 | import lv.enes.orang.utils.NonEmptyList; | ||
| 7 | |||
| 8 | import java.util.HashMap; | ||
| 9 | import java.util.Map; | ||
| 10 | |||
| 11 | public record PartialFunction(Map<String, Value> scope, NonEmptyList<ArgSpec> remainingArgs, Expression body) implements Value { | ||
| 12 | public static Value of(Map<String, Value> scope, NonEmptyList<ArgSpec> remainingArgs, Expression body, Value param) throws OrangRuntimeException { | ||
| 13 | var spec = remainingArgs.getFirst(); | ||
| 14 | var newScope = new HashMap<>(scope); | ||
| 15 | switch (spec.getType()) { | ||
| 16 | case NAMED -> newScope.put(((ArgSpec.Named)spec).name(), param); | ||
| 17 | case NOTHING -> { | ||
| 18 | if (!(param instanceof Nothing)) { | ||
| 19 | throw new OrangRuntimeException(STR."Expected () as a parameter but got \{param.typeName()}"); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | if (remainingArgs.size() == 1) { | ||
| 25 | return new Evaluator(newScope).visit(body); | ||
| 26 | } else { | ||
| 27 | return new PartialFunction(newScope, new NonEmptyList<>(remainingArgs.subList(1, remainingArgs.size())), body); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | @Override | ||
| 32 | public String typeName() { | ||
| 33 | return "Function"; | ||
| 34 | } | ||
| 35 | |||
| 36 | @Override | ||
| 37 | public String stringify() { | ||
| 38 | return "#function"; | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | public Value call(Value param) throws OrangRuntimeException { | ||
| 43 | return PartialFunction.of(scope, remainingArgs, body, param); | ||
| 44 | } | ||
| 45 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Undefined.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Undefined.java new file mode 100644 index 0000000..2aec74b --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Undefined.java | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lombok.EqualsAndHashCode; | ||
| 4 | |||
| 5 | @EqualsAndHashCode | ||
| 6 | public final class Undefined implements Value { | ||
| 7 | public static final Undefined INSTANCE = new Undefined(); | ||
| 8 | |||
| 9 | private Undefined() { | ||
| 10 | } | ||
| 11 | |||
| 12 | @Override | ||
| 13 | public String typeName() { | ||
| 14 | return "Undefined"; | ||
| 15 | } | ||
| 16 | |||
| 17 | @Override | ||
| 18 | public String stringify() { | ||
| 19 | return "#undefined"; | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java b/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java new file mode 100644 index 0000000..d8c8b9c --- /dev/null +++ b/evaluator/src/main/java/lv/enes/orang/evaluator/Value.java | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | package lv.enes.orang.evaluator; | ||
| 2 | |||
| 3 | import lv.enes.orang.core.OrangRuntimeException; | ||
| 4 | |||
| 5 | public sealed interface Value | ||
| 6 | permits Array, BuiltinFunction, Function, Nothing, OrangBoolean, OrangInteger, OrangString, PartialBuiltinFunction, PartialFunction, Undefined { | ||
| 7 | String typeName(); | ||
| 8 | String stringify(); | ||
| 9 | |||
| 10 | default String display() { | ||
| 11 | return stringify(); | ||
| 12 | } | ||
| 13 | |||
| 14 | |||
| 15 | default Value negate() throws OrangRuntimeException { | ||
| 16 | throw new OrangRuntimeException(STR."negate is not implemented for \{typeName()}"); | ||
| 17 | } | ||
| 18 | |||
| 19 | default Value not() throws OrangRuntimeException { | ||
| 20 | throw new OrangRuntimeException(STR."not is not implemented for \{typeName()}"); | ||
| 21 | } | ||
| 22 | |||
| 23 | default Value plus() throws OrangRuntimeException { | ||
| 24 | throw new OrangRuntimeException(STR."plus is not implemented for \{typeName()}"); | ||
| 25 | } | ||
| 26 | |||
| 27 | |||
| 28 | default Value add(Value rhs) throws OrangRuntimeException { | ||
| 29 | throw new OrangRuntimeException(STR."add is not implemented for \{typeName()} and \{rhs.typeName()}"); | ||
| 30 | } | ||
| 31 | |||
| 32 | default Value call(Value rhs) throws OrangRuntimeException { | ||
| 33 | throw new OrangRuntimeException(STR."call is not implemented for \{typeName()} and \{rhs.typeName()}"); | ||
| 34 | } | ||
| 35 | |||
| 36 | default Value divide(Value rhs) throws OrangRuntimeException { | ||
| 37 | throw new OrangRuntimeException(STR."divide is not implemented for \{typeName()} and \{rhs.typeName()}"); | ||
| 38 | } | ||
| 39 | |||
| 40 | default Value multiply(Value rhs) throws OrangRuntimeException { | ||
| 41 | throw new OrangRuntimeException(STR."multiply is not implemented for \{typeName()} and \{rhs.typeName()}"); | ||
| 42 | } | ||
| 43 | |||
| 44 | default Value or(Value rhs) throws OrangRuntimeException { | ||
| 45 | throw new OrangRuntimeException(STR."or is not implemented for \{typeName()} and \{rhs.typeName()}"); | ||
| 46 | } | ||
| 47 | |||
| 48 | default Value subtract(Value rhs) throws OrangRuntimeException { | ||
| 49 | throw new OrangRuntimeException(STR."subtract is not implemented for \{typeName()} and \{rhs.typeName()}"); | ||
| 50 | } | ||
| 51 | |||
| 52 | |||
| 53 | default OrangBoolean greaterThan(Value rhs) throws OrangRuntimeException { | ||
| 54 | throw new OrangRuntimeException(STR."greater than is not implemented for \{typeName()} and \{rhs.typeName()}"); | ||
| 55 | } | ||
| 56 | |||
| 57 | default OrangBoolean greaterThanOrEqual(Value rhs) throws OrangRuntimeException { | ||
| 58 | return greaterThan(rhs).or(orangEquals(rhs)); | ||
| 59 | } | ||
| 60 | |||
| 61 | default OrangBoolean lessThan(Value rhs) throws OrangRuntimeException { | ||
| 62 | return greaterThanOrEqual(rhs).not(); | ||
| 63 | } | ||
| 64 | |||
| 65 | default OrangBoolean lessThanOrEqual(Value rhs) throws OrangRuntimeException { | ||
| 66 | return greaterThan(rhs).not(); | ||
| 67 | } | ||
| 68 | |||
| 69 | default OrangBoolean orangEquals(Value rhs) { | ||
| 70 | return OrangBoolean.of(this.equals(rhs)); | ||
| 71 | } | ||
| 72 | |||
| 73 | default OrangBoolean notEquals(Value rhs) { | ||
| 74 | return orangEquals(rhs).not(); | ||
| 75 | } | ||
| 76 | } | ||
diff --git a/evaluator/src/main/java/module-info.java b/evaluator/src/main/java/module-info.java new file mode 100644 index 0000000..4cb992c --- /dev/null +++ b/evaluator/src/main/java/module-info.java | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | module lv.enes.orang.evaluator { | ||
| 2 | exports lv.enes.orang.evaluator; | ||
| 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 | ||