aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java9
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaParser.java15
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java60
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java4
-rw-r--r--src/main/java/org/zwobble/couscous/values/ObjectValue.java7
-rw-r--r--src/main/java/org/zwobble/couscous/values/ObjectValues.java8
-rw-r--r--src/main/resources/python/runtime/_couscous.py8
-rw-r--r--src/main/resources/python/runtime/java/lang/Integer.py8
-rw-r--r--src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java5
-rw-r--r--src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java43
10 files changed, 137 insertions, 30 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
index 4af0701..36fea9f 100644
--- a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
@@ -5,6 +5,7 @@ import java.util.List;
import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
import org.zwobble.couscous.values.BooleanValue;
import org.zwobble.couscous.values.InternalCouscousValue;
+import org.zwobble.couscous.values.ObjectValues;
import static java.util.Arrays.asList;
@@ -13,6 +14,14 @@ public class StaticMethodCallNode implements ExpressionNode {
return staticMethodCall(InternalCouscousValue.REF, "same", asList(left, right), BooleanValue.REF);
}
+ public static ExpressionNode boxInt(ExpressionNode value) {
+ return staticMethodCall(
+ InternalCouscousValue.REF,
+ "boxInt",
+ asList(value),
+ ObjectValues.BOXED_INT);
+ }
+
public static StaticMethodCallNode staticMethodCall(
String className,
String methodName,
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaParser.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaParser.java
index 0f8d963..c8e13b0 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaParser.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaParser.java
@@ -5,6 +5,8 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Hashtable;
+
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
@@ -18,8 +20,17 @@ public class JavaParser {
parser = ASTParser.newParser(AST.JLS8);
parser.setBindingsRecovery(true);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
- parser.setCompilerOptions(JavaCore.getOptions());
- parser.setEnvironment(new String[]{"/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar"}, new String[0], new String[0], false);
+ @SuppressWarnings("unchecked")
+ Hashtable<String, String> options = JavaCore.getOptions();
+ options.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_8);
+ options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_8);
+ options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_8);
+ parser.setCompilerOptions(options);
+ parser.setEnvironment(
+ new String[]{"/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar"},
+ new String[]{"/usr/lib/jvm/java-8-openjdk-amd64/jre/src.zip"},
+ new String[]{"UTF-8"},
+ false);
parser.setResolveBindings(true);
}
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
index c08e447..a74cb35 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
@@ -3,6 +3,8 @@ package org.zwobble.couscous.frontends.java;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Assignment;
@@ -53,6 +55,7 @@ import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.values.BooleanValue;
import org.zwobble.couscous.values.IntegerValue;
+import org.zwobble.couscous.values.ObjectValues;
import static java.util.Arrays.asList;
import static org.zwobble.couscous.ast.ExpressionStatementNode.expressionStatement;
@@ -141,11 +144,12 @@ public class JavaReader {
}
private static StatementNode readReturnStatement(ReturnStatement statement) {
- return ReturnNode.returns(readExpression(statement.getExpression()));
+ // TODO: set target type
+ return ReturnNode.returns(readExpressionWithoutBoxing(statement.getExpression()));
}
private static StatementNode readExpressionStatement(ExpressionStatement statement) {
- return expressionStatement(readExpression(statement.getExpression()));
+ return expressionStatement(readExpressionWithoutBoxing(statement.getExpression()));
}
private static List<StatementNode> readVariableDeclarationStatement(VariableDeclarationStatement statement) {
@@ -157,10 +161,19 @@ public class JavaReader {
fragment.resolveBinding().getKey(),
fragment.getName().getIdentifier(),
type,
- readExpression(fragment.getInitializer())));
+ readExpression(type, fragment.getInitializer())));
}
- private static ExpressionNode readExpression(Expression expression) {
+ private static ExpressionNode readExpression(TypeName targetType, Expression expression) {
+ ExpressionNode couscousExpression = readExpressionWithoutBoxing(expression);
+ if (couscousExpression.getType().equals(IntegerValue.REF) && !targetType.equals(IntegerValue.REF)) {
+ return StaticMethodCallNode.boxInt(couscousExpression);
+ } else {
+ return couscousExpression;
+ }
+ }
+
+ private static ExpressionNode readExpressionWithoutBoxing(Expression expression) {
switch (expression.getNodeType()) {
case ASTNode.BOOLEAN_LITERAL:
return readBooleanLiteral((BooleanLiteral)expression);
@@ -236,13 +249,15 @@ public class JavaReader {
}
private static ExpressionNode readFieldAccess(FieldAccess expression) {
- return FieldAccessNode.fieldAccess(readExpression(expression.getExpression()), expression.getName().getIdentifier(), typeOf(expression));
+ return FieldAccessNode.fieldAccess(readExpressionWithoutBoxing(expression.getExpression()), expression.getName().getIdentifier(), typeOf(expression));
}
private static ExpressionNode readMethodInvocation(MethodInvocation expression) {
String methodName = expression.getName().getIdentifier();
@SuppressWarnings("unchecked")
- List<ExpressionNode> arguments = readArguments(expression.arguments());
+ List<ExpressionNode> arguments = readArguments(
+ expression.resolveMethodBinding(),
+ expression.arguments());
final TypeName type = typeOf(expression);
IMethodBinding methodBinding = expression.resolveMethodBinding();
@@ -256,7 +271,7 @@ public class JavaReader {
} else {
ExpressionNode receiver = expression.getExpression() == null
? ThisReferenceNode.thisReference(receiverType)
- : readExpression(expression.getExpression());
+ : readExpressionWithoutBoxing(expression.getExpression());
return MethodCallNode.methodCall(
receiver,
methodName,
@@ -270,18 +285,22 @@ public class JavaReader {
List<Expression> arguments = expression.arguments();
return ConstructorCallNode.constructorCall(
typeOf(expression),
- readArguments(arguments));
+ readArguments(expression.resolveConstructorBinding(), arguments));
}
- private static List<ExpressionNode> readArguments(List<Expression> javaArguments) {
- return eagerMap(javaArguments, JavaReader::readExpression);
+ private static List<ExpressionNode> readArguments(IMethodBinding method, List<Expression> javaArguments) {
+ return IntStream.range(0, javaArguments.size())
+ .mapToObj(index -> readExpression(
+ typeOf(method.getParameterTypes()[index]),
+ javaArguments.get(index)))
+ .collect(Collectors.toList());
}
private static ExpressionNode readInfixExpression(InfixExpression expression) {
- final ExpressionNode left = readExpression(expression.getLeftOperand());
- final ExpressionNode right = readExpression(expression.getRightOperand());
-
- if (left.getType().equals(IntegerValue.REF)) {
+ if (typeOf(expression.getLeftOperand()).equals(IntegerValue.REF)) {
+ ExpressionNode left = readExpression(IntegerValue.REF, expression.getLeftOperand());
+ ExpressionNode right = readExpression(IntegerValue.REF, expression.getRightOperand());
+
if (expression.getOperator() == Operator.NOT_EQUALS) {
return MethodCallNode.not(methodCall(left, "equals", asList(right), BooleanValue.REF));
} else {
@@ -293,6 +312,8 @@ public class JavaReader {
operator.returnValue);
}
} else {
+ ExpressionNode left = readExpression(ObjectValues.OBJECT, expression.getLeftOperand());
+ ExpressionNode right = readExpression(ObjectValues.OBJECT, expression.getRightOperand());
if (expression.getOperator() == Operator.EQUALS) {
return StaticMethodCallNode.same(left, right);
} else if (expression.getOperator() == Operator.NOT_EQUALS) {
@@ -344,11 +365,18 @@ public class JavaReader {
}
private static ExpressionNode readConditionalExpression(ConditionalExpression expression) {
- return TernaryConditionalNode.ternaryConditional(readExpression(expression.getExpression()), readExpression(expression.getThenExpression()), readExpression(expression.getElseExpression()));
+ TypeName type = typeOf(expression);
+ return TernaryConditionalNode.ternaryConditional(
+ readExpression(BooleanValue.REF, expression.getExpression()),
+ readExpression(type, expression.getThenExpression()),
+ readExpression(type, expression.getElseExpression()));
}
private static ExpressionNode readAssignment(Assignment expression) {
- return AssignmentNode.assign((AssignableExpressionNode)readExpression(expression.getLeftHandSide()), readExpression(expression.getRightHandSide()));
+ ExpressionNode left = readExpressionWithoutBoxing(expression.getLeftHandSide());
+ return AssignmentNode.assign(
+ (AssignableExpressionNode)left,
+ readExpression(left.getType(), expression.getRightHandSide()));
}
private static String generateClassName(CompilationUnit ast) {
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java
index 30645d6..873805e 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java
@@ -1,12 +1,12 @@
package org.zwobble.couscous.interpreter.values;
-import org.zwobble.couscous.values.ObjectValue;
+import org.zwobble.couscous.values.ObjectValues;
import static java.util.Arrays.asList;
public class InternalCouscousInterpeterValue {
public static final ConcreteType TYPE = ConcreteType.classBuilder("_couscous")
- .staticMethod("same", asList(ObjectValue.REF, ObjectValue.REF),
+ .staticMethod("same", asList(ObjectValues.OBJECT, ObjectValues.OBJECT),
(environment, arguments) ->
new BooleanInterpreterValue(arguments.get(0) == arguments.get(1)))
diff --git a/src/main/java/org/zwobble/couscous/values/ObjectValue.java b/src/main/java/org/zwobble/couscous/values/ObjectValue.java
deleted file mode 100644
index ea59ae0..0000000
--- a/src/main/java/org/zwobble/couscous/values/ObjectValue.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.zwobble.couscous.values;
-
-import org.zwobble.couscous.ast.TypeName;
-
-public class ObjectValue {
- public static final TypeName REF = TypeName.of("java.lang.Object");
-}
diff --git a/src/main/java/org/zwobble/couscous/values/ObjectValues.java b/src/main/java/org/zwobble/couscous/values/ObjectValues.java
new file mode 100644
index 0000000..ee02a11
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/values/ObjectValues.java
@@ -0,0 +1,8 @@
+package org.zwobble.couscous.values;
+
+import org.zwobble.couscous.ast.TypeName;
+
+public class ObjectValues {
+ public static final TypeName OBJECT = TypeName.of("java.lang.Object");
+ public static final TypeName BOXED_INT = TypeName.of("java.lang.Integer");
+}
diff --git a/src/main/resources/python/runtime/_couscous.py b/src/main/resources/python/runtime/_couscous.py
index 0183f0b..70d9a97 100644
--- a/src/main/resources/python/runtime/_couscous.py
+++ b/src/main/resources/python/runtime/_couscous.py
@@ -1,5 +1,11 @@
+from .java.lang.Integer import Integer
+
+
def _div_round_to_zero(a, b):
return -(-a // b) if (a < 0) ^ (b < 0) else a // b
def _mod_round_to_zero(a, b):
- return -(-a % b) if (a < 0) ^ (b < 0) else a % b \ No newline at end of file
+ return -(-a % b) if (a < 0) ^ (b < 0) else a % b
+
+def boxInt(value):
+ return Integer(value)
diff --git a/src/main/resources/python/runtime/java/lang/Integer.py b/src/main/resources/python/runtime/java/lang/Integer.py
index 052f799..e16e7bb 100644
--- a/src/main/resources/python/runtime/java/lang/Integer.py
+++ b/src/main/resources/python/runtime/java/lang/Integer.py
@@ -1,3 +1,11 @@
class Integer(object):
+ @staticmethod
def parseInt(value):
return int(value)
+
+ def __init__(self, value):
+ self._value = value
+
+ def equals(self, other):
+ # TODO: test this properly
+ return isinstance(other, Integer) and other._value == self._value
diff --git a/src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java b/src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java
index e5a6bf7..b639c6f 100644
--- a/src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java
@@ -37,6 +37,11 @@ public class JavaToPythonTests {
assertEquals(value(true), exec("Object x = new Object(); return x == x;"));
}
+ @Test
+ public void integersCanBeAssignedToObjectVariable() {
+ assertEquals(value(false), exec("Object x = 1; return x.equals(new Object());"));
+ }
+
private PrimitiveValue exec(String source) {
try {
String javaClass =
diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java
index dffa7df..c455e8d 100644
--- a/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java
@@ -14,11 +14,13 @@ import org.zwobble.couscous.ast.LocalVariableDeclarationNode;
import org.zwobble.couscous.ast.MethodNode;
import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.StatementNode;
+import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.frontends.java.JavaReader;
import org.zwobble.couscous.values.BooleanValue;
import org.zwobble.couscous.values.IntegerValue;
+import org.zwobble.couscous.values.ObjectValues;
import org.zwobble.couscous.values.StringValue;
import static java.util.Arrays.asList;
@@ -33,6 +35,7 @@ import static org.zwobble.couscous.ast.FieldDeclarationNode.field;
import static org.zwobble.couscous.ast.LiteralNode.literal;
import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
import static org.zwobble.couscous.ast.MethodCallNode.not;
+import static org.zwobble.couscous.ast.StaticMethodCallNode.boxInt;
import static org.zwobble.couscous.ast.StaticMethodCallNode.same;
import static org.zwobble.couscous.ast.StaticMethodCallNode.staticMethodCall;
import static org.zwobble.couscous.ast.TernaryConditionalNode.ternaryConditional;
@@ -154,8 +157,17 @@ public class JavaReaderTests {
@Test
public void canReadConstructorCalls() {
assertEquals(
- constructorCall(TypeName.of("java.lang.String"), asList(literal("_"), literal(42))),
- readExpression("new String(\"_\", 42)"));
+ constructorCall(TypeName.of("java.lang.String"), asList(literal("_"))),
+ readExpression("new String(\"_\")"));
+ }
+
+ @Test
+ public void argumentIsBoxedIfNecessary() {
+ StaticMethodCallNode expression = (StaticMethodCallNode)readExpression(
+ "java.util.Objects.toString(42)");
+ assertEquals(
+ boxInt(literal(42)),
+ expression.getArguments().get(0));
}
@Test
@@ -238,6 +250,25 @@ public class JavaReaderTests {
}
@Test
+ public void integersAreBoxedIfAssignedToObjectVariable() {
+ ClassNode classNode = readClass(
+ "private Object value;" +
+ "public Object getValue() {" +
+ " return value = 4;" +
+ "}");
+
+ ReturnNode returnNode = (ReturnNode) classNode.getMethods().get(0).getBody().get(0);
+ assertEquals(
+ assign(
+ fieldAccess(
+ thisReference(TypeName.of("com.example.Example")),
+ "value",
+ ObjectValues.OBJECT),
+ boxInt(literal(4))),
+ returnNode.getValue());
+ }
+
+ @Test
public void canDeclareAndReferenceLocalVariables() {
List<StatementNode> statements = readStatements("int x = 4; return x;");
@@ -252,6 +283,14 @@ public class JavaReaderTests {
}
@Test
+ public void integersAreBoxedIfDeclaredAsObjectVariable() {
+ List<StatementNode> statements = readStatements("Object x = 4;");
+
+ LocalVariableDeclarationNode declaration = (LocalVariableDeclarationNode) statements.get(0);
+ assertEquals(StaticMethodCallNode.boxInt(literal(4)), declaration.getInitialValue());
+ }
+
+ @Test
public void canDeclareAndReferenceArguments() {
ClassNode classNode = readClass(
"public String identity(int value) {" +