From 6bb8fe3f983cd0a2be76239df2124af633f0e05c Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 31 Mar 2016 12:08:02 +0100 Subject: Add evaluation of arrays to interpreter --- .../java/org/zwobble/couscous/ast/ArrayNode.java | 4 ++ .../zwobble/couscous/interpreter/Evaluator.java | 9 ++-- .../types/IntrinsicInterpreterType.java | 22 +++++----- .../interpreter/values/ArrayInterpreterValue.java | 50 ++++++++++++++++++++++ .../couscous/tests/interpreter/EvaluatorTests.java | 35 ++++++++++++++- .../zwobble/couscous/tests/util/ExtraMatchers.java | 21 +++++++-- 6 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/interpreter/values/ArrayInterpreterValue.java diff --git a/src/main/java/org/zwobble/couscous/ast/ArrayNode.java b/src/main/java/org/zwobble/couscous/ast/ArrayNode.java index ac4c284..78e6869 100644 --- a/src/main/java/org/zwobble/couscous/ast/ArrayNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ArrayNode.java @@ -20,6 +20,10 @@ public class ArrayNode implements ExpressionNode { this.elements = elements; } + public Type getElementType() { + return elementType; + } + public List getElements() { return elements; } diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java index 1082ed1..8c3d355 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java @@ -1,18 +1,19 @@ package org.zwobble.couscous.interpreter; import org.zwobble.couscous.ast.*; -import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.ast.visitors.AssignableExpressionNodeVisitor; import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.interpreter.errors.ConditionMustBeBoolean; import org.zwobble.couscous.interpreter.errors.InvalidCast; import org.zwobble.couscous.interpreter.values.*; +import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Types; import java.util.List; import java.util.stream.Collectors; import static org.zwobble.couscous.types.Types.erasure; +import static org.zwobble.couscous.util.ExtraLists.eagerMap; public class Evaluator implements ExpressionNodeMapper { public static InterpreterValue eval(Environment environment, ExpressionNode expression) { @@ -52,8 +53,10 @@ public class Evaluator implements ExpressionNodeMapper { } @Override - public InterpreterValue visit(ArrayNode arrayNode) { - throw new UnsupportedOperationException(); + public InterpreterValue visit(ArrayNode array) { + return new ArrayInterpreterValue( + array.getElementType(), + eagerMap(array.getElements(), this::eval)); } @Override diff --git a/src/main/java/org/zwobble/couscous/interpreter/types/IntrinsicInterpreterType.java b/src/main/java/org/zwobble/couscous/interpreter/types/IntrinsicInterpreterType.java index d69c855..190908b 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/types/IntrinsicInterpreterType.java +++ b/src/main/java/org/zwobble/couscous/interpreter/types/IntrinsicInterpreterType.java @@ -22,7 +22,7 @@ import static org.zwobble.couscous.util.ExtraLists.list; import static org.zwobble.couscous.util.ExtraSets.set; public class IntrinsicInterpreterType implements InterpreterType { - public static Builder builder(Class interpreterValueType, ScalarType reference) { + public static Builder builder(Class interpreterValueType, Type reference) { return new Builder<>(interpreterValueType, reference); } @@ -40,11 +40,11 @@ public class IntrinsicInterpreterType implements InterpreterType { private final ImmutableMap.Builder methods = ImmutableMap.builder(); private final ImmutableMap.Builder staticMethods = ImmutableMap.builder(); private final Class interpreterValueType; - private final ScalarType name; + private final Type type; - public Builder(Class interpreterValueType, ScalarType name) { + public Builder(Class interpreterValueType, Type type) { this.interpreterValueType = interpreterValueType; - this.name = name; + this.type = type; } public Builder field(String name, ScalarType type) { @@ -88,7 +88,7 @@ public class IntrinsicInterpreterType implements InterpreterType { public InterpreterType build() { return new IntrinsicInterpreterType( - name, + type, fields.build(), constructor, methods.build(), @@ -96,20 +96,20 @@ public class IntrinsicInterpreterType implements InterpreterType { } } - private final ScalarType name; + private final Type type; private final Map fields; private final MethodValue constructor; private final Map methods; private final Map staticMethods; private IntrinsicInterpreterType( - ScalarType name, + Type type, Map fields, MethodValue constructor, Map methods, Map staticMethods) { - this.name = name; + this.type = type; this.fields = fields; this.constructor = constructor; this.methods = methods; @@ -117,8 +117,8 @@ public class IntrinsicInterpreterType implements InterpreterType { } @Override - public ScalarType getType() { - return name; + public Type getType() { + return type; } @Override @@ -164,6 +164,6 @@ public class IntrinsicInterpreterType implements InterpreterType { @Override public String toString() { - return "IntrinsicInterpreterType<" + name + ">"; + return "IntrinsicInterpreterType<" + type + ">"; } } diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/ArrayInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/ArrayInterpreterValue.java new file mode 100644 index 0000000..3a1de58 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/interpreter/values/ArrayInterpreterValue.java @@ -0,0 +1,50 @@ +package org.zwobble.couscous.interpreter.values; + +import org.zwobble.couscous.interpreter.errors.NoSuchField; +import org.zwobble.couscous.interpreter.types.InterpreterType; +import org.zwobble.couscous.interpreter.types.IntrinsicInterpreterType; +import org.zwobble.couscous.types.Type; +import org.zwobble.couscous.types.Types; +import org.zwobble.couscous.values.PrimitiveValue; + +import java.util.List; +import java.util.Optional; + +public class ArrayInterpreterValue implements InterpreterValue { + private final Type elementType; + private final List elements; + + public ArrayInterpreterValue(Type elementType, List elements) { + this.elementType = elementType; + this.elements = elements; + } + + public Type getElementType() { + return elementType; + } + + public List getElements() { + return elements; + } + + @Override + public InterpreterType getType() { + return IntrinsicInterpreterType.builder(ArrayInterpreterValue.class, Types.array(elementType)) + .build(); + } + + @Override + public Optional toPrimitiveValue() { + return Optional.empty(); + } + + @Override + public InterpreterValue getField(String fieldName) { + throw new NoSuchField(fieldName); + } + + @Override + public void setField(String fieldName, InterpreterValue value) { + throw new NoSuchField(fieldName); + } +} diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java index 6112c8e..5f42065 100644 --- a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java +++ b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java @@ -1,25 +1,31 @@ package org.zwobble.couscous.tests.interpreter; import com.google.common.collect.ImmutableMap; +import org.hamcrest.Matcher; import org.junit.Test; import org.zwobble.couscous.ast.*; -import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.interpreter.Environment; import org.zwobble.couscous.interpreter.JavaProject; import org.zwobble.couscous.interpreter.MapBackedProject; import org.zwobble.couscous.interpreter.StackFrameBuilder; import org.zwobble.couscous.interpreter.errors.*; +import org.zwobble.couscous.interpreter.values.ArrayInterpreterValue; import org.zwobble.couscous.interpreter.values.IntegerInterpreterValue; import org.zwobble.couscous.interpreter.values.InterpreterValue; import org.zwobble.couscous.interpreter.values.StringInterpreterValue; import org.zwobble.couscous.tests.BackendEvalTests; +import org.zwobble.couscous.types.ScalarType; import org.zwobble.couscous.types.Types; import org.zwobble.couscous.values.PrimitiveValue; import java.util.List; import java.util.Optional; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.zwobble.couscous.ast.ArrayNode.array; import static org.zwobble.couscous.ast.AssignmentNode.assign; import static org.zwobble.couscous.ast.AssignmentNode.assignStatement; import static org.zwobble.couscous.ast.CastNode.cast; @@ -34,6 +40,7 @@ import static org.zwobble.couscous.interpreter.Evaluator.eval; import static org.zwobble.couscous.interpreter.values.InterpreterValues.value; import static org.zwobble.couscous.tests.TestIds.ANY_ID; import static org.zwobble.couscous.tests.util.ExtraAsserts.assertThrows; +import static org.zwobble.couscous.tests.util.ExtraMatchers.*; import static org.zwobble.couscous.util.ExtraLists.list; public class EvaluatorTests extends BackendEvalTests { @@ -225,7 +232,31 @@ public class EvaluatorTests extends BackendEvalTests { assertEquals(Types.STRING, exception.getExpected()); assertEquals(Types.INT, exception.getActual()); } - + + @Test + public void canEvaluateEmptyArray() { + InterpreterValue array = eval(emptyEnvironment(), array(Types.STRING, list())); + assertThat(array, isArrayInterpreterValue(Types.STRING, list())); + } + + @Test + public void canEvaluateArrayWithElements() { + InterpreterValue array = eval(emptyEnvironment(), array(Types.STRING, list(literal("one"), literal("two")))); + assertThat( + array, + isArrayInterpreterValue(Types.STRING, list( + StringInterpreterValue.of("one"), + StringInterpreterValue.of("two")))); + } + + private Matcher isArrayInterpreterValue(ScalarType elementType, List elements) { + return isInstance( + ArrayInterpreterValue.class, + allOf( + hasFeature("elementType", value -> value.getElementType(), equalTo(elementType)), + hasFeature("elements", value -> value.getElements(), containsExactElements(elements)))); + } + private static Environment emptyEnvironment() { return new Environment( JavaProject.builder().build(), diff --git a/src/test/java/org/zwobble/couscous/tests/util/ExtraMatchers.java b/src/test/java/org/zwobble/couscous/tests/util/ExtraMatchers.java index d79cb05..074e449 100644 --- a/src/test/java/org/zwobble/couscous/tests/util/ExtraMatchers.java +++ b/src/test/java/org/zwobble/couscous/tests/util/ExtraMatchers.java @@ -1,12 +1,13 @@ package org.zwobble.couscous.tests.util; -import org.hamcrest.Description; -import org.hamcrest.DiagnosingMatcher; -import org.hamcrest.FeatureMatcher; -import org.hamcrest.Matcher; +import org.hamcrest.*; +import java.util.List; import java.util.function.Function; +import static org.hamcrest.Matchers.equalTo; +import static org.zwobble.couscous.util.ExtraLists.eagerMap; + public class ExtraMatchers { public static Matcher isInstance(Class type, Matcher downcastMatcher) { return new DiagnosingMatcher() { @@ -51,4 +52,16 @@ public class ExtraMatchers { } }; } + + public static Matcher> containsExactly(List> matchers) { + if (matchers.isEmpty()) { + return Matchers.emptyIterable(); + } else { + return Matchers.contains(matchers); + } + } + + public static Matcher> containsExactElements(List values) { + return containsExactly(eagerMap(values, value -> equalTo(value))); + } } -- cgit v1.2.3