aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java42
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PrimitiveMethods.java24
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java19
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java4
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java12
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonBinaryOperation.java39
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java2
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java52
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Interpreter.java24
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/JavaProject.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java14
-rw-r--r--src/main/java/org/zwobble/couscous/values/InternalCouscousValue.java7
-rw-r--r--src/main/java/org/zwobble/couscous/values/ObjectValue.java7
-rw-r--r--src/main/resources/python/runtime/java/lang/Object.py1
-rw-r--r--src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java27
-rw-r--r--src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java5
-rw-r--r--src/test/java/org/zwobble/couscous/tests/backends/python/PythonSerializerTests.java8
-rw-r--r--src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java29
18 files changed, 268 insertions, 54 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
index 2b63948..4af0701 100644
--- a/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/StaticMethodCallNode.java
@@ -3,36 +3,46 @@ package org.zwobble.couscous.ast;
import java.util.List;
import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
+import org.zwobble.couscous.values.BooleanValue;
+import org.zwobble.couscous.values.InternalCouscousValue;
import static java.util.Arrays.asList;
public class StaticMethodCallNode implements ExpressionNode {
- public static StaticMethodCallNode staticMethodCall(String className, String methodName, ExpressionNode... arguments) {
- return staticMethodCall(className, methodName, asList(arguments));
+ public static ExpressionNode same(ExpressionNode left, ExpressionNode right) {
+ return staticMethodCall(InternalCouscousValue.REF, "same", asList(left, right), BooleanValue.REF);
}
- public static StaticMethodCallNode staticMethodCall(String className, String methodName, List<ExpressionNode> arguments) {
- return staticMethodCall(TypeName.of(className), methodName, arguments);
+ public static StaticMethodCallNode staticMethodCall(
+ String className,
+ String methodName,
+ List<ExpressionNode> arguments,
+ TypeName type) {
+ return staticMethodCall(TypeName.of(className), methodName, arguments, type);
}
public static StaticMethodCallNode staticMethodCall(
TypeName className,
String methodName,
- List<ExpressionNode> arguments) {
- return new StaticMethodCallNode(className, methodName, arguments);
+ List<ExpressionNode> arguments,
+ TypeName type) {
+ return new StaticMethodCallNode(className, methodName, arguments, type);
}
private final TypeName className;
private final String methodName;
private final List<ExpressionNode> arguments;
+ private final TypeName type;
public StaticMethodCallNode(
TypeName className,
String methodName,
- List<ExpressionNode> arguments) {
+ List<ExpressionNode> arguments,
+ TypeName type) {
this.className = className;
this.methodName = methodName;
this.arguments = arguments;
+ this.type = type;
}
public TypeName getClassName() {
@@ -47,20 +57,20 @@ public class StaticMethodCallNode implements ExpressionNode {
return arguments;
}
+ public TypeName getType() {
+ return type;
+ }
+
@Override
public <T> T accept(ExpressionNodeMapper<T> visitor) {
return visitor.visit(this);
}
@Override
- public TypeName getType() {
- throw new UnsupportedOperationException();
- }
-
- @Override
public String toString() {
return "StaticMethodCallNode(className=" + className + ", methodName="
- + methodName + ", arguments=" + arguments + ")";
+ + methodName + ", arguments=" + arguments + ", type=" + type
+ + ")";
}
@Override
@@ -73,6 +83,7 @@ public class StaticMethodCallNode implements ExpressionNode {
+ ((className == null) ? 0 : className.hashCode());
result = prime * result
+ ((methodName == null) ? 0 : methodName.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@@ -100,6 +111,11 @@ public class StaticMethodCallNode implements ExpressionNode {
return false;
} else if (!methodName.equals(other.methodName))
return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
return true;
}
}
diff --git a/src/main/java/org/zwobble/couscous/backends/python/PrimitiveMethods.java b/src/main/java/org/zwobble/couscous/backends/python/PrimitiveMethods.java
index c6aa62d..6605fc9 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/PrimitiveMethods.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/PrimitiveMethods.java
@@ -9,6 +9,7 @@ import org.zwobble.couscous.backends.python.ast.PythonExpressionNode;
import org.zwobble.couscous.backends.python.ast.PythonNotNode;
import org.zwobble.couscous.values.BooleanValue;
import org.zwobble.couscous.values.IntegerValue;
+import org.zwobble.couscous.values.InternalCouscousValue;
import org.zwobble.couscous.values.StringValue;
import com.google.common.collect.ImmutableMap;
@@ -16,6 +17,7 @@ import com.google.common.collect.ImmutableMap.Builder;
import static java.util.Arrays.asList;
import static org.zwobble.couscous.backends.python.ast.PythonAttributeAccessNode.pythonAttributeAccess;
+import static org.zwobble.couscous.backends.python.ast.PythonBinaryOperation.pythonIs;
import static org.zwobble.couscous.backends.python.ast.PythonCallNode.pythonCall;
import static org.zwobble.couscous.backends.python.ast.PythonGetSliceNode.pythonGetSlice;
import static org.zwobble.couscous.backends.python.ast.PythonVariableReferenceNode.pythonVariableReference;
@@ -90,6 +92,18 @@ public class PrimitiveMethods {
.put(IntegerValue.REF, INT_METHODS)
.build();
+ private static final Map<String, PrimitiveStaticMethodGenerator> INTERNAL_METHODS =
+ ImmutableMap.<String, PrimitiveStaticMethodGenerator>builder()
+
+ .put("same", arguments -> pythonIs(arguments.get(0), arguments.get(1)))
+
+ .build();
+
+ private static final Map<TypeName, Map<String, PrimitiveStaticMethodGenerator>> STATIC_METHODS =
+ ImmutableMap.<TypeName, Map<String, PrimitiveStaticMethodGenerator>>builder()
+ .put(InternalCouscousValue.REF, INTERNAL_METHODS)
+ .build();
+
public static boolean isPrimitive(TypeName type) {
return METHODS.containsKey(type);
}
@@ -103,4 +117,14 @@ public class PrimitiveMethods {
public interface PrimitiveMethodGenerator {
PythonExpressionNode generate(PythonExpressionNode receiver, List<PythonExpressionNode> arguments);
}
+
+ public static Optional<PrimitiveStaticMethodGenerator> getPrimitiveStaticMethod(TypeName type, String methodName) {
+ return Optional.ofNullable(STATIC_METHODS.get(type))
+ .flatMap(methodsForType -> Optional.ofNullable(methodsForType.get(methodName)));
+ }
+
+ @FunctionalInterface
+ public interface PrimitiveStaticMethodGenerator {
+ PythonExpressionNode generate(List<PythonExpressionNode> arguments);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
index 54d56ed..fb1851c 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
@@ -29,6 +29,7 @@ import org.zwobble.couscous.ast.visitors.NodeVisitorWithEmptyDefaults;
import org.zwobble.couscous.ast.visitors.NodeVisitors;
import org.zwobble.couscous.ast.visitors.StatementNodeMapper;
import org.zwobble.couscous.backends.python.PrimitiveMethods.PrimitiveMethodGenerator;
+import org.zwobble.couscous.backends.python.ast.PythonAttributeAccessNode;
import org.zwobble.couscous.backends.python.ast.PythonBlock;
import org.zwobble.couscous.backends.python.ast.PythonClassNode;
import org.zwobble.couscous.backends.python.ast.PythonExpressionNode;
@@ -39,6 +40,7 @@ import org.zwobble.couscous.backends.python.ast.PythonStatementNode;
import org.zwobble.couscous.backends.python.ast.PythonVariableReferenceNode;
import org.zwobble.couscous.values.BooleanValue;
import org.zwobble.couscous.values.IntegerValue;
+import org.zwobble.couscous.values.InternalCouscousValue;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValueVisitor;
import org.zwobble.couscous.values.StringValue;
@@ -92,7 +94,9 @@ public class PythonCodeGenerator {
private static Stream<PythonImportNode> generateImports(ClassNode classNode) {
Set<TypeName> classes = findReferencedClasses(classNode);
- return classes.stream().map(name -> pythonImport(importPathToRoot(classNode) + name.getQualifiedName(), asList(pythonImportAlias(name.getSimpleName()))));
+ return classes.stream()
+ .filter(name -> !name.equals(InternalCouscousValue.REF))
+ .map(name -> pythonImport(importPathToRoot(classNode) + name.getQualifiedName(), asList(pythonImportAlias(name.getSimpleName()))));
}
private static String importPathToRoot(ClassNode classNode) {
@@ -243,11 +247,14 @@ public class PythonCodeGenerator {
@Override
public PythonExpressionNode visit(StaticMethodCallNode staticMethodCall) {
- final org.zwobble.couscous.ast.TypeName className = staticMethodCall.getClassName();
- final org.zwobble.couscous.backends.python.ast.PythonVariableReferenceNode classReference = pythonVariableReference(className.getSimpleName());
- final org.zwobble.couscous.backends.python.ast.PythonAttributeAccessNode methodReference = pythonAttributeAccess(classReference, staticMethodCall.getMethodName());
- final java.util.List<org.zwobble.couscous.backends.python.ast.PythonExpressionNode> arguments = generateExpressions(staticMethodCall.getArguments());
- return pythonCall(methodReference, arguments);
+ TypeName className = staticMethodCall.getClassName();
+ PythonVariableReferenceNode classReference = pythonVariableReference(className.getSimpleName());
+ PythonAttributeAccessNode methodReference = pythonAttributeAccess(classReference, staticMethodCall.getMethodName());
+ List<PythonExpressionNode> arguments = generateExpressions(staticMethodCall.getArguments());
+
+ return PrimitiveMethods.getPrimitiveStaticMethod(className, staticMethodCall.getMethodName())
+ .map(generator -> generator.generate(arguments))
+ .orElseGet(() -> pythonCall(methodReference, arguments));
}
@Override
diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java
index c8f9932..d077736 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCompiler.java
@@ -17,7 +17,9 @@ import static org.zwobble.couscous.backends.python.PythonSerializer.serialize;
public class PythonCompiler {
private static final List<String> RUNTIME_FILES = asList(
- "java.lang.Integer", "_couscous");
+ "java.lang.Object",
+ "java.lang.Integer",
+ "_couscous");
private final Path root;
private final String packageName;
diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java b/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java
index eadb545..2fb341f 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/PythonSerializer.java
@@ -4,6 +4,7 @@ import java.util.List;
import java.util.function.Consumer;
import org.zwobble.couscous.backends.python.ast.PythonAssignmentNode;
import org.zwobble.couscous.backends.python.ast.PythonAttributeAccessNode;
+import org.zwobble.couscous.backends.python.ast.PythonBinaryOperation;
import org.zwobble.couscous.backends.python.ast.PythonBlock;
import org.zwobble.couscous.backends.python.ast.PythonBooleanLiteralNode;
import org.zwobble.couscous.backends.python.ast.PythonCallNode;
@@ -34,7 +35,7 @@ public class PythonSerializer implements PythonNodeVisitor {
return writer.asString();
}
- private PythonWriter writer;
+ private final PythonWriter writer;
private PythonSerializer(PythonWriter writer) {
this.writer = writer;
@@ -111,6 +112,15 @@ public class PythonSerializer implements PythonNodeVisitor {
writer.writeSpace();
writeParenthesised(notOperation.getOperand());
}
+
+ @Override
+ public void visit(PythonBinaryOperation operation) {
+ writeParenthesised(operation.getLeft());
+ writer.writeSpace();
+ writer.writeKeyword(operation.getOperator());
+ writer.writeSpace();
+ writeParenthesised(operation.getRight());
+ }
@Override
public void visit(PythonReturnNode pythonReturn) {
diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonBinaryOperation.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonBinaryOperation.java
new file mode 100644
index 0000000..1fc87c3
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonBinaryOperation.java
@@ -0,0 +1,39 @@
+package org.zwobble.couscous.backends.python.ast;
+
+import org.zwobble.couscous.backends.python.ast.visitors.PythonNodeVisitor;
+
+public class PythonBinaryOperation implements PythonExpressionNode {
+ public static PythonExpressionNode pythonIs(PythonExpressionNode left, PythonExpressionNode right) {
+ return new PythonBinaryOperation("is", left, right);
+ }
+
+ private final String operator;
+ private final PythonExpressionNode left;
+ private final PythonExpressionNode right;
+
+ public PythonBinaryOperation(
+ String operator,
+ PythonExpressionNode left,
+ PythonExpressionNode right) {
+ this.operator = operator;
+ this.left = left;
+ this.right = right;
+ }
+
+ public String getOperator() {
+ return operator;
+ }
+
+ public PythonExpressionNode getLeft() {
+ return left;
+ }
+
+ public PythonExpressionNode getRight() {
+ return right;
+ }
+
+ @Override
+ public void accept(PythonNodeVisitor visitor) {
+ visitor.visit(this);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java b/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java
index 59a1fe9..a1ed7d5 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/ast/visitors/PythonNodeVisitor.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.backends.python.ast.visitors;
import org.zwobble.couscous.backends.python.ast.PythonAssignmentNode;
import org.zwobble.couscous.backends.python.ast.PythonAttributeAccessNode;
+import org.zwobble.couscous.backends.python.ast.PythonBinaryOperation;
import org.zwobble.couscous.backends.python.ast.PythonBooleanLiteralNode;
import org.zwobble.couscous.backends.python.ast.PythonCallNode;
import org.zwobble.couscous.backends.python.ast.PythonClassNode;
@@ -27,6 +28,7 @@ public interface PythonNodeVisitor {
void visit(PythonCallNode call);
void visit(PythonGetSliceNode getSlice);
void visit(PythonNotNode notOperation);
+ void visit(PythonBinaryOperation operation);
void visit(PythonReturnNode pythonReturn);
void visit(PythonPassNode pass);
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 2e1be0c..601e565 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
@@ -243,19 +243,36 @@ public class JavaReader {
String methodName = expression.getName().getIdentifier();
@SuppressWarnings("unchecked")
List<ExpressionNode> arguments = readArguments(expression.arguments());
+ final TypeName type = typeOf(expression);
if (expression.getExpression() == null) {
IMethodBinding methodBinding = expression.resolveMethodBinding();
TypeName receiverType = typeOf(methodBinding.getDeclaringClass());
if ((methodBinding.getModifiers() & Modifier.STATIC) != 0) {
- return StaticMethodCallNode.staticMethodCall(receiverType, methodName, arguments);
+ return StaticMethodCallNode.staticMethodCall(
+ receiverType,
+ methodName,
+ arguments,
+ type);
} else {
- return MethodCallNode.methodCall(ThisReferenceNode.thisReference(receiverType), methodName, arguments, typeOf(expression));
+ return MethodCallNode.methodCall(
+ ThisReferenceNode.thisReference(receiverType),
+ methodName,
+ arguments,
+ type);
}
} else if (expression.getExpression().getNodeType() == ASTNode.SIMPLE_NAME) {
Expression receiver = expression.getExpression();
- return StaticMethodCallNode.staticMethodCall(typeOf(receiver), methodName, arguments);
+ return StaticMethodCallNode.staticMethodCall(
+ typeOf(receiver),
+ methodName,
+ arguments,
+ type);
} else {
- return MethodCallNode.methodCall(readExpression(expression.getExpression()), methodName, arguments, typeOf(expression));
+ return MethodCallNode.methodCall(
+ readExpression(expression.getExpression()),
+ methodName,
+ arguments,
+ type);
}
}
@@ -274,15 +291,26 @@ public class JavaReader {
private static ExpressionNode readInfixExpression(InfixExpression expression) {
final ExpressionNode left = readExpression(expression.getLeftOperand());
final ExpressionNode right = readExpression(expression.getRightOperand());
- if (expression.getOperator() == Operator.NOT_EQUALS) {
- return MethodCallNode.not(methodCall(left, "equals", asList(right), BooleanValue.REF));
+
+ if (left.getType().equals(IntegerValue.REF)) {
+ if (expression.getOperator() == Operator.NOT_EQUALS) {
+ return MethodCallNode.not(methodCall(left, "equals", asList(right), BooleanValue.REF));
+ } else {
+ JavaOperator operator = readOperator(expression.getOperator());
+ return MethodCallNode.methodCall(
+ left,
+ operator.methodName,
+ asList(right),
+ operator.returnValue);
+ }
} else {
- JavaOperator operator = readOperator(expression.getOperator());
- return MethodCallNode.methodCall(
- left,
- operator.methodName,
- asList(right),
- operator.returnValue);
+ if (expression.getOperator() == Operator.EQUALS) {
+ return StaticMethodCallNode.same(left, right);
+ } else if (expression.getOperator() == Operator.NOT_EQUALS) {
+ return MethodCallNode.not(StaticMethodCallNode.same(left, right));
+ } else {
+ throw new IllegalArgumentException("Unsupported operator: " + expression.getOperator());
+ }
}
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
index e5f6482..95510cd 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
@@ -2,14 +2,12 @@ package org.zwobble.couscous.interpreter;
import java.util.List;
import java.util.Optional;
-import java.util.stream.Collectors;
+
import org.zwobble.couscous.ast.TypeName;
-import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.interpreter.values.ConcreteType;
import org.zwobble.couscous.interpreter.values.InterpreterValue;
+
import com.google.common.collect.ImmutableMap;
-import static org.zwobble.couscous.ast.LiteralNode.literal;
-import static org.zwobble.couscous.ast.StaticMethodCallNode.staticMethodCall;
-import static org.zwobble.couscous.interpreter.Evaluator.eval;
public class Interpreter {
private Project project;
@@ -19,14 +17,12 @@ public class Interpreter {
}
public InterpreterValue run(TypeName className, String methodName, List<InterpreterValue> arguments) {
- List<ExpressionNode> argumentExpressions = arguments.stream()
- .map(argument -> literal(argument.toPrimitiveValue().get()))
- .collect(Collectors.toList());
- return eval(
- new Environment(
- project,
- Optional.empty(),
- ImmutableMap.of()),
- staticMethodCall(className, methodName, argumentExpressions));
+ final Environment environment = new Environment(
+ project,
+ Optional.empty(),
+ ImmutableMap.of());
+
+ ConcreteType clazz = environment.findClass(className);
+ return clazz.callStaticMethod(environment, methodName, arguments);
}
} \ No newline at end of file
diff --git a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
index e6b6df1..50e4508 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
@@ -5,12 +5,18 @@ import java.util.List;
import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.interpreter.values.BoxedIntegerInterpreterValue;
import org.zwobble.couscous.interpreter.values.ConcreteType;
+import org.zwobble.couscous.interpreter.values.InternalCouscousInterpeterValue;
import com.google.common.collect.Iterables;
public class JavaProject {
+ private static final ConcreteType OBJECT_TYPE = ConcreteType.classBuilder("java.lang.Object")
+ .build();
+
public static MapBackedProject.Builder builder() {
return MapBackedProject.builder()
+ .addClass(InternalCouscousInterpeterValue.TYPE)
+ .addClass(OBJECT_TYPE)
.addClass(BoxedIntegerInterpreterValue.TYPE);
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java
new file mode 100644
index 0000000..30645d6
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java
@@ -0,0 +1,14 @@
+package org.zwobble.couscous.interpreter.values;
+
+import org.zwobble.couscous.values.ObjectValue;
+
+import static java.util.Arrays.asList;
+
+public class InternalCouscousInterpeterValue {
+ public static final ConcreteType TYPE = ConcreteType.classBuilder("_couscous")
+ .staticMethod("same", asList(ObjectValue.REF, ObjectValue.REF),
+ (environment, arguments) ->
+ new BooleanInterpreterValue(arguments.get(0) == arguments.get(1)))
+
+ .build();
+}
diff --git a/src/main/java/org/zwobble/couscous/values/InternalCouscousValue.java b/src/main/java/org/zwobble/couscous/values/InternalCouscousValue.java
new file mode 100644
index 0000000..c08104f
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/values/InternalCouscousValue.java
@@ -0,0 +1,7 @@
+package org.zwobble.couscous.values;
+
+import org.zwobble.couscous.ast.TypeName;
+
+public class InternalCouscousValue {
+ public static final TypeName REF = TypeName.of("_couscous");
+}
diff --git a/src/main/java/org/zwobble/couscous/values/ObjectValue.java b/src/main/java/org/zwobble/couscous/values/ObjectValue.java
new file mode 100644
index 0000000..ea59ae0
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/values/ObjectValue.java
@@ -0,0 +1,7 @@
+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/resources/python/runtime/java/lang/Object.py b/src/main/resources/python/runtime/java/lang/Object.py
new file mode 100644
index 0000000..29a8fba
--- /dev/null
+++ b/src/main/resources/python/runtime/java/lang/Object.py
@@ -0,0 +1 @@
+Object = object
diff --git a/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java b/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
index 63ac1a9..cac2fe0 100644
--- a/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
@@ -1,5 +1,6 @@
package org.zwobble.couscous.tests;
+import java.util.Collections;
import java.util.List;
import org.junit.Test;
@@ -7,6 +8,7 @@ import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.ast.ExpressionNode;
import org.zwobble.couscous.ast.FormalArgumentNode;
import org.zwobble.couscous.ast.MethodCallNode;
+import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.values.BooleanValue;
import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.PrimitiveValue;
@@ -24,6 +26,7 @@ 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.ReturnNode.returns;
+import static org.zwobble.couscous.ast.StaticMethodCallNode.same;
import static org.zwobble.couscous.ast.StaticMethodCallNode.staticMethodCall;
import static org.zwobble.couscous.ast.TernaryConditionalNode.ternaryConditional;
import static org.zwobble.couscous.ast.VariableDeclaration.var;
@@ -41,6 +44,15 @@ public abstract class BackendEvalTests {
}
@Test
+ public void equalityOnReferenceTypesChecksForIdentity() {
+ assertEquals(
+ value(false),
+ evalExpression(same(
+ constructorCall(TypeName.of("java.lang.Object"), Collections.emptyList()),
+ constructorCall(TypeName.of("java.lang.Object"), Collections.emptyList()))));
+ }
+
+ @Test
public void canEvaluateOperationsOnBooleans() {
assertEquals(
value(true),
@@ -136,8 +148,11 @@ public abstract class BackendEvalTests {
@Test
public void canCallBuiltinStaticMethod() {
- PrimitiveValue result = evalExpression(
- staticMethodCall("java.lang.Integer", "parseInt", literal("42")));
+ PrimitiveValue result = evalExpression(staticMethodCall(
+ "java.lang.Integer",
+ "parseInt",
+ asList(literal("42")),
+ IntegerValue.REF));
assertEquals(value(42), result);
}
@@ -145,10 +160,14 @@ public abstract class BackendEvalTests {
public void canCallStaticMethodFromUserDefinedStaticMethod() {
ClassNode classNode = ClassNode.builder("com.example.Example")
.staticMethod("main", method -> method
- .statement(returns(staticMethodCall("java.lang.Integer", "parseInt", literal("42")))))
+ .statement(returns(staticMethodCall(
+ "java.lang.Integer",
+ "parseInt",
+ asList(literal("42")),
+ IntegerValue.REF))))
.build();
PrimitiveValue result = evalExpression(asList(classNode),
- staticMethodCall("com.example.Example", "main"));
+ staticMethodCall("com.example.Example", "main", asList(), IntegerValue.REF));
assertEquals(value(42), result);
}
diff --git a/src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java b/src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java
index 29a5e2a..7493b55 100644
--- a/src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/JavaToPythonTests.java
@@ -30,6 +30,11 @@ public class JavaToPythonTests {
assertEquals(value(false), evalExpression("1 != 1"));
}
+ @Test
+ public void equalityOnReferenceTypesChecksForIdentity() {
+ assertEquals(value(false), evalExpression("new Object() == new Object()"));
+ }
+
private PrimitiveValue evalExpression(String expressionSource) {
try {
final java.lang.String javaClass = "package com.example;" + "public class Example {" + " public static Object main() {" + " return " + expressionSource + ";" + " }" + "}";
diff --git a/src/test/java/org/zwobble/couscous/tests/backends/python/PythonSerializerTests.java b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonSerializerTests.java
index f8956e5..0f21758 100644
--- a/src/test/java/org/zwobble/couscous/tests/backends/python/PythonSerializerTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/backends/python/PythonSerializerTests.java
@@ -10,6 +10,7 @@ import static org.junit.Assert.assertEquals;
import static org.zwobble.couscous.backends.python.PythonSerializer.serialize;
import static org.zwobble.couscous.backends.python.ast.PythonAssignmentNode.pythonAssignment;
import static org.zwobble.couscous.backends.python.ast.PythonAttributeAccessNode.pythonAttributeAccess;
+import static org.zwobble.couscous.backends.python.ast.PythonBinaryOperation.pythonIs;
import static org.zwobble.couscous.backends.python.ast.PythonBooleanLiteralNode.pythonBooleanLiteral;
import static org.zwobble.couscous.backends.python.ast.PythonCallNode.pythonCall;
import static org.zwobble.couscous.backends.python.ast.PythonConditionalExpressionNode.pythonConditionalExpression;
@@ -75,6 +76,13 @@ public class PythonSerializerTests {
}
@Test
+ public void binaryOperationIsSerializedUsingParenthesisedSubExpressions() {
+ assertEquals(
+ "(x) is (y)",
+ serialize(pythonIs(pythonVariableReference("x"), pythonVariableReference("y"))));
+ }
+
+ @Test
public void returnKeywordIsUsedForReturns() {
String output = serialize(pythonReturn(pythonIntegerLiteral(42)));
assertEquals("return 42\n", output);
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 c4e3791..7a6aab1 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
@@ -2,6 +2,7 @@ package org.zwobble.couscous.tests.frontends.java;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.List;
import org.junit.Test;
@@ -32,6 +33,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.same;
import static org.zwobble.couscous.ast.StaticMethodCallNode.staticMethodCall;
import static org.zwobble.couscous.ast.TernaryConditionalNode.ternaryConditional;
import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference;
@@ -117,7 +119,11 @@ public class JavaReaderTests {
@Test
public void canReadStaticMethodCalls() {
assertEquals(
- staticMethodCall(TypeName.of("java.lang.Integer"), "parseInt", asList(literal("42"))),
+ staticMethodCall(
+ TypeName.of("java.lang.Integer"),
+ "parseInt",
+ asList(literal("42")),
+ IntegerValue.REF),
readExpression("Integer.parseInt(\"42\")"));
}
@@ -133,7 +139,8 @@ public class JavaReaderTests {
staticMethodCall(
TypeName.of("com.example.Example"),
"loop",
- asList()),
+ asList(),
+ StringValue.REF),
returnNode.getValue());
}
@@ -145,6 +152,21 @@ public class JavaReaderTests {
}
@Test
+ public void canUseOperatorsOnReferences() {
+ assertEquals(
+ same(
+ constructorCall(TypeName.of("java.lang.Object"), Collections.emptyList()),
+ constructorCall(TypeName.of("java.lang.Object"), Collections.emptyList())),
+ readExpression("new Object() == new Object()"));
+
+ assertEquals(
+ not(same(
+ constructorCall(TypeName.of("java.lang.Object"), Collections.emptyList()),
+ constructorCall(TypeName.of("java.lang.Object"), Collections.emptyList()))),
+ readExpression("new Object() != new Object()"));
+ }
+
+ @Test
public void canUseOperatorsOnIntegers() {
assertEquals(
methodCall(literal(1), "add", asList(literal(2)), IntegerValue.REF),
@@ -246,7 +268,8 @@ public class JavaReaderTests {
staticMethodCall(
TypeName.of("java.lang.Integer"),
"parseInt",
- asList(literal("42")))),
+ asList(literal("42")),
+ IntegerValue.REF)),
readStatement("Integer.parseInt(\"42\");"));
}