diff options
| author | Michael Williamson <mike@zwobble.org> | 2016-03-09 22:44:29 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2016-03-10 16:43:05 +0000 |
| commit | 2a182825a88b6ee957e205862f5cf3a505b3a173 (patch) | |
| tree | 2054c8a0c4252bd4b3f161de0b34482158ee7728 | |
| parent | 20dc1e124d606cf4298d5112b6d3c4f6c74e9b68 (diff) | |
Initial implementation of generics for C#
17 files changed, 444 insertions, 46 deletions
@@ -1,5 +1,7 @@ ## TODO +* Do this references work for anonymous classes? + * Type names for generics * Type erasure diff --git a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java index bb38562..df2b87c 100644 --- a/src/main/java/org/zwobble/couscous/ast/LiteralNode.java +++ b/src/main/java/org/zwobble/couscous/ast/LiteralNode.java @@ -1,10 +1,13 @@ package org.zwobble.couscous.ast; +import org.zwobble.couscous.ast.types.ParameterizedType; import org.zwobble.couscous.ast.types.ScalarType; +import org.zwobble.couscous.ast.types.Type; import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.values.*; +import static org.zwobble.couscous.util.ExtraLists.list; import static org.zwobble.couscous.values.PrimitiveValues.value; public class LiteralNode implements ExpressionNode { @@ -27,22 +30,22 @@ public class LiteralNode implements ExpressionNode { } public static LiteralNode literal(ScalarType type) { - return new LiteralNode(value(type), ObjectValues.CLASS); + return new LiteralNode(value(type), new ParameterizedType(ObjectValues.CLASS, list(type))); } - public static LiteralNode of(PrimitiveValue value, ScalarType type) { + public static LiteralNode of(PrimitiveValue value, Type type) { return new LiteralNode(value, type); } private final PrimitiveValue value; - private final ScalarType type; + private final Type type; - private LiteralNode(PrimitiveValue value, ScalarType type) { + private LiteralNode(PrimitiveValue value, Type type) { this.value = value; this.type = type; } - public ScalarType getType() { + public Type getType() { return type; } diff --git a/src/main/java/org/zwobble/couscous/ast/types/BoundTypeParameter.java b/src/main/java/org/zwobble/couscous/ast/types/BoundTypeParameter.java new file mode 100644 index 0000000..abb8906 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/types/BoundTypeParameter.java @@ -0,0 +1,55 @@ +package org.zwobble.couscous.ast.types; + +public class BoundTypeParameter implements Type { + public static BoundTypeParameter boundTypeParameter(TypeParameter parameter, Type value) { + return new BoundTypeParameter(parameter, value); + } + + private final TypeParameter parameter; + private final Type value; + + public BoundTypeParameter(TypeParameter parameter, Type value) { + this.parameter = parameter; + this.value = value; + } + + public TypeParameter getParameter() { + return parameter; + } + + public Type getValue() { + return value; + } + + @Override + public <T> T accept(Visitor<T> visitor) { + return visitor.visit(this); + } + + @Override + public String toString() { + return "BoundTypeParameter(" + + "parameter=" + parameter + + ", value=" + value + + ')'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BoundTypeParameter that = (BoundTypeParameter) o; + + if (!parameter.equals(that.parameter)) return false; + return value.equals(that.value); + + } + + @Override + public int hashCode() { + int result = parameter.hashCode(); + result = 31 * result + value.hashCode(); + return result; + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/types/ParameterizedType.java b/src/main/java/org/zwobble/couscous/ast/types/ParameterizedType.java new file mode 100644 index 0000000..a23fdc0 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/types/ParameterizedType.java @@ -0,0 +1,57 @@ +package org.zwobble.couscous.ast.types; + +import java.util.List; + +public class ParameterizedType implements Type { + public static ParameterizedType parameterizedType(ScalarType rawType, List<Type> parameters) { + return new ParameterizedType(rawType, parameters); + } + + private final ScalarType rawType; + private final List<Type> parameters; + + public ParameterizedType(ScalarType rawType, List<Type> parameters) { + this.rawType = rawType; + this.parameters = parameters; + } + + public ScalarType getRawType() { + return rawType; + } + + public List<Type> getParameters() { + return parameters; + } + + @Override + public <T> T accept(Visitor<T> visitor) { + return visitor.visit(this); + } + + @Override + public String toString() { + return "ParameterizedType(" + + "rawType=" + rawType + + ", parameters=" + parameters + + ')'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ParameterizedType that = (ParameterizedType) o; + + if (!rawType.equals(that.rawType)) return false; + return parameters.equals(that.parameters); + + } + + @Override + public int hashCode() { + int result = rawType.hashCode(); + result = 31 * result + parameters.hashCode(); + return result; + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/types/Type.java b/src/main/java/org/zwobble/couscous/ast/types/Type.java index e936279..2a64341 100644 --- a/src/main/java/org/zwobble/couscous/ast/types/Type.java +++ b/src/main/java/org/zwobble/couscous/ast/types/Type.java @@ -5,5 +5,8 @@ public interface Type { interface Visitor<T> { T visit(ScalarType type); + T visit(TypeParameter parameter); + T visit(ParameterizedType type); + T visit(BoundTypeParameter type); } } diff --git a/src/main/java/org/zwobble/couscous/ast/types/TypeParameter.java b/src/main/java/org/zwobble/couscous/ast/types/TypeParameter.java new file mode 100644 index 0000000..0dee75e --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/types/TypeParameter.java @@ -0,0 +1,51 @@ +package org.zwobble.couscous.ast.types; + +public class TypeParameter implements Type { + public static TypeParameter typeParameter(ScalarType declaringType, String name) { + return new TypeParameter(declaringType, name); + } + + private final ScalarType declaringType; + private final String name; + + public TypeParameter(ScalarType declaringType, String name) { + this.declaringType = declaringType; + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public <T> T accept(Visitor<T> visitor) { + return visitor.visit(this); + } + + @Override + public String toString() { + return "TypeParameter(" + + "declaringType=" + declaringType + + ", name=" + name + + ')'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TypeParameter that = (TypeParameter) o; + + if (!declaringType.equals(that.declaringType)) return false; + return name.equals(that.name); + + } + + @Override + public int hashCode() { + int result = declaringType.hashCode(); + result = 31 * result + name.hashCode(); + return result; + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/types/Types.java b/src/main/java/org/zwobble/couscous/ast/types/Types.java index c222c54..5fb079b 100644 --- a/src/main/java/org/zwobble/couscous/ast/types/Types.java +++ b/src/main/java/org/zwobble/couscous/ast/types/Types.java @@ -1,5 +1,7 @@ package org.zwobble.couscous.ast.types; +import org.zwobble.couscous.values.ObjectValues; + public class Types { public static ScalarType erasure(Type type) { return type.accept(new Type.Visitor<ScalarType>() { @@ -7,6 +9,21 @@ public class Types { public ScalarType visit(ScalarType type) { return type; } + + @Override + public ScalarType visit(TypeParameter parameter) { + return ObjectValues.OBJECT; + } + + @Override + public ScalarType visit(ParameterizedType type) { + return type.getRawType(); + } + + @Override + public ScalarType visit(BoundTypeParameter type) { + return erasure(type.getParameter()); + } }); } } diff --git a/src/main/java/org/zwobble/couscous/backends/Names.java b/src/main/java/org/zwobble/couscous/backends/Names.java index 094de02..1e9600a 100644 --- a/src/main/java/org/zwobble/couscous/backends/Names.java +++ b/src/main/java/org/zwobble/couscous/backends/Names.java @@ -3,10 +3,9 @@ package org.zwobble.couscous.backends; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; import org.zwobble.couscous.ast.MethodSignature; -import org.zwobble.couscous.ast.types.Type; +import org.zwobble.couscous.ast.types.*; import static com.google.common.collect.Iterables.transform; -import static org.zwobble.couscous.ast.types.Types.erasure; import static org.zwobble.couscous.util.ExtraLists.list; public class Names { @@ -18,6 +17,26 @@ public class Names { } private static String typeToString(Type argument) { - return erasure(argument).getQualifiedName().replace('.', '_'); + return argument.accept(new Type.Visitor<String>() { + @Override + public String visit(ScalarType type) { + return type.getQualifiedName().replace('.', '_'); + } + + @Override + public String visit(TypeParameter parameter) { + return parameter.getName(); + } + + @Override + public String visit(ParameterizedType type) { + return typeToString(type.getRawType()); + } + + @Override + public String visit(BoundTypeParameter type) { + return typeToString(type.getParameter()); + } + }); } } diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java index 8388421..469705b 100644 --- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java @@ -2,8 +2,7 @@ package org.zwobble.couscous.backends.csharp; import com.google.common.collect.ImmutableMap; import org.zwobble.couscous.ast.*; -import org.zwobble.couscous.ast.types.ScalarType; -import org.zwobble.couscous.ast.types.Type; +import org.zwobble.couscous.ast.types.*; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.backends.Names; import org.zwobble.couscous.values.*; @@ -13,6 +12,7 @@ import java.util.Optional; import static org.zwobble.couscous.ast.types.Types.erasure; import static org.zwobble.couscous.util.Casts.tryCast; +import static org.zwobble.couscous.util.ExtraLists.eagerMap; public class CsharpCodeGenerator { private final static Map<ScalarType, ScalarType> PRIMITIVES = ImmutableMap.<ScalarType, ScalarType>builder() @@ -44,11 +44,39 @@ public class CsharpCodeGenerator { } private Type transformType(Type type) { - if (PRIMITIVES.containsKey(type)) { - return PRIMITIVES.get(type); - } else { - return addPrefix(type, namespace); - } + return type.accept(new Type.Visitor<Type>() { + @Override + public Type visit(ScalarType type) { + if (PRIMITIVES.containsKey(type)) { + return PRIMITIVES.get(type); + } else { + return addPrefix(type, namespace); + } + } + + @Override + public TypeParameter visit(TypeParameter parameter) { + return parameter; + } + + @Override + public Type visit(ParameterizedType type) { + if (type.getRawType().equals(ObjectValues.CLASS)) { + return ScalarType.of("System.Type"); + } else { + return new ParameterizedType( + (ScalarType) transformType(type.getRawType()), + eagerMap(type.getParameters(), parameter -> transformType(parameter))); + } + } + + @Override + public Type visit(BoundTypeParameter type) { + return new BoundTypeParameter( + visit(type.getParameter()), + transformType(type.getValue())); + } + }); } private String transformMethodName(MethodSignature signature) { @@ -81,9 +109,28 @@ public class CsharpCodeGenerator { private Type addPrefix(Type type, String namespace) { return type.accept(new Type.Visitor<Type>() { @Override - public Type visit(ScalarType type) { + public ScalarType visit(ScalarType type) { return ScalarType.of(namespace + "." + type.getQualifiedName()); } + + @Override + public TypeParameter visit(TypeParameter parameter) { + return parameter; + } + + @Override + public Type visit(ParameterizedType type) { + return new ParameterizedType( + visit(type.getRawType()), + eagerMap(type.getParameters(), parameter -> addPrefix(parameter, namespace))); + } + + @Override + public Type visit(BoundTypeParameter type) { + return new BoundTypeParameter( + visit(type.getParameter()), + addPrefix(type.getValue(), namespace)); + } }); } } diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java index c898525..0d38a88 100644 --- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java +++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java @@ -1,8 +1,7 @@ package org.zwobble.couscous.backends.csharp; import org.zwobble.couscous.ast.*; -import org.zwobble.couscous.ast.types.ScalarType; -import org.zwobble.couscous.ast.types.Type; +import org.zwobble.couscous.ast.types.*; import org.zwobble.couscous.ast.visitors.NodeVisitor; import org.zwobble.couscous.backends.SourceCodeWriter; import org.zwobble.couscous.util.Action; @@ -422,6 +421,34 @@ public class CsharpSerializer implements NodeVisitor { writer.writeIdentifier(typeReference(type)); return null; } + + @Override + public Void visit(TypeParameter parameter) { + writer.writeIdentifier(parameter.getName()); + return null; + } + + @Override + public Void visit(ParameterizedType type) { + writer.writeIdentifier(typeReference(type.getRawType())); + writer.writeSymbol("<"); + writer.writeWithSeparator( + type.getParameters(), + parameter -> writeTypeReference(parameter), + () -> { + writer.writeSymbol(","); + writer.writeSpace(); + } + ); + writer.writeSymbol(">"); + return null; + } + + @Override + public Void visit(BoundTypeParameter type) { + writeTypeReference(type.getValue()); + return null; + } }); } diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java index 98f2686..72766b3 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java @@ -184,7 +184,8 @@ public class JavaExpressionReader { List<ExpressionNode> arguments = readArguments( expression.resolveMethodBinding(), expression.arguments()); - final Type type = typeOf(expression); + MethodSignature signature = JavaReader.signature(expression.resolveMethodBinding()); + final Type type = signature.getReturnType(); IMethodBinding methodBinding = expression.resolveMethodBinding(); Type receiverType = typeOf(methodBinding.getDeclaringClass()); 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 950e678..382c592 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java @@ -1,6 +1,7 @@ package org.zwobble.couscous.frontends.java; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.*; @@ -8,8 +9,10 @@ import org.zwobble.couscous.ast.*; import org.zwobble.couscous.ast.sugar.AnonymousClass; import org.zwobble.couscous.ast.sugar.Lambda; import org.zwobble.couscous.ast.sugar.TypeDeclarationBody; +import org.zwobble.couscous.ast.types.BoundTypeParameter; import org.zwobble.couscous.ast.types.ScalarType; import org.zwobble.couscous.ast.types.Type; +import org.zwobble.couscous.ast.types.TypeParameter; import org.zwobble.couscous.ast.visitors.NodeTransformer; import org.zwobble.couscous.util.ExtraLists; @@ -17,6 +20,7 @@ import java.io.IOException; import java.nio.file.Path; import java.util.*; import java.util.function.Function; +import java.util.stream.Collectors; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.*; @@ -29,8 +33,10 @@ import static org.zwobble.couscous.ast.FieldAccessNode.fieldAccess; import static org.zwobble.couscous.ast.FieldDeclarationNode.field; import static org.zwobble.couscous.ast.FormalTypeParameterNode.formalTypeParameter; import static org.zwobble.couscous.ast.InstanceReceiver.instanceReceiver; +import static org.zwobble.couscous.ast.ReturnNode.returns; import static org.zwobble.couscous.ast.StaticReceiver.staticReceiver; import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference; +import static org.zwobble.couscous.ast.TypeCoercionNode.typeCoercion; import static org.zwobble.couscous.ast.VariableReferenceNode.reference; import static org.zwobble.couscous.ast.types.Types.erasure; import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariables; @@ -128,10 +134,14 @@ public class JavaReader { ScalarType name = generateAnonymousName(expression); Scope scope = outerScope.enterClass(name); IMethodBinding functionalInterfaceMethod = expression.resolveTypeBinding().getFunctionalInterfaceMethod(); - return generateClosure(scope, name, toAnonymousClass(functionalInterfaceMethod, lambda.apply(scope))); + return generateClosure(scope, name, toAnonymousClass(scope, name, functionalInterfaceMethod, lambda.apply(scope))); } - private AnonymousClass toAnonymousClass(IMethodBinding functionalInterfaceMethod, Lambda lambda) { + private AnonymousClass toAnonymousClass(Scope scope, ScalarType name, IMethodBinding functionalInterfaceMethod, Lambda lambda) { + // TODO: does this need type coercion adding? Since the body is straight from the lambda, but the type signature is from the interface method + // TODO: can generate override methods by using a placeholder this reference for, well, this references. + // They can be replaced once the anonymous class is turned into a real class. + // Or perhaps we should just do the conversion directly? Since we (in practice) already know the real class name. MethodNode method = MethodNode.method( emptyList(), false, @@ -140,10 +150,12 @@ public class JavaReader { typeOf(functionalInterfaceMethod.getReturnType()), Optional.of(lambda.getBody())); + Iterable<MethodNode> overrides = generateOverrideMethods(name, functionalInterfaceMethod, scope, method); + return new AnonymousClass( superTypesAndSelf(functionalInterfaceMethod.getDeclaringClass()), list(), - list(method)); + ExtraLists.cons(method, overrides)); } private List<StatementNode> replaceCaptureReferences( @@ -305,7 +317,7 @@ public class JavaReader { for (Object declaration : bodyDeclarations) { tryCast(MethodDeclaration.class, declaration) - .ifPresent(method -> readMethod(body, scope, method)); + .ifPresent(method -> readMethod(body, scope, type, method)); tryCast(Initializer.class, declaration) .ifPresent(initializer -> body.addInitializer( @@ -356,8 +368,9 @@ public class JavaReader { } - private void readMethod(TypeDeclarationBody.Builder builder, Scope outerScope, MethodDeclaration method) { - Scope scope = outerScope.enterMethod(method.getName().getIdentifier()); + private void readMethod(TypeDeclarationBody.Builder builder, Scope outerScope, ScalarType type, MethodDeclaration method) { + String methodName = method.getName().getIdentifier(); + Scope scope = outerScope.enterMethod(methodName); List<FormalArgumentNode> formalArguments = readFormalArguments(scope, method); List<AnnotationNode> annotations = readAnnotations(method); @@ -370,16 +383,89 @@ public class JavaReader { formalArguments, body.get())); } else { - builder.addMethod(MethodNode.method( + boolean isStatic = Modifier.isStatic(method.getModifiers()); + MethodNode methodNode = MethodNode.method( annotations, - Modifier.isStatic(method.getModifiers()), - method.getName().getIdentifier(), + isStatic, + methodName, formalArguments, returnType.get(), - body)); + body); + builder.addMethod(methodNode); + + Iterable<MethodNode> overrideMethods = generateOverrideMethods(type, method.resolveBinding(), scope, methodNode); + + for (MethodNode overrideMethod : overrideMethods) { + builder.addMethod(overrideMethod); + } } } + private Iterable<MethodNode> generateOverrideMethods(ScalarType type, IMethodBinding method, Scope scope, MethodNode methodNode) { + return transform( + filter( + overrides(method), + override -> !override.equals(methodNode.signature())), + override -> { + List<FormalArgumentNode> arguments = eagerMap(override.getArguments(), argument -> scope.formalArgument("arg", argument)); + return MethodNode.method( + methodNode.getAnnotations(), + methodNode.isStatic(), + methodNode.getName(), + arguments, + override.getReturnType(), + Optional.of(list( + returns(typeCoercion( + MethodCallNode.methodCall( + thisReference(type), + methodNode.getName(), + eagerMap(arguments, argument -> reference(argument)), + methodNode.getReturnType()), + override.getReturnType())) + ))); + }); + } + + private Iterable<MethodSignature> overrides(IMethodBinding methodBinding) { + ITypeBinding declaringClass = methodBinding.getDeclaringClass(); + + // TODO: remove duplication with JavaTypes + // TODO: consider superclass + return ExtraLists.concat(list(declaringClass), asList(declaringClass.getInterfaces())) + .stream() + .flatMap(type -> asList(type.getDeclaredMethods()).stream().filter(superMethod -> methodBinding.overrides(superMethod))) + .map(JavaReader::signature) + .collect(Collectors.toList()); + } + + // TODO: move this elsewhere + static MethodSignature signature(IMethodBinding method) { + ITypeBinding declaringClass = method.getDeclaringClass(); + ITypeBinding erasedDeclaringClass = declaringClass.getErasure(); + + List<Type> argumentTypes = eagerMap(asList(method.getParameterTypes()), JavaTypes::typeOf); + Type returnType = typeOf(method.getReturnType()); + + if (declaringClass.isEqualTo(erasedDeclaringClass)) { + return new MethodSignature(method.getName(), argumentTypes, returnType); + } else { + IMethodBinding[] erasedMethods = erasedDeclaringClass.getDeclaredMethods(); + IMethodBinding erasedMethod = Iterables.getOnlyElement(Iterables.filter( + asList(erasedMethods), + m -> m.isSubsignature(method))); + return new MethodSignature( + method.getName(), + eagerMap( + argumentTypes, + asList(erasedMethod.getParameterTypes()), + (argumentType, erasedParameterType) -> new BoundTypeParameter((TypeParameter) typeOf(erasedParameterType), argumentType)), + new BoundTypeParameter( + (TypeParameter) typeOf(erasedMethod.getReturnType()), + returnType)); + } + } + + private List<AnnotationNode> readAnnotations(MethodDeclaration method) { return eagerMap(asList(method.resolveBinding().getAnnotations()), this::readAnnotation); } diff --git a/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java b/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java index fd59e8c..f0060a2 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java @@ -2,16 +2,20 @@ package org.zwobble.couscous.frontends.java; import com.google.common.collect.ImmutableSet; import org.eclipse.jdt.core.dom.*; +import org.zwobble.couscous.ast.types.ParameterizedType; import org.zwobble.couscous.ast.types.ScalarType; import org.zwobble.couscous.ast.types.Type; -import org.zwobble.couscous.ast.types.Types; +import org.zwobble.couscous.ast.types.TypeParameter; import org.zwobble.couscous.values.ObjectValues; +import java.util.List; import java.util.Optional; import java.util.Set; import static com.google.common.collect.Iterables.transform; import static java.util.Arrays.asList; +import static org.zwobble.couscous.ast.types.Types.erasure; +import static org.zwobble.couscous.util.ExtraLists.eagerMap; class JavaTypes { static Type typeOf(Expression expression) { @@ -29,9 +33,19 @@ class JavaTypes { static Type typeOf(ITypeBinding typeBinding) { ITypeBinding outerClass = typeBinding.getDeclaringClass(); if (outerClass == null) { - return ScalarType.of(typeBinding.getErasure().getQualifiedName()); + ScalarType rawType = ScalarType.of(typeBinding.getErasure().getQualifiedName()); + if (typeBinding.isParameterizedType()) { + List<Type> typeParameters = eagerMap( + asList(typeBinding.getTypeArguments()), + JavaTypes::typeOf); + return new ParameterizedType(rawType, typeParameters); + } else { + return rawType; + } + } else if (typeBinding.isTypeVariable()) { + return new TypeParameter(erasure(typeOf(outerClass)), typeBinding.getName()); } else { - return ScalarType.of(Types.erasure(typeOf(outerClass)).getQualifiedName() + "__" + typeBinding.getName()); + return ScalarType.of(erasure(typeOf(outerClass)).getQualifiedName() + "__" + typeBinding.getName()); } } diff --git a/src/main/java/org/zwobble/couscous/util/ExtraLists.java b/src/main/java/org/zwobble/couscous/util/ExtraLists.java index ce9230a..8485b02 100644 --- a/src/main/java/org/zwobble/couscous/util/ExtraLists.java +++ b/src/main/java/org/zwobble/couscous/util/ExtraLists.java @@ -1,8 +1,11 @@ package org.zwobble.couscous.util; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import java.util.Iterator; import java.util.List; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -26,16 +29,16 @@ public class ExtraLists { return ImmutableList.of(value1, value2, value3); } - public static <T> List<T> cons(T value, List<T> list) { - return Stream.concat(Stream.of(value), list.stream()).collect(Collectors.toList()); + public static <T> List<T> cons(T value, Iterable<T> list) { + return concat(list(value), list); } public static <T> List<T> append(List<T> list, T value) { return Stream.concat(list.stream(), Stream.of(value)).collect(Collectors.toList()); } - public static <T> List<T> concat(List<? extends T> first, List<? extends T> second) { - return Stream.concat(first.stream(), second.stream()).collect(Collectors.toList()); + public static <T> List<T> concat(Iterable<? extends T> first, Iterable<? extends T> second) { + return ImmutableList.copyOf(Iterables.concat(first, second)); } public static <T> List<T> concat(List<? extends T> first, List<? extends T> second, List<? extends T> third) { @@ -82,6 +85,24 @@ public class ExtraLists { .collect(Collectors.toList()); } + public static <T1, T2, R> List<R> eagerMap( + Iterable<T1> iterable1, + Iterable<T2> iterable2, + BiFunction<T1, T2, R> function) + { + // TODO: error handling when iterables are different lengths + Iterator<T1> iterator1 = iterable1.iterator(); + Iterator<T2> iterator2 = iterable2.iterator(); + + ImmutableList.Builder<R> result = ImmutableList.builder(); + + while (iterator1.hasNext()) { + result.add(function.apply(iterator1.next(), iterator2.next())); + } + + return result.build(); + } + public static <T, R> List<R> eagerFlatMap( Iterable<T> iterable, Function<T, Iterable<R>> function) { diff --git a/src/test/java/org/zwobble/couscous/tests/ValueObjectTests.java b/src/test/java/org/zwobble/couscous/tests/ValueObjectTests.java index f0780d5..7e17104 100644 --- a/src/test/java/org/zwobble/couscous/tests/ValueObjectTests.java +++ b/src/test/java/org/zwobble/couscous/tests/ValueObjectTests.java @@ -7,8 +7,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.zwobble.couscous.ast.*; import org.zwobble.couscous.ast.identifiers.Identifier; -import org.zwobble.couscous.ast.types.ScalarType; -import org.zwobble.couscous.ast.types.Type; +import org.zwobble.couscous.ast.types.*; import org.zwobble.couscous.util.ExtraArrays; import org.zwobble.couscous.values.PrimitiveValue; import org.zwobble.couscous.values.StringValue; @@ -44,6 +43,7 @@ public class ValueObjectTests { return asList(new Object[][] { {AnnotationNode.class}, {AssignmentNode.class}, + {BoundTypeParameter.class}, {CastNode.class}, {ClassNode.class}, {ConstructorCallNode.class}, @@ -61,12 +61,14 @@ public class ValueObjectTests { {MethodCallNode.class}, {MethodNode.class}, {OperationNode.class}, + {ParameterizedType.class}, {ReturnNode.class}, {ScalarType.class}, {StaticReceiver.class}, {TernaryConditionalNode.class}, {ThisReferenceNode.class}, {TypeCoercionNode.class}, + {TypeParameter.class}, {ScalarType.class}, {VariableDeclaration.class}, {VariableReferenceNode.class}, diff --git a/src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java b/src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java index 68d270d..ad0da0b 100644 --- a/src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java +++ b/src/test/java/org/zwobble/couscous/tests/backends/csharp/JavaToCsharpTests.java @@ -1,7 +1,5 @@ package org.zwobble.couscous.tests.backends.csharp; -import org.junit.Ignore; -import org.junit.Test; import org.zwobble.couscous.CouscousCompiler; import org.zwobble.couscous.ast.types.ScalarType; import org.zwobble.couscous.backends.csharp.CsharpBackend; @@ -18,11 +16,6 @@ import static org.zwobble.couscous.tests.util.ExtraFiles.deleteRecursively; import static org.zwobble.couscous.util.ExtraLists.list; public class JavaToCsharpTests extends CompilerTests { - @Test - @Ignore - public void genericInterfaces() throws Exception { - } - protected PrimitiveValue execProgram( Path directory, ScalarType type, diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java index e73b63b..7756215 100644 --- a/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java +++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/ExpressionReadingTests.java @@ -38,7 +38,7 @@ public class ExpressionReadingTests { assertEquals(literal(false), readBooleanExpression("false")); assertEquals(literal(42), readIntExpression("42")); assertEquals(literal('h'), readIntExpression("'h'")); - assertEquals(literal(StringValue.REF), readExpression("Class<?>", "String.class")); + assertEquals(literal(StringValue.REF), readExpression("Class<String>", "String.class")); } @Test |
