aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2015-10-01 21:04:37 +0100
committerMichael Williamson <mike@zwobble.org>2015-10-01 21:04:37 +0100
commit8187130b8a8aabbcf983e530413d6969141d6da2 (patch)
treed72dcb499d08812e7391b95cdf780dff054b3a3c /src
parent36f3fdbcad95dcc5bfec785fffca4f42b24d1307 (diff)
Allow static method calls
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java25
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java2
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Environment.java10
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Evaluator.java17
-rw-r--r--src/main/java/org/zwobble/couscous/values/BooleanValue.java2
-rw-r--r--src/main/java/org/zwobble/couscous/values/Callable.java7
-rw-r--r--src/main/java/org/zwobble/couscous/values/ConcreteType.java52
-rw-r--r--src/main/java/org/zwobble/couscous/values/IntegerValue.java11
-rw-r--r--src/main/java/org/zwobble/couscous/values/MethodValue.java11
-rw-r--r--src/main/java/org/zwobble/couscous/values/StaticMethodValue.java29
-rw-r--r--src/main/java/org/zwobble/couscous/values/StringValue.java7
-rw-r--r--src/main/java/org/zwobble/couscous/values/UnitValue.java2
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java8
13 files changed, 166 insertions, 17 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
new file mode 100644
index 0000000..390c37f
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
@@ -0,0 +1,25 @@
+package org.zwobble.couscous.ast;
+
+import java.util.List;
+
+import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor;
+
+import static java.util.Arrays.asList;
+
+import lombok.Value;
+
+@Value
+public class StaticMethodCallNode implements ExpressionNode {
+ public static StaticMethodCallNode staticMethodCall(String className, String methodName, ExpressionNode... arguments) {
+ return new StaticMethodCallNode(className, methodName, asList(arguments));
+ }
+
+ String className;
+ String methodName;
+ List<ExpressionNode> arguments;
+
+ @Override
+ public <T> T accept(ExpressionNodeVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java
index f083625..5d3ea9b 100644
--- a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeVisitor.java
@@ -3,6 +3,7 @@ package org.zwobble.couscous.ast.visitors;
import org.zwobble.couscous.ast.Assignment;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.MethodCallNode;
+import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
import org.zwobble.couscous.ast.VariableReferenceNode;
@@ -12,4 +13,5 @@ public interface ExpressionNodeVisitor<T> {
T visit(Assignment assignment);
T visit(TernaryConditionalNode ternaryConditional);
T visit(MethodCallNode methodCall);
+ T visit(StaticMethodCallNode staticMethodCall);
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Environment.java b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
index a3b3871..ea4a5e4 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Environment.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
@@ -3,6 +3,8 @@ package org.zwobble.couscous.interpreter;
import java.util.HashMap;
import java.util.Map;
+import org.zwobble.couscous.values.ConcreteType;
+import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.InterpreterValue;
public class Environment {
@@ -19,4 +21,12 @@ public class Environment {
public void put(int referentId, InterpreterValue value) {
stackFrame.put(referentId, value);
}
+
+ public ConcreteType<?> findClass(String className) {
+ if (className == "java.lang.Integer") {
+ return IntegerValue.TYPE;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
index 6a566c9..5acef94 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
@@ -1,11 +1,13 @@
package org.zwobble.couscous.interpreter;
+import java.util.List;
import java.util.stream.Collectors;
import org.zwobble.couscous.ast.Assignment;
import org.zwobble.couscous.ast.ExpressionNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.MethodCallNode;
+import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
import org.zwobble.couscous.ast.VariableReferenceNode;
import org.zwobble.couscous.ast.visitors.ExpressionNodeVisitor;
@@ -62,10 +64,21 @@ public class Evaluator implements ExpressionNodeVisitor<InterpreterValue> {
public InterpreterValue visit(MethodCallNode methodCall) {
val receiver = eval(methodCall.getReceiver());
val type = receiver.getType();
- val arguments = methodCall.getArguments()
+ val arguments = evalArguments(methodCall.getArguments());
+ return type.callMethod(receiver, methodCall.getMethodName(), arguments);
+ }
+
+ private List<InterpreterValue> evalArguments(List<ExpressionNode> arguments) {
+ return arguments
.stream()
.map(argument -> eval(argument))
.collect(Collectors.toList());
- return type.callMethod(receiver, methodCall.getMethodName(), arguments);
+ }
+
+ @Override
+ public InterpreterValue visit(StaticMethodCallNode staticMethodCall) {
+ val clazz = environment.findClass(staticMethodCall.getClassName());
+ val arguments = evalArguments(staticMethodCall.getArguments());
+ return clazz.callStaticMethod(staticMethodCall.getMethodName(), arguments);
}
}
diff --git a/src/main/java/org/zwobble/couscous/values/BooleanValue.java b/src/main/java/org/zwobble/couscous/values/BooleanValue.java
index 3f1f2a2..aeeb7e3 100644
--- a/src/main/java/org/zwobble/couscous/values/BooleanValue.java
+++ b/src/main/java/org/zwobble/couscous/values/BooleanValue.java
@@ -6,7 +6,7 @@ import lombok.Value;
@Value
public class BooleanValue implements InterpreterValue {
- private static final ConcreteType<BooleanValue> TYPE = ConcreteType.<BooleanValue>builder()
+ private static final ConcreteType<BooleanValue> TYPE = ConcreteType.<BooleanValue>builder("Boolean")
.build();
@Getter(value = AccessLevel.NONE)
diff --git a/src/main/java/org/zwobble/couscous/values/Callable.java b/src/main/java/org/zwobble/couscous/values/Callable.java
new file mode 100644
index 0000000..9f3e486
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/values/Callable.java
@@ -0,0 +1,7 @@
+package org.zwobble.couscous.values;
+
+import java.util.List;
+
+public interface Callable {
+ List<ConcreteType<?>> getArgumentTypes();
+}
diff --git a/src/main/java/org/zwobble/couscous/values/ConcreteType.java b/src/main/java/org/zwobble/couscous/values/ConcreteType.java
index 38b1b6e..db5209e 100644
--- a/src/main/java/org/zwobble/couscous/values/ConcreteType.java
+++ b/src/main/java/org/zwobble/couscous/values/ConcreteType.java
@@ -3,6 +3,8 @@ package org.zwobble.couscous.values;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Supplier;
import org.zwobble.couscous.interpreter.Arguments;
import org.zwobble.couscous.interpreter.NoSuchMethod;
@@ -17,32 +19,64 @@ public class ConcreteType<T> {
public static class Builder<T> {
private final ImmutableMap.Builder<String, MethodValue<T>> methods =
ImmutableMap.builder();
+ private final ImmutableMap.Builder<String, StaticMethodValue> staticMethods =
+ ImmutableMap.builder();
+ private final String name;
+
+ public Builder(String name) {
+ this.name = name;
+ }
public Builder<T> method(
String name,
- List<ConcreteType<?>> argumentsTypes,
+ Supplier<List<ConcreteType<?>>> argumentsTypes,
BiFunction<T, Arguments, InterpreterValue> method) {
methods.put(name, new MethodValue<T>(argumentsTypes, method));
return this;
}
+ public Builder<T> staticMethod(
+ String name,
+ Supplier<List<ConcreteType<?>>> argumentsTypes,
+ Function<Arguments, InterpreterValue> method) {
+ staticMethods.put(name, new StaticMethodValue(argumentsTypes, method));
+ return this;
+ }
+
public ConcreteType<T> build() {
- return new ConcreteType<T>(methods.build());
+ return new ConcreteType<T>(name, methods.build(), staticMethods.build());
}
}
+ private String name;
private Map<String, MethodValue<T>> methods;
+ private ImmutableMap<String, StaticMethodValue> staticMethods;
- public ConcreteType(Map<String, MethodValue<T>> methods) {
+ public ConcreteType(
+ String name,
+ Map<String, MethodValue<T>> methods,
+ ImmutableMap<String, StaticMethodValue> staticMethods) {
+ this.name = name;
this.methods = methods;
+ this.staticMethods = staticMethods;
}
- public static <T> ConcreteType.Builder<T> builder() {
- return new Builder<>();
+ public static <T> ConcreteType.Builder<T> builder(String name) {
+ return new Builder<>(name);
}
@SuppressWarnings("unchecked")
public InterpreterValue callMethod(InterpreterValue receiver, String methodName, List<InterpreterValue> arguments) {
+ val method = findMethod(methods, methodName, arguments);
+ return method.apply((T)receiver, new Arguments(arguments));
+ }
+
+ public InterpreterValue callStaticMethod(String methodName, List<InterpreterValue> arguments) {
+ val method = findMethod(staticMethods, methodName, arguments);
+ return method.apply(new Arguments(arguments));
+ }
+
+ private static <T extends Callable> T findMethod(Map<String, T> methods, String methodName, List<InterpreterValue> arguments) {
if (!methods.containsKey(methodName)) {
throw new NoSuchMethod(methodName);
}
@@ -58,7 +92,11 @@ public class ConcreteType<T> {
throw new UnexpectedValueType(formalArgumentType, actualArgumentType);
}
}
-
- return method.apply((T)receiver, new Arguments(arguments));
+ return method;
+ }
+
+ @Override
+ public String toString() {
+ return "ConcreteType<" + name + ">";
}
}
diff --git a/src/main/java/org/zwobble/couscous/values/IntegerValue.java b/src/main/java/org/zwobble/couscous/values/IntegerValue.java
index 8b5b423..1b20a01 100644
--- a/src/main/java/org/zwobble/couscous/values/IntegerValue.java
+++ b/src/main/java/org/zwobble/couscous/values/IntegerValue.java
@@ -1,10 +1,19 @@
package org.zwobble.couscous.values;
+import static java.util.Arrays.asList;
+
import lombok.Value;
+import lombok.val;
@Value
public class IntegerValue implements InterpreterValue {
- public static final ConcreteType<?> TYPE = ConcreteType.<IntegerValue>builder()
+ public static final ConcreteType<?> TYPE = ConcreteType.<IntegerValue>builder("Integer")
+ .staticMethod("parseInt", () -> asList(StringValue.TYPE),
+ arguments -> {
+ val value = (StringValue)arguments.get(0);
+ return new IntegerValue(Integer.parseInt(value.getValue()));
+ })
+
.build();
int value;
diff --git a/src/main/java/org/zwobble/couscous/values/MethodValue.java b/src/main/java/org/zwobble/couscous/values/MethodValue.java
index fe23f94..df53722 100644
--- a/src/main/java/org/zwobble/couscous/values/MethodValue.java
+++ b/src/main/java/org/zwobble/couscous/values/MethodValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.values;
import java.util.List;
import java.util.function.BiFunction;
+import java.util.function.Supplier;
import org.zwobble.couscous.interpreter.Arguments;
@@ -10,11 +11,17 @@ import lombok.Getter;
import lombok.Value;
@Value
-public class MethodValue<T> {
- List<ConcreteType<?>> argumentTypes;
+public class MethodValue<T> implements Callable {
+ @Getter(value=AccessLevel.NONE)
+ Supplier<List<ConcreteType<?>>> argumentTypes;
@Getter(value=AccessLevel.NONE)
BiFunction<T, Arguments, InterpreterValue> apply;
+ @Override
+ public List<ConcreteType<?>> getArgumentTypes() {
+ return argumentTypes.get();
+ }
+
public InterpreterValue apply(T receiver, Arguments arguments) {
return apply.apply(receiver, arguments);
}
diff --git a/src/main/java/org/zwobble/couscous/values/StaticMethodValue.java b/src/main/java/org/zwobble/couscous/values/StaticMethodValue.java
new file mode 100644
index 0000000..15a70d3
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/values/StaticMethodValue.java
@@ -0,0 +1,29 @@
+package org.zwobble.couscous.values;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.zwobble.couscous.interpreter.Arguments;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Value;
+
+@Value
+public class StaticMethodValue implements Callable {
+ @Getter(value=AccessLevel.NONE)
+ Supplier<List<ConcreteType<?>>> argumentTypes;
+ @Getter(value=AccessLevel.NONE)
+ Function<Arguments, InterpreterValue> apply;
+
+ @Override
+ public List<ConcreteType<?>> getArgumentTypes() {
+ return argumentTypes.get();
+ }
+
+ public InterpreterValue apply(Arguments arguments) {
+ return apply.apply(arguments);
+ }
+
+}
diff --git a/src/main/java/org/zwobble/couscous/values/StringValue.java b/src/main/java/org/zwobble/couscous/values/StringValue.java
index 0ade0e1..e8a7c0e 100644
--- a/src/main/java/org/zwobble/couscous/values/StringValue.java
+++ b/src/main/java/org/zwobble/couscous/values/StringValue.java
@@ -7,10 +7,11 @@ import lombok.val;
@Value
public class StringValue implements InterpreterValue {
- public static final ConcreteType<?> TYPE = ConcreteType.<StringValue>builder()
- .method("length", asList(), (receiver, arguments) -> new IntegerValue(receiver.value.length()))
+ public static final ConcreteType<?> TYPE = ConcreteType.<StringValue>builder("String")
+ .method("length", () -> asList(),
+ (receiver, arguments) -> new IntegerValue(receiver.value.length()))
- .method("substring", asList(IntegerValue.TYPE, IntegerValue.TYPE),
+ .method("substring", () -> asList(IntegerValue.TYPE, IntegerValue.TYPE),
(receiver, arguments) -> {
val startIndex = (IntegerValue)arguments.get(0);
val endIndex = (IntegerValue)arguments.get(1);
diff --git a/src/main/java/org/zwobble/couscous/values/UnitValue.java b/src/main/java/org/zwobble/couscous/values/UnitValue.java
index 5742d8a..af00b32 100644
--- a/src/main/java/org/zwobble/couscous/values/UnitValue.java
+++ b/src/main/java/org/zwobble/couscous/values/UnitValue.java
@@ -4,7 +4,7 @@ import lombok.ToString;
@ToString
public class UnitValue implements InterpreterValue {
- private static final ConcreteType<?> TYPE = ConcreteType.<UnitValue>builder()
+ private static final ConcreteType<?> TYPE = ConcreteType.<UnitValue>builder("Unit")
.build();
public static final UnitValue UNIT = new UnitValue();
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 d4cb2f1..d17e7fc 100644
--- a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
@@ -17,6 +17,7 @@ import static org.junit.Assert.assertEquals;
import static org.zwobble.couscous.ast.Assignment.assign;
import static org.zwobble.couscous.ast.LiteralNode.literal;
import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
+import static org.zwobble.couscous.ast.StaticMethodCallNode.staticMethodCall;
import static org.zwobble.couscous.interpreter.Evaluator.eval;
import static org.zwobble.couscous.tests.util.ExtraAsserts.assertThrows;
@@ -91,6 +92,13 @@ public class EvaluatorTests {
assertEquals(new UnexpectedValueType(IntegerValue.TYPE, StringValue.TYPE), exception);
}
+ @Test
+ public void canCallStaticMethod() {
+ val result = eval(emptyEnvironment(),
+ staticMethodCall("java.lang.Integer", "parseInt", literal("42")));
+ assertEquals(new IntegerValue(42), result);
+ }
+
private static Environment emptyEnvironment() {
return new Environment(ImmutableMap.of());
}