diff options
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) {" + |
