aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2015-10-06 11:52:26 +0100
committerMichael Williamson <mike@zwobble.org>2015-10-06 11:52:26 +0100
commitf8824d99ce826ebf83498dbce2bbf1d98472a9e3 (patch)
treec60d08a20db43fda9de9b41451407e3e651ee847
parentfda9d33c0d630906baad5c60e75db5cd8502bfe8 (diff)
Support user-defined static methods in Python backend
-rw-r--r--src/main/java/org/zwobble/couscous/JavaProject.java15
-rw-r--r--src/main/java/org/zwobble/couscous/MapBackedProject.java9
-rw-r--r--src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java27
-rw-r--r--src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java6
-rw-r--r--src/test/java/org/zwobble/couscous/tests/MethodRunner.java7
-rw-r--r--src/test/java/org/zwobble/couscous/tests/backends/python/PythonCompilerEvalTests.java24
-rw-r--r--src/test/java/org/zwobble/couscous/tests/backends/python/PythonMethodRunner.java11
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java33
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterMethodTests.java16
9 files changed, 103 insertions, 45 deletions
diff --git a/src/main/java/org/zwobble/couscous/JavaProject.java b/src/main/java/org/zwobble/couscous/JavaProject.java
index 04ed53d..d54bd9c 100644
--- a/src/main/java/org/zwobble/couscous/JavaProject.java
+++ b/src/main/java/org/zwobble/couscous/JavaProject.java
@@ -1,10 +1,25 @@
package org.zwobble.couscous;
+import java.util.List;
+
+import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.interpreter.values.ConcreteType;
import org.zwobble.couscous.interpreter.values.IntegerInterpreterValue;
+import com.google.common.collect.Iterables;
+
+import lombok.val;
+
public class JavaProject {
public static MapBackedProject.Builder builder() {
return MapBackedProject.builder()
.addClass(IntegerInterpreterValue.TYPE);
}
+
+ public static Project of(List<ClassNode> classNodes) {
+ val concreteTypes = (Iterable)Iterables.transform(classNodes, ConcreteType::fromNode);
+ return builder()
+ .addClasses(concreteTypes)
+ .build();
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/MapBackedProject.java b/src/main/java/org/zwobble/couscous/MapBackedProject.java
index 10f2ba1..3150279 100644
--- a/src/main/java/org/zwobble/couscous/MapBackedProject.java
+++ b/src/main/java/org/zwobble/couscous/MapBackedProject.java
@@ -7,6 +7,8 @@ import org.zwobble.couscous.interpreter.values.ConcreteType;
import com.google.common.collect.ImmutableMap;
+import lombok.val;
+
public class MapBackedProject implements Project {
public static Builder builder() {
return new Builder();
@@ -24,6 +26,13 @@ public class MapBackedProject implements Project {
return this;
}
+ public Builder addClasses(Iterable<ConcreteType<?>> classes) {
+ for (val clazz : classes) {
+ addClass(clazz);
+ }
+ return this;
+ }
+
public Project build() {
return new MapBackedProject(classes.build());
}
diff --git a/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java b/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
index e2ce667..ebfbd88 100644
--- a/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
@@ -1,12 +1,19 @@
package org.zwobble.couscous.tests;
+import java.util.List;
+
import org.junit.Test;
+import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.MethodNode;
+import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.StringValue;
+import com.google.common.collect.ImmutableList;
+
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.zwobble.couscous.ast.LiteralNode.literal;
@@ -64,5 +71,23 @@ public abstract class BackendEvalTests {
assertEquals(value(42), result);
}
- protected abstract PrimitiveValue evalExpression(ExpressionNode expression);
+ @Test
+ public void canCallStaticMethodFromUserDefinedStaticMethod() {
+ val classNode = ClassNode.builder("com.example.Example")
+ .method(MethodNode.staticMethod("main")
+ .statement(new ReturnNode(staticMethodCall("java.lang.Integer", "parseInt", literal("42"))))
+ .build())
+ .build();
+ val result = evalExpression(asList(classNode),
+ staticMethodCall("com.example.Example", "main"));
+ assertEquals(value(42), result);
+ }
+
+ protected PrimitiveValue evalExpression(ExpressionNode expression) {
+ return evalExpression(ImmutableList.of(), expression);
+ }
+
+ protected abstract PrimitiveValue evalExpression(
+ List<ClassNode> classes,
+ ExpressionNode expression);
}
diff --git a/src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java b/src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java
index b55a2ca..fd6d91e 100644
--- a/src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/BackendMethodTests.java
@@ -96,7 +96,11 @@ public abstract class BackendMethodTests {
.build();
val runner = buildMethodRunner();
- return runner.runMethod(classNode, method.getName(), asList(arguments));
+ return runner.runMethod(
+ asList(classNode),
+ classNode.getName(),
+ method.getName(),
+ asList(arguments));
}
protected abstract MethodRunner buildMethodRunner();
diff --git a/src/test/java/org/zwobble/couscous/tests/MethodRunner.java b/src/test/java/org/zwobble/couscous/tests/MethodRunner.java
index ff9439c..3f69779 100644
--- a/src/test/java/org/zwobble/couscous/tests/MethodRunner.java
+++ b/src/test/java/org/zwobble/couscous/tests/MethodRunner.java
@@ -3,8 +3,13 @@ package org.zwobble.couscous.tests;
import java.util.List;
import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.values.PrimitiveValue;
public interface MethodRunner {
- PrimitiveValue runMethod(ClassNode classNode, String methodName, List<PrimitiveValue> arguments);
+ PrimitiveValue runMethod(
+ List<ClassNode> classNodes,
+ TypeName className,
+ String methodName,
+ List<PrimitiveValue> arguments);
}
diff --git a/src/test/java/org/zwobble/couscous/tests/backends/python/PythonCompilerEvalTests.java b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonCompilerEvalTests.java
index 6f19eb3..e4a5ddf 100644
--- a/src/test/java/org/zwobble/couscous/tests/backends/python/PythonCompilerEvalTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonCompilerEvalTests.java
@@ -1,5 +1,7 @@
package org.zwobble.couscous.tests.backends.python;
+import java.util.List;
+
import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.ast.ExpressionNode;
import org.zwobble.couscous.ast.MethodNode;
@@ -8,19 +10,29 @@ import org.zwobble.couscous.tests.BackendEvalTests;
import org.zwobble.couscous.values.PrimitiveValue;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import static java.util.Arrays.asList;
import lombok.val;
public class PythonCompilerEvalTests extends BackendEvalTests {
@Override
- protected PrimitiveValue evalExpression(ExpressionNode expression) {
+ protected PrimitiveValue evalExpression(
+ List<ClassNode> classes,
+ ExpressionNode expression) {
+
+ val programNode = ClassNode.builder("Program")
+ .method(MethodNode.staticMethod("run")
+ .statement(new ReturnNode(expression))
+ .build())
+ .build();
+
val runner = new PythonMethodRunner();
+
return runner.runMethod(
- ClassNode.builder("Program")
- .method(MethodNode.staticMethod("run")
- .statement(new ReturnNode(expression))
- .build())
- .build(),
+ ImmutableList.copyOf(Iterables.concat(classes, asList(programNode))),
+ programNode.getName(),
"run",
ImmutableList.of());
}
diff --git a/src/test/java/org/zwobble/couscous/tests/backends/python/PythonMethodRunner.java b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonMethodRunner.java
index 73c1c52..33de465 100644
--- a/src/test/java/org/zwobble/couscous/tests/backends/python/PythonMethodRunner.java
+++ b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonMethodRunner.java
@@ -7,6 +7,7 @@ import java.nio.file.Files;
import java.util.List;
import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.backends.python.PythonCodeGenerator;
import org.zwobble.couscous.backends.python.PythonCompiler;
import org.zwobble.couscous.backends.python.PythonSerializer;
@@ -19,7 +20,6 @@ import com.google.common.base.Joiner;
import com.google.common.io.CharStreams;
import static java.lang.Integer.parseInt;
-import static java.util.Arrays.asList;
import static org.zwobble.couscous.tests.util.ExtraFiles.deleteRecursively;
import static org.zwobble.couscous.values.PrimitiveValues.value;
@@ -30,21 +30,22 @@ public class PythonMethodRunner implements MethodRunner {
@Override
@SneakyThrows
public PrimitiveValue runMethod(
- ClassNode classNode,
+ List<ClassNode> classNodes,
+ TypeName className,
String methodName,
List<PrimitiveValue> arguments) {
val directoryPath = Files.createTempDirectory(null);
val compiler = new PythonCompiler(directoryPath);
try {
- compiler.compile(asList(classNode));
+ compiler.compile(classNodes);
val argumentsString = Joiner.on(", ").join(arguments.stream()
.map(PythonCodeGenerator::generateCode)
.map(PythonSerializer::serialize)
.iterator());
- val program = "from " + classNode.getName().getQualifiedName() + " import " + classNode.getSimpleName() +
- ";print(repr(" + classNode.getSimpleName() + "." + methodName + "(" + argumentsString + ")))";
+ val program = "from " + className.getQualifiedName() + " import " + className.getSimpleName() +
+ ";print(repr(" + className.getSimpleName() + "." + methodName + "(" + argumentsString + ")))";
val process = new ProcessBuilder("python3.4", "-c", program)
.directory(directoryPath.toFile())
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 bc6a205..4ec8428 100644
--- a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
@@ -1,12 +1,12 @@
package org.zwobble.couscous.tests.interpreter;
+import java.util.List;
+
import org.junit.Test;
import org.zwobble.couscous.JavaProject;
import org.zwobble.couscous.MapBackedProject;
import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.ast.ExpressionNode;
-import org.zwobble.couscous.ast.MethodNode;
-import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
import org.zwobble.couscous.interpreter.ConditionMustBeBoolean;
import org.zwobble.couscous.interpreter.Environment;
@@ -14,7 +14,6 @@ import org.zwobble.couscous.interpreter.NoSuchMethod;
import org.zwobble.couscous.interpreter.StackFrameBuilder;
import org.zwobble.couscous.interpreter.UnexpectedValueType;
import org.zwobble.couscous.interpreter.WrongNumberOfArguments;
-import org.zwobble.couscous.interpreter.values.ConcreteType;
import org.zwobble.couscous.interpreter.values.IntegerInterpreterValue;
import org.zwobble.couscous.interpreter.values.StringInterpreterValue;
import org.zwobble.couscous.tests.BackendEvalTests;
@@ -30,7 +29,6 @@ import static org.zwobble.couscous.ast.AssignmentNode.assign;
import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg;
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.ast.VariableDeclaration.var;
import static org.zwobble.couscous.interpreter.Evaluator.eval;
import static org.zwobble.couscous.interpreter.values.InterpreterValues.value;
@@ -93,23 +91,6 @@ public class EvaluatorTests extends BackendEvalTests {
assertEquals(new UnexpectedValueType(IntegerValue.REF, StringValue.REF), exception);
}
- @Test
- public void canCallStaticMethodFromAnotherStaticMethod() {
- val classNode = ClassNode.builder("com.example.Program")
- .method(MethodNode.staticMethod("main")
- .statement(new ReturnNode(staticMethodCall("java.lang.Integer", "parseInt", literal("42"))))
- .build())
- .build();
- val environment = new Environment(
- JavaProject.builder()
- .addClass(ConcreteType.fromNode(classNode))
- .build(),
- ImmutableMap.of());
- val result = eval(environment,
- staticMethodCall("com.example.Program", "main"));
- assertEquals(new IntegerInterpreterValue(42), result);
- }
-
private static Environment emptyEnvironment() {
return new Environment(
JavaProject.builder().build(),
@@ -117,7 +98,13 @@ public class EvaluatorTests extends BackendEvalTests {
}
@Override
- protected PrimitiveValue evalExpression(ExpressionNode expression) {
- return eval(emptyEnvironment(), expression).toPrimitiveValue().get();
+ protected PrimitiveValue evalExpression(
+ List<ClassNode> classes,
+ ExpressionNode expression) {
+
+ val environment = new Environment(
+ JavaProject.of(classes),
+ ImmutableMap.of());
+ return eval(environment, expression).toPrimitiveValue().get();
}
}
diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterMethodTests.java b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterMethodTests.java
index 26939dc..449a251 100644
--- a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterMethodTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterMethodTests.java
@@ -4,18 +4,18 @@ import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
-import org.zwobble.couscous.MapBackedProject;
+import org.zwobble.couscous.JavaProject;
import org.zwobble.couscous.ast.AssignmentNode;
import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.ast.ExpressionStatementNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.ReturnNode;
+import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.interpreter.Interpreter;
import org.zwobble.couscous.interpreter.UnboundVariable;
import org.zwobble.couscous.interpreter.UnexpectedValueType;
import org.zwobble.couscous.interpreter.VariableNotInScope;
import org.zwobble.couscous.interpreter.WrongNumberOfArguments;
-import org.zwobble.couscous.interpreter.values.ConcreteType;
import org.zwobble.couscous.interpreter.values.InterpreterValues;
import org.zwobble.couscous.tests.BackendMethodTests;
import org.zwobble.couscous.tests.MethodRunner;
@@ -23,8 +23,6 @@ import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.StringValue;
-import com.google.common.collect.ImmutableMap;
-
import static org.junit.Assert.assertEquals;
import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg;
import static org.zwobble.couscous.ast.LiteralNode.literal;
@@ -106,16 +104,18 @@ public class InterpreterMethodTests extends BackendMethodTests {
return new MethodRunner() {
@Override
public PrimitiveValue runMethod(
- ClassNode classNode,
+ List<ClassNode> classNodes,
+ TypeName className,
String methodName,
List<PrimitiveValue> arguments) {
- val interpreter = new Interpreter(new MapBackedProject(ImmutableMap.of(
- classNode.getName(), ConcreteType.fromNode(classNode))));
+
+ val project = JavaProject.of(classNodes);
+ val interpreter = new Interpreter(project);
val argumentValues = arguments.stream()
.map(InterpreterValues::value)
.collect(Collectors.toList());
- return interpreter.run(classNode.getName(), methodName, argumentValues)
+ return interpreter.run(className, methodName, argumentValues)
.toPrimitiveValue().get();
}
};