summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--checker/src/main/java/lv/enes/orang/checker/Checker.java39
-rw-r--r--core/src/main/java/lv/enes/orang/core/ImmutableScope.java47
-rw-r--r--core/src/main/java/lv/enes/orang/core/MutableScope.java26
-rw-r--r--core/src/main/java/lv/enes/orang/core/Scope.java31
-rw-r--r--orang/src/main/java/lv/enes/orang/Evaluator.java27
-rw-r--r--orang/src/main/java/lv/enes/orang/value/Function.java5
-rw-r--r--orang/src/main/java/lv/enes/orang/value/PartialFunction.java13
7 files changed, 42 insertions, 146 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
index 14604b8..ee0914f 100644
--- a/checker/src/main/java/lv/enes/orang/checker/Checker.java
+++ b/checker/src/main/java/lv/enes/orang/checker/Checker.java
@@ -1,27 +1,17 @@
1package lv.enes.orang.checker; 1package lv.enes.orang.checker;
2 2
3import lv.enes.orang.ast.*; 3import lv.enes.orang.ast.*;
4import lv.enes.orang.core.ImmutableScope;
5import lv.enes.orang.core.Scope;
6 4
7import java.util.HashMap; 5import java.util.*;
8import java.util.Map;
9 6
10public class Checker implements ExpressionVisitor<Void, CheckerException>, StatementVisitor<Checker, CheckerException> { 7public class Checker implements ExpressionVisitor<Void, CheckerException>, StatementVisitor<Checker, CheckerException> {
11 private final Scope<Boolean> definitions; 8 private final Set<String> definitions;
12 9
13 public static <E> Checker of(Map<String, E> builtins) { 10 public static <E> Checker of(Map<String, E> builtins) {
14 var boolMap = builtins.keySet() 11 return new Checker(Collections.unmodifiableSet(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 } 12 }
23 13
24 private Checker(Scope<Boolean> definitions) { 14 private Checker(Set<String> definitions) {
25 this.definitions = definitions; 15 this.definitions = definitions;
26 } 16 }
27 17
@@ -55,10 +45,13 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State
55 45
56 @Override 46 @Override
57 public Checker visitDefinition(Definition def) throws CheckerException { 47 public Checker visitDefinition(Definition def) throws CheckerException {
58 if (definitions.hasDefinition(def.name())) { 48 if (definitions.contains(def.name())) {
59 throw new CheckerException(STR."Top-level definition '\{def.name()}' redefined!"); 49 throw new CheckerException(STR."Top-level definition '\{def.name()}' redefined!");
60 } 50 }
61 var ch = new Checker(ImmutableScope.of(definitions, def.name(), true)); 51
52 var newScope = new HashSet<>(definitions);
53 newScope.add(def.name());
54 var ch = new Checker(newScope);
62 ch.visit(def.body()); 55 ch.visit(def.body());
63 return ch; 56 return ch;
64 } 57 }
@@ -79,13 +72,13 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State
79 72
80 @Override 73 @Override
81 public Void visitFnExpression(FnExpression expr) throws CheckerException { 74 public Void visitFnExpression(FnExpression expr) throws CheckerException {
82 var args = new HashMap<String, Boolean>(); 75 var newScope = new HashSet<>(definitions);
83 for (var arg : expr.args()) { 76 for (var arg : expr.args()) {
84 if (arg instanceof ArgSpec.Named(String name)) { 77 if (arg instanceof ArgSpec.Named(String name)) {
85 args.put(name, true); 78 newScope.add(name);
86 } 79 }
87 } 80 }
88 new Checker(ImmutableScope.of(definitions, args)).visit(expr.body()); 81 new Checker(newScope).visit(expr.body());
89 return null; 82 return null;
90 } 83 }
91 84
@@ -105,11 +98,11 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State
105 98
106 @Override 99 @Override
107 public Void visitLetInExpression(LetInExpression expr) throws CheckerException { 100 public Void visitLetInExpression(LetInExpression expr) throws CheckerException {
108 var locals = new HashMap<String, Boolean>(); 101 var newScope = new HashSet<>(definitions);
109 for (var local : expr.bindings()) { 102 for (var local : expr.bindings()) {
110 locals.put(local.name(), true); 103 newScope.add(local.name());
111 } 104 }
112 new Checker(ImmutableScope.of(definitions, locals)).visit(expr.body()); 105 new Checker(newScope).visit(expr.body());
113 return null; 106 return null;
114 } 107 }
115 108
@@ -136,7 +129,7 @@ public class Checker implements ExpressionVisitor<Void, CheckerException>, State
136 129
137 @Override 130 @Override
138 public Void visitVariable(VariableExpression expr) throws CheckerException { 131 public Void visitVariable(VariableExpression expr) throws CheckerException {
139 if (!definitions.hasDefinition(expr.name())) { 132 if (!definitions.contains(expr.name())) {
140 throw new CheckerException(STR."Variable named '\{expr.name()}' not defined!"); 133 throw new CheckerException(STR."Variable named '\{expr.name()}' not defined!");
141 } 134 }
142 return null; 135 return null;
diff --git a/core/src/main/java/lv/enes/orang/core/ImmutableScope.java b/core/src/main/java/lv/enes/orang/core/ImmutableScope.java
deleted file mode 100644
index 5120b08..0000000
--- a/core/src/main/java/lv/enes/orang/core/ImmutableScope.java
+++ /dev/null
@@ -1,47 +0,0 @@
1package lv.enes.orang.core;
2
3import java.util.HashMap;
4import java.util.Map;
5
6public class ImmutableScope<E> extends Scope<E> {
7 public static <E> ImmutableScope<E> of(Map<String, E> builtins) {
8 return new ImmutableScope<>(null, new HashMap<>(builtins));
9 }
10
11 public static <E> ImmutableScope<E> of(Scope<E> parent) {
12 if (parent instanceof ImmutableScope<E> imm) {
13 return imm;
14 }
15 return ImmutableScope.of(parent, Map.of());
16 }
17
18 public static <E> ImmutableScope<E> of(Scope<E> parent, String key, E value) {
19 return ImmutableScope.of(parent, Map.of(key, value));
20 }
21
22 public static <E> ImmutableScope<E> of(Scope<E> parent, Map<String, E> definitions) {
23 return new ImmutableScope<>(parent, definitions).maybeFlattened();
24 }
25
26 protected ImmutableScope(Scope<E> parent, Map<String, E> definitions) {
27 super(parent, Map.copyOf(definitions));
28 }
29
30 public ImmutableScope<E> maybeFlattened() {
31 if (depth > MAX_DEPTH) {
32 return flattened();
33 }
34 return this;
35 }
36
37 private ImmutableScope<E> flattened() {
38 if (parent instanceof ImmutableScope<E> immParent) {
39 var flatParent = immParent.flattened();
40 var newDefs = new HashMap<>(flatParent.definitions);
41 newDefs.putAll(definitions);
42 return new ImmutableScope<>(flatParent.parent, newDefs);
43 }
44
45 return this;
46 }
47}
diff --git a/core/src/main/java/lv/enes/orang/core/MutableScope.java b/core/src/main/java/lv/enes/orang/core/MutableScope.java
deleted file mode 100644
index 8d8b455..0000000
--- a/core/src/main/java/lv/enes/orang/core/MutableScope.java
+++ /dev/null
@@ -1,26 +0,0 @@
1package lv.enes.orang.core;
2
3import java.util.HashMap;
4import java.util.Map;
5
6public class MutableScope<E> extends Scope<E> {
7 public static <E> MutableScope<E> of(Map<String, E> builtins) {
8 return new MutableScope<>(null, new HashMap<>(builtins));
9 }
10
11 public static <E> MutableScope<E> of(Scope<E> parent) {
12 return new MutableScope<>(parent, Map.of());
13 }
14
15 public static <E> MutableScope<E> of(Scope<E> parent, String name, E value) {
16 return new MutableScope<>(parent, Map.of(name, value));
17 }
18
19 protected MutableScope(Scope<E> parent, Map<String, E> definitions) {
20 super(parent, new HashMap<>(definitions));
21 }
22
23 public void setDefinition(String key, E value) {
24 definitions.put(key, value);
25 }
26}
diff --git a/core/src/main/java/lv/enes/orang/core/Scope.java b/core/src/main/java/lv/enes/orang/core/Scope.java
deleted file mode 100644
index 0f4d23a..0000000
--- a/core/src/main/java/lv/enes/orang/core/Scope.java
+++ /dev/null
@@ -1,31 +0,0 @@
1package lv.enes.orang.core;
2
3import java.util.Map;
4
5public abstract class Scope<E> {
6 public static final int MAX_DEPTH = 4;
7
8 protected final Scope<E> parent;
9 protected final Map<String, E> definitions;
10 protected final int depth;
11
12 protected Scope(Scope<E> parent, Map<String, E> definitions) {
13 this.parent = parent;
14 this.definitions = definitions;
15 this.depth = parent == null ? 0 : parent.depth + 1;
16 }
17
18 public E getDefinition(String name) throws OrangRuntimeException {
19 if (definitions.containsKey(name)) {
20 return definitions.get(name);
21 } else if (parent != null) {
22 return parent.getDefinition(name);
23 } else {
24 throw new OrangRuntimeException(STR."Value named \{name} is not defined!");
25 }
26 }
27
28 public boolean hasDefinition(String name) {
29 return definitions.containsKey(name) || parent != null && parent.hasDefinition(name);
30 }
31}
diff --git a/orang/src/main/java/lv/enes/orang/Evaluator.java b/orang/src/main/java/lv/enes/orang/Evaluator.java
index 8930f5c..69a256f 100644
--- a/orang/src/main/java/lv/enes/orang/Evaluator.java
+++ b/orang/src/main/java/lv/enes/orang/Evaluator.java
@@ -1,20 +1,20 @@
1package lv.enes.orang; 1package lv.enes.orang;
2 2
3import lv.enes.orang.ast.*; 3import lv.enes.orang.ast.*;
4import lv.enes.orang.core.MutableScope;
5import lv.enes.orang.core.OrangRuntimeException; 4import lv.enes.orang.core.OrangRuntimeException;
6import lv.enes.orang.core.Scope;
7import lv.enes.orang.value.*; 5import lv.enes.orang.value.*;
8 6
9import java.util.ArrayList; 7import java.util.ArrayList;
10import java.util.Collections; 8import java.util.Collections;
9import java.util.HashMap;
10import java.util.Map;
11 11
12public record Evaluator(Scope<Value> scope, Value lastResult) implements ExpressionVisitor<Value, OrangRuntimeException>, StatementVisitor<Evaluator, OrangRuntimeException> { 12public record Evaluator(Map<String, Value> scope, Value lastResult) implements ExpressionVisitor<Value, OrangRuntimeException>, StatementVisitor<Evaluator, OrangRuntimeException> {
13 public Evaluator() { 13 public Evaluator() {
14 this(MutableScope.of(Builtins.BUILTINS)); 14 this(new HashMap<>(Builtins.BUILTINS));
15 } 15 }
16 16
17 public Evaluator(Scope<Value> scope) { 17 public Evaluator(Map<String, Value> scope) {
18 this(scope, Undefined.INSTANCE); 18 this(scope, Undefined.INSTANCE);
19 } 19 }
20 20
@@ -59,9 +59,10 @@ public record Evaluator(Scope<Value> scope, Value lastResult) implements Express
59 59
60 @Override 60 @Override
61 public Evaluator visitDefinition(Definition def) throws OrangRuntimeException { 61 public Evaluator visitDefinition(Definition def) throws OrangRuntimeException {
62 var newScope = MutableScope.of(scope, def.name(), Undefined.INSTANCE); 62 var newScope = new HashMap<>(scope);
63 newScope.put(def.name(), Undefined.INSTANCE);
63 var newEvaluator = new Evaluator(newScope); 64 var newEvaluator = new Evaluator(newScope);
64 newScope.setDefinition(def.name(), newEvaluator.visit(def.body())); 65 newScope.put(def.name(), newEvaluator.visit(def.body()));
65 return newEvaluator; 66 return newEvaluator;
66 } 67 }
67 68
@@ -105,13 +106,13 @@ public record Evaluator(Scope<Value> scope, Value lastResult) implements Express
105 106
106 @Override 107 @Override
107 public Value visitLetInExpression(LetInExpression expr) throws OrangRuntimeException { 108 public Value visitLetInExpression(LetInExpression expr) throws OrangRuntimeException {
108 var newScope = MutableScope.of(scope); 109 var newScope = new HashMap<>(scope);
109 for (var binding : expr.bindings()) { 110 for (var binding : expr.bindings()) {
110 newScope.setDefinition(binding.name(), Undefined.INSTANCE); 111 newScope.put(binding.name(), Undefined.INSTANCE);
111 } 112 }
112 var newEvaluator = new Evaluator(newScope); 113 var newEvaluator = new Evaluator(newScope);
113 for (var binding : expr.bindings()) { 114 for (var binding : expr.bindings()) {
114 newScope.setDefinition(binding.name(), newEvaluator.visit(binding.value())); 115 newScope.put(binding.name(), newEvaluator.visit(binding.value()));
115 } 116 }
116 return newEvaluator.visit(expr.body()); 117 return newEvaluator.visit(expr.body());
117 } 118 }
@@ -142,7 +143,11 @@ public record Evaluator(Scope<Value> scope, Value lastResult) implements Express
142 143
143 @Override 144 @Override
144 public Value visitVariable(VariableExpression expr) throws OrangRuntimeException { 145 public Value visitVariable(VariableExpression expr) throws OrangRuntimeException {
145 return scope.getDefinition(expr.name()); 146 if (scope.containsKey(expr.name())) {
147 return scope.get(expr.name());
148 }
149
150 throw new OrangRuntimeException(STR."Value named \{expr.name()} is not defined!");
146 } 151 }
147 152
148 @Override 153 @Override
diff --git a/orang/src/main/java/lv/enes/orang/value/Function.java b/orang/src/main/java/lv/enes/orang/value/Function.java
index d0500b9..f8c1a7f 100644
--- a/orang/src/main/java/lv/enes/orang/value/Function.java
+++ b/orang/src/main/java/lv/enes/orang/value/Function.java
@@ -3,10 +3,11 @@ package lv.enes.orang.value;
3import lv.enes.orang.ast.ArgSpec; 3import lv.enes.orang.ast.ArgSpec;
4import lv.enes.orang.ast.Expression; 4import lv.enes.orang.ast.Expression;
5import lv.enes.orang.core.OrangRuntimeException; 5import lv.enes.orang.core.OrangRuntimeException;
6import lv.enes.orang.core.Scope;
7import lv.enes.orang.utils.NonEmptyList; 6import lv.enes.orang.utils.NonEmptyList;
8 7
9public record Function(Scope<Value> scope, NonEmptyList<ArgSpec> args, Expression body) implements Value { 8import java.util.Map;
9
10public record Function(Map<String, Value> scope, NonEmptyList<ArgSpec> args, Expression body) implements Value {
10 @Override 11 @Override
11 public String typeName() { 12 public String typeName() {
12 return "Function"; 13 return "Function";
diff --git a/orang/src/main/java/lv/enes/orang/value/PartialFunction.java b/orang/src/main/java/lv/enes/orang/value/PartialFunction.java
index d03bb13..427da76 100644
--- a/orang/src/main/java/lv/enes/orang/value/PartialFunction.java
+++ b/orang/src/main/java/lv/enes/orang/value/PartialFunction.java
@@ -3,17 +3,18 @@ package lv.enes.orang.value;
3import lv.enes.orang.*; 3import lv.enes.orang.*;
4import lv.enes.orang.ast.ArgSpec; 4import lv.enes.orang.ast.ArgSpec;
5import lv.enes.orang.ast.Expression; 5import lv.enes.orang.ast.Expression;
6import lv.enes.orang.core.MutableScope;
7import lv.enes.orang.core.OrangRuntimeException; 6import lv.enes.orang.core.OrangRuntimeException;
8import lv.enes.orang.core.Scope;
9import lv.enes.orang.utils.NonEmptyList; 7import lv.enes.orang.utils.NonEmptyList;
10 8
11public record PartialFunction(Scope<Value> scope, NonEmptyList<ArgSpec> remainingArgs, Expression body) implements Value { 9import java.util.HashMap;
12 public static Value of(Scope<Value> scope, NonEmptyList<ArgSpec> remainingArgs, Expression body, Value param) throws OrangRuntimeException { 10import java.util.Map;
11
12public record PartialFunction(Map<String, Value> scope, NonEmptyList<ArgSpec> remainingArgs, Expression body) implements Value {
13 public static Value of(Map<String, Value> scope, NonEmptyList<ArgSpec> remainingArgs, Expression body, Value param) throws OrangRuntimeException {
13 var spec = remainingArgs.getFirst(); 14 var spec = remainingArgs.getFirst();
14 var newScope = MutableScope.of(scope); 15 var newScope = new HashMap<>(scope);
15 switch (spec.getType()) { 16 switch (spec.getType()) {
16 case NAMED -> newScope.setDefinition(((ArgSpec.Named)spec).name(), param); 17 case NAMED -> newScope.put(((ArgSpec.Named)spec).name(), param);
17 case NOTHING -> { 18 case NOTHING -> {
18 if (!(param instanceof Nothing)) { 19 if (!(param instanceof Nothing)) {
19 throw new OrangRuntimeException(STR."Expected () as a parameter but got \{param.typeName()}"); 20 throw new OrangRuntimeException(STR."Expected () as a parameter but got \{param.typeName()}");