From ad4903f04c757779b2e9a875298ef8418cb140b4 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 31 Oct 2015 16:07:19 +0000 Subject: Support equality on reference types --- .../zwobble/couscous/ast/StaticMethodCallNode.java | 42 +++++++++++------ .../couscous/backends/python/PrimitiveMethods.java | 24 ++++++++++ .../backends/python/PythonCodeGenerator.java | 19 +++++--- .../couscous/backends/python/PythonCompiler.java | 4 +- .../couscous/backends/python/PythonSerializer.java | 12 ++++- .../backends/python/ast/PythonBinaryOperation.java | 39 ++++++++++++++++ .../python/ast/visitors/PythonNodeVisitor.java | 2 + .../couscous/frontends/java/JavaReader.java | 52 +++++++++++++++++----- .../zwobble/couscous/interpreter/Interpreter.java | 24 +++++----- .../zwobble/couscous/interpreter/JavaProject.java | 6 +++ .../values/InternalCouscousInterpeterValue.java | 14 ++++++ .../couscous/values/InternalCouscousValue.java | 7 +++ .../org/zwobble/couscous/values/ObjectValue.java | 7 +++ 13 files changed, 205 insertions(+), 47 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/backends/python/ast/PythonBinaryOperation.java create mode 100644 src/main/java/org/zwobble/couscous/interpreter/values/InternalCouscousInterpeterValue.java create mode 100644 src/main/java/org/zwobble/couscous/values/InternalCouscousValue.java create mode 100644 src/main/java/org/zwobble/couscous/values/ObjectValue.java (limited to 'src/main/java/org') 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 arguments) { - return staticMethodCall(TypeName.of(className), methodName, arguments); + public static StaticMethodCallNode staticMethodCall( + String className, + String methodName, + List arguments, + TypeName type) { + return staticMethodCall(TypeName.of(className), methodName, arguments, type); } public static StaticMethodCallNode staticMethodCall( TypeName className, String methodName, - List arguments) { - return new StaticMethodCallNode(className, methodName, arguments); + List arguments, + TypeName type) { + return new StaticMethodCallNode(className, methodName, arguments, type); } private final TypeName className; private final String methodName; private final List arguments; + private final TypeName type; public StaticMethodCallNode( TypeName className, String methodName, - List arguments) { + List 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 accept(ExpressionNodeMapper 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 INTERNAL_METHODS = + ImmutableMap.builder() + + .put("same", arguments -> pythonIs(arguments.get(0), arguments.get(1))) + + .build(); + + private static final Map> STATIC_METHODS = + ImmutableMap.>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 arguments); } + + public static Optional 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 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 generateImports(ClassNode classNode) { Set 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 arguments = generateExpressions(staticMethodCall.getArguments()); - return pythonCall(methodReference, arguments); + TypeName className = staticMethodCall.getClassName(); + PythonVariableReferenceNode classReference = pythonVariableReference(className.getSimpleName()); + PythonAttributeAccessNode methodReference = pythonAttributeAccess(classReference, staticMethodCall.getMethodName()); + List 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 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 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 arguments) { - List 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"); +} -- cgit v1.2.3