aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNode.java5
-rw-r--r--src/main/java/org/zwobble/couscous/ast/InterfaceNode.java9
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java18
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java13
-rw-r--r--src/test/java/org/zwobble/couscous/tests/CompilerTests.java12
-rw-r--r--src/test/resources/java/inner-inner-class/com/example/InnerClass.java36
6 files changed, 80 insertions, 13 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java
index 322a32a..0d37130 100644
--- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java
@@ -113,9 +113,12 @@ public class ClassNode implements TypeNode {
@Override
public Iterable<? extends Node> childNodes() {
return Iterables.concat(
+ typeParameters,
+ staticConstructor,
ExtraIterables.of(constructor),
fields,
- methods
+ methods,
+ innerTypes
);
}
diff --git a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
index 3159d03..6a56a6b 100644
--- a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
@@ -1,5 +1,6 @@
package org.zwobble.couscous.ast;
+import com.google.common.collect.Iterables;
import org.zwobble.couscous.ast.visitors.NodeTransformer;
import org.zwobble.couscous.types.ScalarType;
import org.zwobble.couscous.types.Type;
@@ -91,7 +92,13 @@ public class InterfaceNode implements TypeNode {
@Override
public Iterable<? extends Node> childNodes() {
- return methods;
+ return Iterables.concat(
+ typeParameters,
+ staticConstructor,
+ fields,
+ methods,
+ innerTypes
+ );
}
@Override
diff --git a/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java b/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java
index 68c0aa6..8c454d4 100644
--- a/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java
+++ b/src/main/java/org/zwobble/couscous/transforms/ClosureGenerator.java
@@ -29,6 +29,7 @@ import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariable
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.ExtraIterables.only;
import static org.zwobble.couscous.util.ExtraLists.*;
public class ClosureGenerator {
@@ -40,9 +41,7 @@ public class ClosureGenerator {
findFreeTypeParameters(classNode));
InsertionOrderSet<ReferenceNode> freeVariables = InsertionOrderSet.copyOf(Iterables.filter(
- findFreeVariables(ExtraLists.concat(
- classNode.getFields(),
- classNode.getMethods())),
+ findFreeVariables(ExtraLists.copyOf(classNode.childNodes())),
// TODO: check this references work correctly in anonymous classes
variable -> !isThisReference(classNode.getName(), variable)));
InsertionOrderSet<CapturedVariable> capturedVariables = InsertionOrderSet.copyOf(transform(
@@ -58,6 +57,8 @@ public class ClosureGenerator {
throw new RuntimeException("Class has unexpected static constructor");
}
+ NodeTransformer transformer = replaceCaptureReferencesTransformer(classNode.getName(), capturedVariables);
+
ClassNode generatedClass = new ClassNode(
classNode.getName(),
// TODO: generate fresh type parameter and replace in body
@@ -67,8 +68,9 @@ public class ClosureGenerator {
list(),
buildConstructor(scope, classNode.getName(), capturedVariables, classNode.getConstructor()),
eagerMap(classNode.getMethods(), method ->
- method.mapBody(body -> replaceCaptureReferences(classNode.getName(), body, capturedVariables))),
- list()
+ method.mapBody(body -> transformer.transformStatements(body))),
+ // TODO: should just call replaceCaptureReferences over entire class?
+ eagerMap(classNode.getInnerTypes(), type -> only(transformer.transformTypeDeclaration(type)))
);
return new GeneratedClosure(generatedClass, freeTypeParameters, freeVariables);
}
@@ -95,16 +97,14 @@ public class ClosureGenerator {
});
}
- private static List<StatementNode> replaceCaptureReferences(
+ private static NodeTransformer replaceCaptureReferencesTransformer(
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);
+ return NodeTransformer.replaceExpressions(replacements);
}
private static ConstructorNode buildConstructor(
diff --git a/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java b/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java
index 6c40af9..1cb74c8 100644
--- a/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java
+++ b/src/main/java/org/zwobble/couscous/transforms/HoistNestedTypes.java
@@ -29,6 +29,17 @@ public class HoistNestedTypes {
private final Scope topScope = Scope.create().temporaryPrefix("_couscous_hoist_nested_types");
private List<TypeNode> hoistTypes(List<TypeNode> declarations) {
+ while (true) {
+ List<TypeNode> hoisted = hoistInnerTypes(declarations);
+ if (hoisted.size() == declarations.size()) {
+ return hoisted;
+ } else {
+ declarations = hoisted;
+ }
+ }
+ }
+
+ private List<TypeNode> hoistInnerTypes(List<TypeNode> declarations) {
List<HoistResult> hoistResults = eagerMap(declarations, declaration -> hoistInnerTypes(declaration));
Iterable<HoistedType> hoistedTypes = lazyFlatMap(hoistResults, hoisted -> hoisted.hoistedTypes);
@@ -126,8 +137,6 @@ public class HoistNestedTypes {
}
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 -> {
diff --git a/src/test/java/org/zwobble/couscous/tests/CompilerTests.java b/src/test/java/org/zwobble/couscous/tests/CompilerTests.java
index 495f7ed..838029a 100644
--- a/src/test/java/org/zwobble/couscous/tests/CompilerTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/CompilerTests.java
@@ -133,6 +133,18 @@ public abstract class CompilerTests {
}
@Test
+ public void innerInnerClasses() throws Exception {
+ assertEquals(
+ value(42),
+ execTestProgram(
+ "inner-inner-class",
+ ScalarType.topLevel("com.example.InnerClass"),
+ "run",
+ list(),
+ Types.INT));
+ }
+
+ @Test
public void canImplementUserDefinedInterface() throws Exception {
assertEquals(
value(42),
diff --git a/src/test/resources/java/inner-inner-class/com/example/InnerClass.java b/src/test/resources/java/inner-inner-class/com/example/InnerClass.java
new file mode 100644
index 0000000..ec93f19
--- /dev/null
+++ b/src/test/resources/java/inner-inner-class/com/example/InnerClass.java
@@ -0,0 +1,36 @@
+package com.example;
+
+public class InnerClass {
+ public static int run() {
+ return new InnerClass(21).value();
+ }
+
+ private final int stash;
+
+ public InnerClass(int stash) {
+ this.stash = stash;
+ }
+
+ public int value() {
+ IntSupplier supplier = new IntSupplier(2);
+ return supplier.get();
+ }
+
+ private class IntSupplier {
+ private final int factor;
+
+ public IntSupplier(int factor) {
+ this.factor = factor;
+ }
+
+ public int get() {
+ return new Multiplier().calculate();
+ }
+
+ private class Multiplier {
+ public int calculate() {
+ return stash * factor;
+ }
+ }
+ }
+}