aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNode.java47
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java9
-rw-r--r--src/main/java/org/zwobble/couscous/ast/EnumNode.java10
-rw-r--r--src/main/java/org/zwobble/couscous/ast/InterfaceNode.java42
-rw-r--r--src/main/java/org/zwobble/couscous/ast/TypeNode.java2
-rw-r--r--src/main/java/org/zwobble/couscous/ast/sugar/TypeDeclarationBody.java22
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java3
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java2
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java17
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java7
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java70
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java4
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java1
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java167
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java10
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/JavaProject.java5
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java147
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java137
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java6
-rw-r--r--src/main/java/org/zwobble/couscous/types/BoundTypeParameter.java10
-rw-r--r--src/main/java/org/zwobble/couscous/types/ParameterizedType.java11
-rw-r--r--src/main/java/org/zwobble/couscous/types/ScalarType.java8
-rw-r--r--src/main/java/org/zwobble/couscous/types/Type.java4
-rw-r--r--src/main/java/org/zwobble/couscous/types/TypeParameter.java7
-rw-r--r--src/main/java/org/zwobble/couscous/util/ExtraLists.java8
-rw-r--r--src/main/java/org/zwobble/couscous/util/InsertionOrderSet.java6
-rw-r--r--src/test/java/org/zwobble/couscous/tests/frontends/java/FreeVariablesTests.java32
27 files changed, 577 insertions, 217 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java
index 3a53cfd..322a32a 100644
--- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java
@@ -9,6 +9,9 @@ import org.zwobble.couscous.util.ExtraIterables;
import java.util.List;
import java.util.Set;
+import static org.zwobble.couscous.util.ExtraLists.eagerFlatMap;
+import static org.zwobble.couscous.util.ExtraLists.list;
+
public class ClassNode implements TypeNode {
public static ClassNodeBuilder builder(ScalarType name) {
return new ClassNodeBuilder(name);
@@ -24,9 +27,11 @@ public class ClassNode implements TypeNode {
List<FieldDeclarationNode> fields,
List<StatementNode> staticConstructor,
ConstructorNode constructor,
- List<MethodNode> methods)
+ List<MethodNode> methods,
+ List<TypeNode> innerTypes
+ )
{
- return new ClassNode(name, typeParameters, superTypes, fields, staticConstructor, constructor, methods);
+ return new ClassNode(name, typeParameters, superTypes, fields, staticConstructor, constructor, methods, innerTypes);
}
private final ScalarType name;
@@ -36,7 +41,8 @@ public class ClassNode implements TypeNode {
private final List<StatementNode> staticConstructor;
private final ConstructorNode constructor;
private final List<MethodNode> methods;
-
+ private final List<TypeNode> innerTypes;
+
public ClassNode(
ScalarType name,
List<FormalTypeParameterNode> typeParameters,
@@ -44,7 +50,9 @@ public class ClassNode implements TypeNode {
List<FieldDeclarationNode> fields,
List<StatementNode> staticConstructor,
ConstructorNode constructor,
- List<MethodNode> methodNodes)
+ List<MethodNode> methodNodes,
+ List<TypeNode> innerTypes
+ )
{
this.name = name;
this.typeParameters = typeParameters;
@@ -53,6 +61,7 @@ public class ClassNode implements TypeNode {
this.staticConstructor = staticConstructor;
this.constructor = constructor;
methods = methodNodes;
+ this.innerTypes = innerTypes;
}
@Override
@@ -86,7 +95,12 @@ public class ClassNode implements TypeNode {
public List<MethodNode> getMethods() {
return methods;
}
-
+
+ @Override
+ public List<TypeNode> getInnerTypes() {
+ return innerTypes;
+ }
+
public String getSimpleName() {
return name.getSimpleName();
}
@@ -114,7 +128,23 @@ public class ClassNode implements TypeNode {
transformer.transformFields(fields),
transformer.transformStatements(staticConstructor),
transformer.transformConstructor(constructor),
- transformer.transformMethods(methods));
+ transformer.transformMethods(methods),
+ eagerFlatMap(innerTypes, transformer::transformTypeDeclaration)
+ );
+ }
+
+ @Override
+ public TypeNode stripInnerTypes() {
+ return new ClassNode(
+ name,
+ typeParameters,
+ superTypes,
+ fields,
+ staticConstructor,
+ constructor,
+ methods,
+ list()
+ );
}
@Override
@@ -127,6 +157,7 @@ public class ClassNode implements TypeNode {
", staticConstructor=" + staticConstructor +
", constructor=" + constructor +
", methods=" + methods +
+ ", innerTypes=" + innerTypes +
')';
}
@@ -143,7 +174,8 @@ public class ClassNode implements TypeNode {
if (!fields.equals(classNode.fields)) return false;
if (!staticConstructor.equals(classNode.staticConstructor)) return false;
if (!constructor.equals(classNode.constructor)) return false;
- return methods.equals(classNode.methods);
+ if (!methods.equals(classNode.methods)) return false;
+ return innerTypes.equals(classNode.innerTypes);
}
@@ -156,6 +188,7 @@ public class ClassNode implements TypeNode {
result = 31 * result + staticConstructor.hashCode();
result = 31 * result + constructor.hashCode();
result = 31 * result + methods.hashCode();
+ result = 31 * result + innerTypes.hashCode();
return result;
}
}
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
index 4735a2e..ec17c9b 100644
--- a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
+++ b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
@@ -24,6 +24,7 @@ public class ClassNodeBuilder {
private List<StatementNode> staticConstructor;
private Optional<ConstructorNode> constructor;
private final ImmutableList.Builder<MethodNode> methods;
+ private final ImmutableList.Builder<TypeNode> innerTypes;
public ClassNodeBuilder(ScalarType name) {
this.name = name;
@@ -33,6 +34,7 @@ public class ClassNodeBuilder {
staticConstructor = list();
this.constructor = Optional.empty();
this.methods = ImmutableList.builder();
+ this.innerTypes = ImmutableList.builder();
}
public ClassNodeBuilder(String name) {
@@ -119,7 +121,9 @@ public class ClassNodeBuilder {
fields.build(),
staticConstructor,
constructor.orElse(ConstructorNode.DEFAULT),
- methods.build());
+ methods.build(),
+ innerTypes.build()
+ );
}
public InterfaceNode buildInterface() {
@@ -129,7 +133,8 @@ public class ClassNodeBuilder {
superTypes.build(),
fields.build(),
staticConstructor,
- methods.build()
+ methods.build(),
+ innerTypes.build()
);
}
diff --git a/src/main/java/org/zwobble/couscous/ast/EnumNode.java b/src/main/java/org/zwobble/couscous/ast/EnumNode.java
index 92084dd..4a41a03 100644
--- a/src/main/java/org/zwobble/couscous/ast/EnumNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/EnumNode.java
@@ -50,6 +50,11 @@ public class EnumNode implements TypeNode {
}
@Override
+ public List<TypeNode> getInnerTypes() {
+ return list();
+ }
+
+ @Override
public int nodeType() {
return NodeTypes.ENUM;
}
@@ -67,6 +72,11 @@ public class EnumNode implements TypeNode {
}
@Override
+ public TypeNode stripInnerTypes() {
+ return this;
+ }
+
+ @Override
public String toString() {
return "EnumNode(" +
"name=" + name +
diff --git a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
index c0444a4..3159d03 100644
--- a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
@@ -7,6 +7,9 @@ import org.zwobble.couscous.types.Type;
import java.util.List;
import java.util.Set;
+import static org.zwobble.couscous.util.ExtraLists.eagerFlatMap;
+import static org.zwobble.couscous.util.ExtraLists.list;
+
public class InterfaceNode implements TypeNode {
public static InterfaceNode declareInterface(
ScalarType name,
@@ -14,9 +17,11 @@ public class InterfaceNode implements TypeNode {
Set<Type> superTypes,
List<FieldDeclarationNode> fields,
List<StatementNode> staticConstructor,
- List<MethodNode> methods)
+ List<MethodNode> methods,
+ List<TypeNode> innerTypes
+ )
{
- return new InterfaceNode(name, typeParameters, superTypes, fields, staticConstructor, methods);
+ return new InterfaceNode(name, typeParameters, superTypes, fields, staticConstructor, methods, innerTypes);
}
private final ScalarType name;
@@ -25,6 +30,7 @@ public class InterfaceNode implements TypeNode {
private final List<FieldDeclarationNode> fields;
private final List<StatementNode> staticConstructor;
private final List<MethodNode> methods;
+ private final List<TypeNode> innerTypes;
private InterfaceNode(
ScalarType name,
@@ -32,7 +38,9 @@ public class InterfaceNode implements TypeNode {
Set<Type> superTypes,
List<FieldDeclarationNode> fields,
List<StatementNode> staticConstructor,
- List<MethodNode> methods)
+ List<MethodNode> methods,
+ List<TypeNode> innerTypes
+ )
{
this.name = name;
this.typeParameters = typeParameters;
@@ -40,6 +48,7 @@ public class InterfaceNode implements TypeNode {
this.fields = fields;
this.staticConstructor = staticConstructor;
this.methods = methods;
+ this.innerTypes = innerTypes;
}
@Override
@@ -71,6 +80,11 @@ public class InterfaceNode implements TypeNode {
}
@Override
+ public List<TypeNode> getInnerTypes() {
+ return innerTypes;
+ }
+
+ @Override
public int nodeType() {
return NodeTypes.INTERFACE;
}
@@ -88,7 +102,22 @@ public class InterfaceNode implements TypeNode {
transformer.transformTypes(superTypes),
transformer.transformFields(fields),
transformer.transformStatements(staticConstructor),
- transformer.transformMethods(methods));
+ transformer.transformMethods(methods),
+ eagerFlatMap(innerTypes, transformer::transformTypeDeclaration)
+ );
+ }
+
+ @Override
+ public TypeNode stripInnerTypes() {
+ return new InterfaceNode(
+ name,
+ typeParameters,
+ superTypes,
+ fields,
+ staticConstructor,
+ methods,
+ list()
+ );
}
@Override
@@ -100,6 +129,7 @@ public class InterfaceNode implements TypeNode {
", fields=" + fields +
", staticConstructor=" + staticConstructor +
", methods=" + methods +
+ ", innerTypes=" + innerTypes +
')';
}
@@ -115,7 +145,8 @@ public class InterfaceNode implements TypeNode {
if (!superTypes.equals(that.superTypes)) return false;
if (!fields.equals(that.fields)) return false;
if (!staticConstructor.equals(that.staticConstructor)) return false;
- return methods.equals(that.methods);
+ if (!methods.equals(that.methods)) return false;
+ return innerTypes.equals(that.innerTypes);
}
@@ -127,6 +158,7 @@ public class InterfaceNode implements TypeNode {
result = 31 * result + fields.hashCode();
result = 31 * result + staticConstructor.hashCode();
result = 31 * result + methods.hashCode();
+ result = 31 * result + innerTypes.hashCode();
return result;
}
}
diff --git a/src/main/java/org/zwobble/couscous/ast/TypeNode.java b/src/main/java/org/zwobble/couscous/ast/TypeNode.java
index 9aa9cfb..01b033b 100644
--- a/src/main/java/org/zwobble/couscous/ast/TypeNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/TypeNode.java
@@ -12,5 +12,7 @@ public interface TypeNode extends Node {
List<FormalTypeParameterNode> getTypeParameters();
Set<Type> getSuperTypes();
List<MethodNode> getMethods();
+ List<TypeNode> getInnerTypes();
TypeNode transformSubtree(NodeTransformer transformer);
+ TypeNode stripInnerTypes();
}
diff --git a/src/main/java/org/zwobble/couscous/ast/sugar/TypeDeclarationBody.java b/src/main/java/org/zwobble/couscous/ast/sugar/TypeDeclarationBody.java
index 381c521..99b7c06 100644
--- a/src/main/java/org/zwobble/couscous/ast/sugar/TypeDeclarationBody.java
+++ b/src/main/java/org/zwobble/couscous/ast/sugar/TypeDeclarationBody.java
@@ -19,6 +19,7 @@ public class TypeDeclarationBody {
private final ImmutableList.Builder<StatementNode> instanceInitializers = ImmutableList.builder();
private ConstructorNode constructor = ConstructorNode.DEFAULT;
private final ImmutableList.Builder<MethodNode> methods = ImmutableList.builder();
+ private final ImmutableList.Builder<TypeNode> innerTypes = ImmutableList.builder();
private Builder() {
}
@@ -43,6 +44,10 @@ public class TypeDeclarationBody {
this.methods.add(method);
}
+ public void addInnerType(TypeNode typeDeclaration) {
+ this.innerTypes.add(typeDeclaration);
+ }
+
public TypeDeclarationBody build() {
return new TypeDeclarationBody(
fields.build(),
@@ -50,7 +55,9 @@ public class TypeDeclarationBody {
ConstructorNode.constructor(
constructor.getArguments(),
ExtraLists.concat(instanceInitializers.build(), constructor.getBody())),
- methods.build());
+ methods.build(),
+ innerTypes.build()
+ );
}
}
@@ -58,16 +65,21 @@ public class TypeDeclarationBody {
private final List<StatementNode> staticConstructor;
private final ConstructorNode constructor;
private final List<MethodNode> methods;
+ private final List<TypeNode> innerTypes;
public TypeDeclarationBody(
List<FieldDeclarationNode> fields,
- List<StatementNode> staticConstructor, ConstructorNode constructor,
- List<MethodNode> methods)
+ List<StatementNode> staticConstructor,
+ ConstructorNode constructor,
+ List<MethodNode> methods,
+ List<TypeNode> innerTypes
+ )
{
this.fields = fields;
this.staticConstructor = staticConstructor;
this.constructor = constructor;
this.methods = methods;
+ this.innerTypes = innerTypes;
}
public List<FieldDeclarationNode> getFields() {
@@ -85,4 +97,8 @@ public class TypeDeclarationBody {
public List<MethodNode> getMethods() {
return methods;
}
+
+ public List<TypeNode> getInnerTypes() {
+ return innerTypes;
+ }
}
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 47a7635..260d7ec 100644
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
@@ -7,6 +7,7 @@ import org.zwobble.couscous.backends.csharp.primitives.CsharpPrimitiveMethods;
import org.zwobble.couscous.backends.naming.Naming;
import org.zwobble.couscous.transforms.DesugarForEachToFor;
import org.zwobble.couscous.transforms.DesugarForToWhile;
+import org.zwobble.couscous.transforms.HoistNestedTypes;
import org.zwobble.couscous.transforms.SplitStaticsFromInterface;
import org.zwobble.couscous.types.*;
@@ -56,7 +57,7 @@ public class CsharpCodeGenerator {
CsharpPrimitiveMethods.TRANSFORMER,
nodeTransformer
),
- types
+ HoistNestedTypes.hoist(types)
)
);
}
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 4ddeb7c..c2fa029 100644
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java
@@ -596,7 +596,7 @@ public class CsharpSerializer {
}
private void writeInNamespace(TypeNode typeNode, Action writeBody) {
- writeInNamespace(typeNode.getName().getPackage().get(), writeBody);
+ writeInNamespace(typeNode.getName().getQualifier().get(), writeBody);
}
private void writeInNamespace(String namespace, Action writeBody) {
diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java b/src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java
index 5b6e819..6509fc4 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/PythonBackend.java
@@ -8,6 +8,7 @@ import org.zwobble.couscous.ast.visitors.NodeTransformer;
import org.zwobble.couscous.transforms.DesugarForEachToFor;
import org.zwobble.couscous.transforms.DesugarForToWhile;
import org.zwobble.couscous.transforms.DesugarSwitchToIfElse;
+import org.zwobble.couscous.transforms.HoistNestedTypes;
import java.io.File;
import java.io.IOException;
@@ -50,13 +51,15 @@ public class PythonBackend implements Backend {
}
private List<TypeNode> desugar(List<TypeNode> classes) {
- return NodeTransformer.applyAll(
- list(
- DesugarSwitchToIfElse.transformer(),
- DesugarForEachToFor.transformer(),
- DesugarForToWhile.transformer()
- ),
- classes
+ return HoistNestedTypes.hoist(
+ NodeTransformer.applyAll(
+ list(
+ DesugarSwitchToIfElse.transformer(),
+ DesugarForEachToFor.transformer(),
+ DesugarForToWhile.transformer()
+ ),
+ classes
+ )
);
}
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java b/src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java
index 845c6f9..5d5915d 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/AnonymousType.java
@@ -2,6 +2,8 @@ package org.zwobble.couscous.frontends.java;
import org.zwobble.couscous.types.Type;
+import java.util.function.Function;
+
public class AnonymousType implements Type {
public static Type anonymousType(String key) {
return new AnonymousType(key);
@@ -19,6 +21,11 @@ public class AnonymousType implements Type {
}
@Override
+ public Type transformSubTypes(Function<Type, Type> transform) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java b/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java
index b904f0d..7679431 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java
@@ -14,8 +14,6 @@ import java.util.stream.Stream;
import static org.zwobble.couscous.ast.structure.NodeStructure.descendantNodesAndSelf;
import static org.zwobble.couscous.util.Casts.tryCast;
import static org.zwobble.couscous.util.ExtraIterables.iterable;
-import static org.zwobble.couscous.util.ExtraIterables.lazyFlatMap;
-import static org.zwobble.couscous.util.ExtraLists.list;
import static org.zwobble.couscous.util.ExtraStreams.toStream;
public class FreeVariables {
@@ -50,32 +48,52 @@ public class FreeVariables {
}
}
+ public static class FindDeclaredTypes {
+ private static final Function<Node, Stream<TypeParameter>> VISITOR =
+ DynamicNodeMapper.instantiate(new FindDeclaredTypes(), "visit");
+
+ public Stream<TypeParameter> visit(Node node) {
+ return Stream.empty();
+ }
+
+ public Stream<TypeParameter> visit(MethodNode methodNode) {
+ return methodNode.getTypeParameters().stream().map(parameter -> parameter.getType());
+ }
+ }
+
public static Set<TypeParameter> findFreeTypeParameters(Node root) {
+ // TODO: test removal of declared types
+ Set<TypeParameter> declaredTypes = descendantNodesAndSelf(root)
+ .flatMap(FindDeclaredTypes.VISITOR)
+ .collect(Collectors.toSet());
+
// TODO: this assumes that *all* type parameters are free, which is often but not always true in inner types
- Iterable<Type> types = iterable(() -> descendantNodesAndSelf(root)
- .flatMap(FindReferencedTypes.VISITOR));
-
- return ImmutableSet.copyOf(lazyFlatMap(types, type -> type.accept(new Type.Visitor<Iterable<TypeParameter>>() {
- @Override
- public Iterable<TypeParameter> visit(ScalarType type) {
- return list();
- }
-
- @Override
- public Iterable<TypeParameter> visit(TypeParameter parameter) {
- return list(parameter);
- }
-
- @Override
- public Iterable<TypeParameter> visit(ParameterizedType type) {
- return lazyFlatMap(type.getParameters(), parameter -> parameter.accept(this));
- }
-
- @Override
- public Iterable<TypeParameter> visit(BoundTypeParameter type) {
- return type.getValue().accept(this);
- }
- })));
+ Stream<TypeParameter> types = descendantNodesAndSelf(root)
+ .flatMap(FindReferencedTypes.VISITOR)
+ .flatMap(type -> type.accept(new Type.Visitor<Stream<TypeParameter>>() {
+ @Override
+ public Stream<TypeParameter> visit(ScalarType type) {
+ return Stream.empty();
+ }
+
+ @Override
+ public Stream<TypeParameter> visit(TypeParameter parameter) {
+ return Stream.of(parameter);
+ }
+
+ @Override
+ public Stream<TypeParameter> visit(ParameterizedType type) {
+ return type.getParameters().stream().flatMap(parameter -> parameter.accept(this));
+ }
+
+ @Override
+ public Stream<TypeParameter> visit(BoundTypeParameter type) {
+ return type.getValue().accept(this);
+ }
+ }))
+ .filter(type -> !declaredTypes.contains(type));
+
+ return ImmutableSet.copyOf(iterable(() -> types));
}
public static List<ReferenceNode> findFreeVariables(List<? extends Node> body) {
diff --git a/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java b/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java
index 9bad1d2..c019cb8 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java
@@ -40,6 +40,10 @@ public class GeneratedClosure {
concat(capturedVariables, arguments));
}
+ public boolean hasCaptures() {
+ return !capturedTypes.isEmpty() || !capturedVariables.isEmpty();
+ }
+
private ScalarType getType() {
return classNode.getName();
}
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 00198a6..aa64543 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java
@@ -290,6 +290,7 @@ public class JavaExpressionReader {
return methodCall(
thisReference(typeOf(declaringClass.getDeclaringClass())),
// TODO: remove duplication with JavaReader in name generation
+ // TODO: handle this when hoisting (i.e. in HoistNestedTypes)
"create_" + declaringClass.getName(),
arguments,
type);
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 5202127..a047e9e 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
@@ -1,8 +1,6 @@
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.*;
import org.zwobble.couscous.ast.*;
@@ -11,21 +9,19 @@ 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.visitors.NodeTransformer;
-import org.zwobble.couscous.types.*;
+import org.zwobble.couscous.transforms.ClosureGenerator;
+import org.zwobble.couscous.types.ScalarType;
import org.zwobble.couscous.types.Type;
import org.zwobble.couscous.util.ExtraLists;
-import org.zwobble.couscous.util.InsertionOrderSet;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
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.transform;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@@ -37,17 +33,12 @@ import static org.zwobble.couscous.ast.FieldDeclarationNode.field;
import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg;
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.VariableReferenceNode.reference;
-import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeTypeParameters;
-import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariables;
import static org.zwobble.couscous.frontends.java.JavaMethods.signature;
import static org.zwobble.couscous.frontends.java.JavaTypes.*;
import static org.zwobble.couscous.types.Types.erasure;
import static org.zwobble.couscous.util.Casts.tryCast;
-import static org.zwobble.couscous.util.ExtraIterables.lazyMap;
import static org.zwobble.couscous.util.ExtraLists.*;
import static org.zwobble.couscous.util.ExtraMaps.map;
@@ -125,7 +116,8 @@ public class JavaReader {
superTypes,
body.getFields(),
body.getStaticConstructor(),
- body.getMethods()
+ body.getMethods(),
+ body.getInnerTypes()
);
} else {
return ClassNode.declareClass(
@@ -135,7 +127,9 @@ public class JavaReader {
body.getFields(),
body.getStaticConstructor(),
body.getConstructor(),
- body.getMethods());
+ body.getMethods(),
+ body.getInnerTypes()
+ );
}
}
@@ -197,46 +191,6 @@ public class JavaReader {
list(method));
}
- private List<StatementNode> replaceCaptureReferences(
- ScalarType className,
- List<StatementNode> body,
- InsertionOrderSet<CapturedVariable> freeVariables)
- {
- Map<ExpressionNode, ExpressionNode> replacements = Maps.transformValues(
- Maps.uniqueIndex(freeVariables, variable -> variable.freeVariable),
- freeVariable -> captureAccess(className, freeVariable));
- NodeTransformer transformer = NodeTransformer.replaceExpressions(replacements);
- return eagerFlatMap(body, transformer::transformStatement);
- }
-
- private ConstructorNode buildConstructor(
- Scope outerScope,
- ScalarType type,
- InsertionOrderSet<CapturedVariable> freeVariables,
- ConstructorNode existing)
- {
- Scope scope = outerScope.enterConstructor();
-
- ImmutableList.Builder<FormalArgumentNode> arguments = ImmutableList.builder();
- ImmutableList.Builder<StatementNode> body = ImmutableList.builder();
-
- for (CapturedVariable variable : freeVariables) {
- FormalArgumentNode argument = scope.formalArgument(variable.field.getName(), variable.field.getType());
- arguments.add(argument);
- body.add(assignStatement(captureAccess(type, variable), reference(argument)));
- }
-
- arguments.addAll(existing.getArguments());
- body.addAll(existing.getBody());
-
- return new ConstructorNode(arguments.build(), body.build());
- }
-
- private FieldAccessNode captureAccess(ScalarType type, CapturedVariable freeVariable) {
- FieldDeclarationNode field = freeVariable.field;
- return fieldAccess(thisReference(type), field.getName(), field.getType());
- }
-
GeneratedClosure readAnonymousClass(Scope outerScope, AnonymousClassDeclaration declaration) {
ScalarType className = generateAnonymousName(declaration);
Scope scope = outerScope.enterClass(className);
@@ -255,61 +209,6 @@ public class JavaReader {
return closure;
}
- private static class CapturedVariable {
- private final ReferenceNode freeVariable;
- private final FieldDeclarationNode field;
-
- public CapturedVariable(ReferenceNode freeVariable, FieldDeclarationNode field) {
- this.freeVariable = freeVariable;
- this.field = field;
- }
- }
-
- private GeneratedClosure classWithCapture(
- Scope scope,
- ClassNode classNode
- ) {
- InsertionOrderSet<org.zwobble.couscous.types.TypeParameter> freeTypeParameters = InsertionOrderSet.copyOf(
- findFreeTypeParameters(classNode));
-
- InsertionOrderSet<ReferenceNode> freeVariables = InsertionOrderSet.copyOf(Iterables.filter(
- findFreeVariables(ExtraLists.concat(
- classNode.getFields(),
- classNode.getMethods())),
- // TODO: check this references work correctly in anonymous classes
- variable -> !isThisReference(classNode.getName(), variable)));
- InsertionOrderSet<CapturedVariable> capturedVariables = InsertionOrderSet.copyOf(transform(
- freeVariables,
- freeVariable -> new CapturedVariable(freeVariable, fieldForCapture(freeVariable))));
- Iterable<FieldDeclarationNode> captureFields = transform(capturedVariables, capture -> capture.field);
-
- List<FieldDeclarationNode> fields = ImmutableList.copyOf(concat(
- classNode.getFields(),
- captureFields));
-
- if (!classNode.getStaticConstructor().isEmpty()) {
- throw new RuntimeException("Class has unexpected static constructor");
- }
-
- ClassNode generatedClass = new ClassNode(
- classNode.getName(),
- // TODO: generate fresh type parameter and replace in body
- ExtraLists.concat(lazyMap(freeTypeParameters, parameter -> formalTypeParameter(parameter)), classNode.getTypeParameters()),
- classNode.getSuperTypes(),
- fields,
- list(),
- buildConstructor(scope, classNode.getName(), capturedVariables, classNode.getConstructor()),
- eagerMap(classNode.getMethods(), method ->
- method.mapBody(body -> replaceCaptureReferences(classNode.getName(), body, capturedVariables))));
- return new GeneratedClosure(generatedClass, freeTypeParameters, freeVariables);
- }
-
- private boolean isThisReference(ScalarType name, ReferenceNode reference) {
- return tryCast(ThisReferenceNode.class, reference)
- .map(node -> node.getType().equals(name))
- .orElse(false);
- }
-
private GeneratedClosure classWithCapture(
Scope scope,
ScalarType className,
@@ -322,7 +221,9 @@ public class JavaReader {
anonymousClass.getFields(),
list(),
ConstructorNode.DEFAULT,
- anonymousClass.getMethods());
+ anonymousClass.getMethods(),
+ list()
+ );
if (anonymousClass.getType().isPresent()) {
NodeTransformer nodeTransformer = NodeTransformer.replaceExpressions(map(
thisReference(anonymousClass.getType().get()),
@@ -330,23 +231,7 @@ public class JavaReader {
));
classNode = nodeTransformer.transformClass(classNode);
}
- return classWithCapture(scope, classNode);
- }
-
- private FieldDeclarationNode fieldForCapture(ReferenceNode freeVariable) {
- return freeVariable.accept(new ReferenceNode.Visitor<FieldDeclarationNode>() {
- @Override
- public FieldDeclarationNode visit(VariableReferenceNode reference) {
- return field(reference.getReferent().getName(), reference.getType());
- }
-
- @Override
- public FieldDeclarationNode visit(ThisReferenceNode thisReference) {
- Type type = thisReference.getType();
- String name = "this_" + erasure(type).getQualifiedName().replace(".", "__");
- return field(name, type);
- }
- });
+ return ClosureGenerator.classWithCapture(scope, classNode);
}
private ScalarType generateAnonymousName(ASTNode node) {
@@ -380,39 +265,11 @@ public class JavaReader {
.ifPresent(field -> readField(body, scope, type, field, isInterface));
tryCast(TypeDeclaration.class, declaration)
- .ifPresent(typeDeclaration -> classes.add(readNestedTypeDeclaration(body, typeDeclaration)));
+ .ifPresent(typeDeclaration -> body.addInnerType(readTypeDeclaration(typeDeclaration)));
}
return body.build();
}
- private TypeNode readNestedTypeDeclaration(TypeDeclarationBody.Builder body, TypeDeclaration typeDeclaration) {
- TypeNode typeNode = readTypeDeclaration(typeDeclaration);
- // TODO: can we remove duplication of scope creation with readTypeDeclaration()?
- Scope scope = topScope.enterClass(typeNode.getName());
- return tryCast(ClassNode.class, typeNode)
- .filter(node -> !Modifier.isStatic(typeDeclaration.getModifiers()))
- .<TypeNode>map(node -> {
- GeneratedClosure closure = classWithCapture(scope, node);
-// Type type = node.getTypeParameters().isEmpty()
-// ? typeNode.getName()
-// : parameterizedType(typeNode.getName(), eagerMap(node.getTypeParameters()));
- Type type = typeNode.getName();
- // TODO: method type parameters
- List<FormalArgumentNode> methodArguments = eagerMap(
- node.getConstructor().getArguments(),
- // TODO: use scope of outer class
- argument -> scope.formalArgument(argument.getName(), argument.getType()));
- MethodNode method = MethodNode.builder("create_" + typeDeclaration.getName().getIdentifier())
- .arguments(methodArguments)
- .returns(type)
- .statement(returns(closure.generateConstructor(lazyMap(methodArguments, argument -> reference(argument)))))
- .build();
- body.addMethod(method);
- return closure.getClassNode();
- })
- .orElse(typeNode);
- }
-
private void readField(TypeDeclarationBody.Builder builder, Scope scope, ScalarType declaringType, FieldDeclaration field, boolean isInterface) {
@SuppressWarnings("unchecked")
List<VariableDeclarationFragment> fragments = field.fragments();
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 aa20fc9..5d9eddd 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaTypes.java
@@ -47,10 +47,12 @@ public class JavaTypes {
Identifier declaringScope = getDeclaringScope(typeBinding);
return new TypeParameter(declaringScope, typeBinding.getName());
}
- ScalarType rawType = outerClass == null
- ? ScalarType.of(typeBinding.getErasure().getQualifiedName())
- // TODO: test for erasure of inner type name
- : ScalarType.of(erasure(typeOf(outerClass)).getQualifiedName() + "__" + typeBinding.getErasure().getName());
+ // TODO: tidy-up
+// ScalarType rawType = outerClass == null
+// ? ScalarType.of(typeBinding.getErasure().getQualifiedName())
+// // TODO: test for erasure of inner type name
+// : ScalarType.of(erasure(typeOf(outerClass)).getQualifiedName() + "__" + typeBinding.getErasure().getName());
+ ScalarType rawType = ScalarType.of(typeBinding.getErasure().getQualifiedName());
if (typeBinding.isParameterizedType()) {
List<Type> typeParameters = eagerMap(
asList(typeBinding.getTypeArguments()),
diff --git a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
index f813faf..a85d126 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/JavaProject.java
@@ -10,6 +10,7 @@ import org.zwobble.couscous.interpreter.values.BoxedIntegerInterpreterValue;
import org.zwobble.couscous.interpreter.values.InternalCouscousInterpreterValue;
import org.zwobble.couscous.transforms.DesugarForToWhile;
import org.zwobble.couscous.transforms.DesugarSwitchToIfElse;
+import org.zwobble.couscous.transforms.HoistNestedTypes;
import java.util.List;
@@ -30,7 +31,9 @@ public class JavaProject {
NodeTransformer switchTransformer = DesugarSwitchToIfElse.transformer();
NodeTransformer forTransformer = DesugarForToWhile.transformer();
Iterable<InterpreterType> concreteTypes = Iterables.transform(
- NodeTransformer.applyAll(list(switchTransformer, forTransformer), classNodes),
+ HoistNestedTypes.hoist(
+ NodeTransformer.applyAll(list(switchTransformer, forTransformer), classNodes)
+ ),
UserDefinedInterpreterType::new
);
return builder()
diff --git a/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java b/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java
new file mode 100644
index 0000000..68c0aa6
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java
@@ -0,0 +1,147 @@
+package org.zwobble.couscous.transforms;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import org.zwobble.couscous.ast.*;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.frontends.java.GeneratedClosure;
+import org.zwobble.couscous.frontends.java.Scope;
+import org.zwobble.couscous.types.ScalarType;
+import org.zwobble.couscous.types.Type;
+import org.zwobble.couscous.types.TypeParameter;
+import org.zwobble.couscous.util.ExtraLists;
+import org.zwobble.couscous.util.InsertionOrderSet;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.transform;
+import static org.zwobble.couscous.ast.AssignmentNode.assignStatement;
+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.ThisReferenceNode.thisReference;
+import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
+import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeTypeParameters;
+import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariables;
+import static org.zwobble.couscous.types.Types.erasure;
+import static org.zwobble.couscous.util.Casts.tryCast;
+import static org.zwobble.couscous.util.ExtraIterables.lazyMap;
+import static org.zwobble.couscous.util.ExtraLists.*;
+
+public class ClosureGenerator {
+ public static GeneratedClosure classWithCapture(
+ Scope scope,
+ ClassNode classNode
+ ) {
+ InsertionOrderSet<TypeParameter> freeTypeParameters = InsertionOrderSet.copyOf(
+ findFreeTypeParameters(classNode));
+
+ InsertionOrderSet<ReferenceNode> freeVariables = InsertionOrderSet.copyOf(Iterables.filter(
+ findFreeVariables(ExtraLists.concat(
+ classNode.getFields(),
+ classNode.getMethods())),
+ // TODO: check this references work correctly in anonymous classes
+ variable -> !isThisReference(classNode.getName(), variable)));
+ InsertionOrderSet<CapturedVariable> capturedVariables = InsertionOrderSet.copyOf(transform(
+ freeVariables,
+ freeVariable -> new CapturedVariable(freeVariable, fieldForCapture(freeVariable))));
+ Iterable<FieldDeclarationNode> captureFields = transform(capturedVariables, capture -> capture.field);
+
+ List<FieldDeclarationNode> fields = ImmutableList.copyOf(concat(
+ classNode.getFields(),
+ captureFields));
+
+ if (!classNode.getStaticConstructor().isEmpty()) {
+ throw new RuntimeException("Class has unexpected static constructor");
+ }
+
+ ClassNode generatedClass = new ClassNode(
+ classNode.getName(),
+ // TODO: generate fresh type parameter and replace in body
+ ExtraLists.concat(lazyMap(freeTypeParameters, parameter -> formalTypeParameter(parameter)), classNode.getTypeParameters()),
+ classNode.getSuperTypes(),
+ fields,
+ list(),
+ buildConstructor(scope, classNode.getName(), capturedVariables, classNode.getConstructor()),
+ eagerMap(classNode.getMethods(), method ->
+ method.mapBody(body -> replaceCaptureReferences(classNode.getName(), body, capturedVariables))),
+ list()
+ );
+ return new GeneratedClosure(generatedClass, freeTypeParameters, freeVariables);
+ }
+
+ private static boolean isThisReference(ScalarType name, ReferenceNode reference) {
+ return tryCast(ThisReferenceNode.class, reference)
+ .map(node -> node.getType().equals(name))
+ .orElse(false);
+ }
+
+ private static FieldDeclarationNode fieldForCapture(ReferenceNode freeVariable) {
+ return freeVariable.accept(new ReferenceNode.Visitor<FieldDeclarationNode>() {
+ @Override
+ public FieldDeclarationNode visit(VariableReferenceNode reference) {
+ return field(reference.getReferent().getName(), reference.getType());
+ }
+
+ @Override
+ public FieldDeclarationNode visit(ThisReferenceNode thisReference) {
+ Type type = thisReference.getType();
+ String name = "this_" + erasure(type).getQualifiedName().replace(".", "__");
+ return field(name, type);
+ }
+ });
+ }
+
+ private static List<StatementNode> replaceCaptureReferences(
+ ScalarType className,
+ List<StatementNode> body,
+ InsertionOrderSet<CapturedVariable> freeVariables)
+ {
+ Map<ExpressionNode, ExpressionNode> replacements = Maps.transformValues(
+ Maps.uniqueIndex(freeVariables, variable -> variable.freeVariable),
+ freeVariable -> captureAccess(className, freeVariable));
+ NodeTransformer transformer = NodeTransformer.replaceExpressions(replacements);
+ return eagerFlatMap(body, transformer::transformStatement);
+ }
+
+ private static ConstructorNode buildConstructor(
+ Scope outerScope,
+ ScalarType type,
+ InsertionOrderSet<CapturedVariable> freeVariables,
+ ConstructorNode existing)
+ {
+ Scope scope = outerScope.enterConstructor();
+
+ ImmutableList.Builder<FormalArgumentNode> arguments = ImmutableList.builder();
+ ImmutableList.Builder<StatementNode> body = ImmutableList.builder();
+
+ for (CapturedVariable variable : freeVariables) {
+ FormalArgumentNode argument = scope.formalArgument(variable.field.getName(), variable.field.getType());
+ arguments.add(argument);
+ body.add(assignStatement(captureAccess(type, variable), reference(argument)));
+ }
+
+ arguments.addAll(existing.getArguments());
+ body.addAll(existing.getBody());
+
+ return new ConstructorNode(arguments.build(), body.build());
+ }
+
+ private static FieldAccessNode captureAccess(ScalarType type, CapturedVariable freeVariable) {
+ FieldDeclarationNode field = freeVariable.field;
+ return fieldAccess(thisReference(type), field.getName(), field.getType());
+ }
+
+ private static class CapturedVariable {
+ private final ReferenceNode freeVariable;
+ private final FieldDeclarationNode field;
+
+ public CapturedVariable(ReferenceNode freeVariable, FieldDeclarationNode field) {
+ this.freeVariable = freeVariable;
+ this.field = field;
+ }
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java b/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java
new file mode 100644
index 0000000..e4ff1c8
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java
@@ -0,0 +1,137 @@
+package org.zwobble.couscous.transforms;
+
+import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.ast.FormalArgumentNode;
+import org.zwobble.couscous.ast.MethodNode;
+import org.zwobble.couscous.ast.TypeNode;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.frontends.java.GeneratedClosure;
+import org.zwobble.couscous.frontends.java.Scope;
+import org.zwobble.couscous.types.ScalarType;
+import org.zwobble.couscous.types.Type;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import static org.zwobble.couscous.ast.ReturnNode.returns;
+import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
+import static org.zwobble.couscous.util.Casts.tryCast;
+import static org.zwobble.couscous.util.ExtraIterables.iterable;
+import static org.zwobble.couscous.util.ExtraIterables.lazyMap;
+import static org.zwobble.couscous.util.ExtraLists.*;
+
+public class HoistNestedTypes {
+ public static List<TypeNode> hoist(List<TypeNode> declarations) {
+ return new HoistNestedTypes().hoistTypes(declarations);
+ }
+
+ private final Scope topScope = Scope.create().temporaryPrefix("_couscous_hoist_nested_types");
+
+ private List<TypeNode> hoistTypes(List<TypeNode> declarations) {
+ Iterable<TypeNode> types = eagerFlatMap(declarations, declaration -> hoistInnerTypes(declaration));
+
+ NodeTransformer referenceTransformer = NodeTransformer.builder()
+ .transformType(this::transformType)
+ .build();
+
+ return eagerFlatMap(types, referenceTransformer::transformTypeDeclaration);
+ }
+
+ private Type transformType(Type type) {
+ if (isInnerType(type)) {
+ return hoistedTypeName((ScalarType) type);
+ } else {
+ return type.transformSubTypes(this::transformType);
+ }
+ }
+
+ private static final Pattern INNER_TYPE_QUALIFIER_REGEX = Pattern.compile(".*\\.[A-Z][^.]*$");
+
+ private boolean isInnerType(Type type) {
+ // TODO: implement separate type classes for top-level and inner types rather than using this heuristic
+
+ return tryCast(ScalarType.class, type)
+ .flatMap(scalarType -> scalarType.getQualifier())
+ .map(qualifier -> INNER_TYPE_QUALIFIER_REGEX.matcher(qualifier).matches())
+ .orElse(false);
+ }
+
+ private static Type hoistedTypeName(ScalarType type) {
+ return ScalarType.of(type.getQualifier().get() + "__" + type.getSimpleName());
+ }
+
+ private Iterable<TypeNode> hoistInnerTypes(TypeNode declaration) {
+ TypeNode type = declaration.stripInnerTypes();
+ Iterable<HoistedType> hoistedTypes = eagerMap(declaration.getInnerTypes(), this::hoistType);
+
+ return cons(
+ addMethods(type, eagerFlatMap(hoistedTypes, hoisted -> iterable(hoisted.method))),
+ lazyMap(hoistedTypes, hoisted -> hoisted.type)
+ );
+ }
+
+ private static TypeNode addMethods(TypeNode declaration, List<MethodNode> methods) {
+ if (methods.isEmpty()) {
+ return declaration;
+ } else if (declaration instanceof ClassNode) {
+ ClassNode classDeclaration = (ClassNode) declaration;
+ return new ClassNode(
+ classDeclaration.getName(),
+ classDeclaration.getTypeParameters(),
+ classDeclaration.getSuperTypes(),
+ classDeclaration.getFields(),
+ classDeclaration.getStaticConstructor(),
+ classDeclaration.getConstructor(),
+ concat(classDeclaration.getMethods(), methods),
+ classDeclaration.getInnerTypes()
+ );
+ } else {
+ throw new UnsupportedOperationException("Cannot add methods to non-class node");
+ }
+ }
+
+ private HoistedType hoistType(TypeNode typeNode) {
+ // TODO: we need to hoist inner types of the inner types, specifically *after* the initial hoist (to make closures easier to generate)
+ // TODO: can we remove duplication of scope creation with readTypeDeclaration()?
+ Scope scope = topScope.enterClass(typeNode.getName());
+ return tryCast(ClassNode.class, typeNode)
+ .flatMap(node -> {
+ // TODO: don't generate closure if not necessary (i.e. it's an explicit or implicit static inner class)
+ GeneratedClosure closure = ClosureGenerator.classWithCapture(scope, node);
+// Type type = node.getTypeParameters().isEmpty()
+// ? typeNode.getName()
+// : parameterizedType(typeNode.getName(), eagerMap(node.getTypeParameters()));
+ if (closure.hasCaptures()) {
+ Type type = typeNode.getName();
+ // TODO: method type parameters
+ List<FormalArgumentNode> methodArguments = eagerMap(
+ node.getConstructor().getArguments(),
+ // TODO: use scope of outer class
+ argument -> scope.formalArgument(argument.getName(), argument.getType()));
+ MethodNode method = MethodNode.builder("create_" + typeNode.getName().getSimpleName())
+ .arguments(methodArguments)
+ .returns(type)
+ .statement(returns(closure.generateConstructor(lazyMap(methodArguments, argument -> reference(argument)))))
+ .build();
+ return Optional.of(new HoistedType(
+ closure.getClassNode(),
+ Optional.of(method)
+ ));
+ } else {
+ return Optional.empty();
+ }
+ })
+ .orElse(new HoistedType(typeNode, Optional.empty()));
+ }
+
+ private static class HoistedType {
+ private final TypeNode type;
+ private final Optional<MethodNode> method;
+
+ private HoistedType(TypeNode type, Optional<MethodNode> method) {
+ this.type = type;
+ this.method = method;
+ }
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java b/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java
index b2c88ea..3af4042 100644
--- a/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java
+++ b/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java
@@ -27,7 +27,8 @@ public class SplitStaticsFromInterface {
interfaceNode.getFields(),
interfaceNode.getStaticConstructor(),
ConstructorNode.DEFAULT,
- eagerFilter(interfaceNode.getMethods(), method -> method.isStatic())
+ eagerFilter(interfaceNode.getMethods(), method -> method.isStatic()),
+ list()
));
result.add(InterfaceNode.declareInterface(
interfaceNode.getName(),
@@ -35,7 +36,8 @@ public class SplitStaticsFromInterface {
interfaceNode.getSuperTypes(),
list(),
list(),
- eagerFilter(interfaceNode.getMethods(), method -> !method.isStatic())
+ eagerFilter(interfaceNode.getMethods(), method -> !method.isStatic()),
+ interfaceNode.getInnerTypes()
));
} else {
result.add(typeNode);
diff --git a/src/main/java/org/zwobble/couscous/types/BoundTypeParameter.java b/src/main/java/org/zwobble/couscous/types/BoundTypeParameter.java
index 8eb5f5a..4272ac7 100644
--- a/src/main/java/org/zwobble/couscous/types/BoundTypeParameter.java
+++ b/src/main/java/org/zwobble/couscous/types/BoundTypeParameter.java
@@ -1,5 +1,7 @@
package org.zwobble.couscous.types;
+import java.util.function.Function;
+
public class BoundTypeParameter implements Type {
public static BoundTypeParameter boundTypeParameter(TypeParameter parameter, Type value) {
return new BoundTypeParameter(parameter, value);
@@ -27,6 +29,14 @@ public class BoundTypeParameter implements Type {
}
@Override
+ public Type transformSubTypes(Function<Type, Type> transform) {
+ return new BoundTypeParameter(
+ (TypeParameter) transform.apply(parameter),
+ transform.apply(value)
+ );
+ }
+
+ @Override
public String toString() {
return "BoundTypeParameter(" +
"parameter=" + parameter +
diff --git a/src/main/java/org/zwobble/couscous/types/ParameterizedType.java b/src/main/java/org/zwobble/couscous/types/ParameterizedType.java
index 7db73e0..6d4dca1 100644
--- a/src/main/java/org/zwobble/couscous/types/ParameterizedType.java
+++ b/src/main/java/org/zwobble/couscous/types/ParameterizedType.java
@@ -1,6 +1,9 @@
package org.zwobble.couscous.types;
import java.util.List;
+import java.util.function.Function;
+
+import static org.zwobble.couscous.util.ExtraLists.eagerMap;
public class ParameterizedType implements Type {
public static ParameterizedType parameterizedType(ScalarType rawType, List<Type> parameters) {
@@ -29,6 +32,14 @@ public class ParameterizedType implements Type {
}
@Override
+ public Type transformSubTypes(Function<Type, Type> transform) {
+ return new ParameterizedType(
+ (ScalarType)transform.apply(rawType),
+ eagerMap(parameters, transform)
+ );
+ }
+
+ @Override
public String toString() {
return "ParameterizedType(" +
"rawType=" + rawType +
diff --git a/src/main/java/org/zwobble/couscous/types/ScalarType.java b/src/main/java/org/zwobble/couscous/types/ScalarType.java
index 136b6cb..4f77342 100644
--- a/src/main/java/org/zwobble/couscous/types/ScalarType.java
+++ b/src/main/java/org/zwobble/couscous/types/ScalarType.java
@@ -3,6 +3,7 @@ package org.zwobble.couscous.types;
import com.google.common.base.Strings;
import java.util.Optional;
+import java.util.function.Function;
public class ScalarType implements Type {
public static ScalarType of(String qualifiedName) {
@@ -29,7 +30,7 @@ public class ScalarType implements Type {
return qualifiedName.substring(qualifiedName.lastIndexOf(".") + 1);
}
- public Optional<String> getPackage() {
+ public Optional<String> getQualifier() {
int lastDot = qualifiedName.lastIndexOf(".");
if (lastDot == -1) {
return Optional.empty();
@@ -73,4 +74,9 @@ public class ScalarType implements Type {
public <T> T accept(Visitor<T> visitor) {
return visitor.visit(this);
}
+
+ @Override
+ public Type transformSubTypes(Function<Type, Type> transform) {
+ return this;
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/types/Type.java b/src/main/java/org/zwobble/couscous/types/Type.java
index ad0f6fc..b5acd6d 100644
--- a/src/main/java/org/zwobble/couscous/types/Type.java
+++ b/src/main/java/org/zwobble/couscous/types/Type.java
@@ -1,5 +1,7 @@
package org.zwobble.couscous.types;
+import java.util.function.Function;
+
public interface Type {
<T> T accept(Visitor<T> visitor);
@@ -9,4 +11,6 @@ public interface Type {
T visit(ParameterizedType type);
T visit(BoundTypeParameter type);
}
+
+ Type transformSubTypes(Function<Type, Type> transform);
}
diff --git a/src/main/java/org/zwobble/couscous/types/TypeParameter.java b/src/main/java/org/zwobble/couscous/types/TypeParameter.java
index 39bf46b..fd4ada4 100644
--- a/src/main/java/org/zwobble/couscous/types/TypeParameter.java
+++ b/src/main/java/org/zwobble/couscous/types/TypeParameter.java
@@ -2,6 +2,8 @@ package org.zwobble.couscous.types;
import org.zwobble.couscous.ast.identifiers.Identifier;
+import java.util.function.Function;
+
public class TypeParameter implements Type {
public static TypeParameter typeParameter(Identifier declaringScope, String name) {
return new TypeParameter(declaringScope, name);
@@ -29,6 +31,11 @@ public class TypeParameter implements Type {
}
@Override
+ public Type transformSubTypes(Function<Type, Type> transform) {
+ return this;
+ }
+
+ @Override
public String toString() {
return "TypeParameter(" +
"declaringScope=" + declaringScope +
diff --git a/src/main/java/org/zwobble/couscous/util/ExtraLists.java b/src/main/java/org/zwobble/couscous/util/ExtraLists.java
index 7849ea6..b84afcd 100644
--- a/src/main/java/org/zwobble/couscous/util/ExtraLists.java
+++ b/src/main/java/org/zwobble/couscous/util/ExtraLists.java
@@ -158,4 +158,12 @@ public class ExtraLists {
public static <T> List<T> copyOf(Iterable<T> iterable) {
return ImmutableList.copyOf(iterable);
}
+
+ public static <T> T getLast(T[] values) {
+ return values[values.length - 1];
+ }
+
+ public static <T> T getLast(List<T> values) {
+ return values.get(values.size() - 1);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/util/InsertionOrderSet.java b/src/main/java/org/zwobble/couscous/util/InsertionOrderSet.java
index 4272add..8d168fc 100644
--- a/src/main/java/org/zwobble/couscous/util/InsertionOrderSet.java
+++ b/src/main/java/org/zwobble/couscous/util/InsertionOrderSet.java
@@ -20,8 +20,14 @@ public interface InsertionOrderSet<T> extends Iterable<T> {
public Iterator<T> iterator() {
return list.iterator();
}
+
+ @Override
+ public boolean isEmpty() {
+ return list.isEmpty();
+ }
};
}
List<T> asList();
+ boolean isEmpty();
}
diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/FreeVariablesTests.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/FreeVariablesTests.java
index 8bcf38d..0af0020 100644
--- a/src/test/java/org/zwobble/couscous/tests/frontends/java/FreeVariablesTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/FreeVariablesTests.java
@@ -1,22 +1,27 @@
package org.zwobble.couscous.tests.frontends.java;
import org.junit.Test;
-import org.zwobble.couscous.ast.ReferenceNode;
-import org.zwobble.couscous.ast.VariableDeclaration;
+import org.zwobble.couscous.ast.*;
+import org.zwobble.couscous.frontends.java.FreeVariables;
import org.zwobble.couscous.tests.TestIds;
+import org.zwobble.couscous.types.Type;
+import org.zwobble.couscous.types.TypeParameter;
import org.zwobble.couscous.types.Types;
import java.util.List;
+import java.util.Set;
import static java.util.Collections.emptyList;
import static org.junit.Assert.assertEquals;
import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg;
+import static org.zwobble.couscous.ast.FormalTypeParameterNode.formalTypeParameter;
import static org.zwobble.couscous.ast.LiteralNode.literal;
import static org.zwobble.couscous.ast.LocalVariableDeclarationNode.localVariableDeclaration;
import static org.zwobble.couscous.ast.VariableDeclaration.var;
import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariables;
import static org.zwobble.couscous.util.ExtraLists.list;
+import static org.zwobble.couscous.util.ExtraSets.set;
public class FreeVariablesTests {
@Test
@@ -43,4 +48,27 @@ public class FreeVariablesTests {
reference(declaration)));
assertEquals(emptyList(), freeVariables);
}
+
+ @Test
+ public void typeParameterIsFreeIfThereIsNoAssociatedDeclaration() {
+ TypeParameter typeParameter = new TypeParameter(TestIds.ANY_ID, "T");
+ MethodNode method = MethodNode.builder("f").argument(argOfType(typeParameter)).build();
+ Set<TypeParameter> freeTypeParameters = FreeVariables.findFreeTypeParameters(method);
+ assertEquals(set(typeParameter), freeTypeParameters);
+ }
+
+ @Test
+ public void typeParameterIsNotFreeIfItIsDeclared() {
+ TypeParameter typeParameter = new TypeParameter(TestIds.ANY_ID, "T");
+ MethodNode method = MethodNode.builder("f")
+ .typeParameter(formalTypeParameter(typeParameter))
+ .argument(argOfType(typeParameter))
+ .build();
+ Set<TypeParameter> freeTypeParameters = FreeVariables.findFreeTypeParameters(method);
+ assertEquals(set(), freeTypeParameters);
+ }
+
+ private FormalArgumentNode argOfType(Type type) {
+ return FormalArgumentNode.formalArg(var(TestIds.variable("x"), "x", type));
+ }
}