diff options
Diffstat (limited to 'src/main/java')
5 files changed, 71 insertions, 24 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java index 5d6d5e2..4cdb993 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java @@ -13,6 +13,7 @@ public class ClassNode implements TypeNode { public static ClassNodeBuilder builder(String name) { return new ClassNodeBuilder(name); } + public static ClassNode declareClass( TypeName name, Set<TypeName> superTypes, @@ -82,6 +83,7 @@ public class ClassNode implements TypeNode { return visitor.visit(this); } + @Override public ClassNode transform(NodeTransformer transformer) { return new ClassNode( transformer.transform(name), diff --git a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java index 4ae43c8..854728f 100644 --- a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java @@ -49,6 +49,7 @@ public class InterfaceNode implements TypeNode { return visitor.visit(this); } + @Override public InterfaceNode transform(NodeTransformer transformer) { return new InterfaceNode( transformer.transform(name), diff --git a/src/main/java/org/zwobble/couscous/ast/TypeNode.java b/src/main/java/org/zwobble/couscous/ast/TypeNode.java index 9946267..8277cda 100644 --- a/src/main/java/org/zwobble/couscous/ast/TypeNode.java +++ b/src/main/java/org/zwobble/couscous/ast/TypeNode.java @@ -1,5 +1,7 @@ package org.zwobble.couscous.ast; +import org.zwobble.couscous.ast.visitors.NodeTransformer; + import java.util.List; import java.util.Set; @@ -7,4 +9,5 @@ public interface TypeNode extends Node { TypeName getName(); Set<TypeName> getSuperTypes(); List<MethodNode> getMethods(); + TypeNode transform(NodeTransformer transformer); } 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 d56b091..dd83b8c 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java @@ -9,15 +9,11 @@ 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.util.Casts; import org.zwobble.couscous.util.ExtraLists; 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.*; import java.util.function.Function; import static com.google.common.collect.Iterables.concat; @@ -35,7 +31,9 @@ import static org.zwobble.couscous.ast.ThisReferenceNode.thisReference; import static org.zwobble.couscous.ast.VariableReferenceNode.reference; import static org.zwobble.couscous.frontends.java.FreeVariables.findFreeVariables; import static org.zwobble.couscous.frontends.java.JavaTypes.*; +import static org.zwobble.couscous.util.Casts.tryCast; import static org.zwobble.couscous.util.ExtraLists.*; +import static org.zwobble.couscous.util.ExtraMaps.lookup; public class JavaReader { public static List<TypeNode> readClassFromFile(List<Path> sourcePaths, Path sourcePath) throws IOException { @@ -46,7 +44,7 @@ public class JavaReader { throw new RuntimeException("Errors during parsing:\n\n" + describe(errors)); } JavaReader reader = new JavaReader(); - return cons(reader.readCompilationUnit(ast), reader.classes.build()); + return reader.readCompilationUnit(ast); } private static String describe(List<IProblem> errors) { @@ -62,14 +60,23 @@ public class JavaReader { private final ImmutableList.Builder<TypeNode> classes; private int anonymousClassCount = 0; private final Scope topScope = Scope.create(); + private final Map<TypeName, TypeName> nestedClasses = new HashMap<>(); private JavaReader() { classes = ImmutableList.builder(); } - private TypeNode readCompilationUnit(CompilationUnit ast) { + private List<TypeNode> readCompilationUnit(CompilationUnit ast) { TypeDeclaration type = (TypeDeclaration)ast.types().get(0); - return readTypeDeclaration(type); + List<TypeNode> classes = cons(readTypeDeclaration(type), this.classes.build()); + NodeTransformer transformer = NodeTransformer.builder() + .transformExpression(expression -> + tryCast(ConstructorCallNode.class, expression) + .flatMap(call -> lookup(nestedClasses, call.getType()).map(outerType -> + // TODO: handle existing arguments + new ConstructorCallNode(call.getType(), list(thisReference(outerType)))))) + .build(); + return eagerMap(classes, classNode -> classNode.transform(transformer)); } private TypeNode readTypeDeclaration(TypeDeclaration type) { @@ -188,30 +195,45 @@ public class JavaReader { private GeneratedClosure classWithCapture( Scope scope, - TypeName className, - AnonymousClass anonymousClass + ClassNode classNode ) { List<ReferenceNode> freeVariables = findFreeVariables(ExtraLists.concat( - anonymousClass.getFields(), - anonymousClass.getMethods())); + classNode.getFields(), + classNode.getMethods())); List<CapturedVariable> capturedVariables = ImmutableList.copyOf(transform( freeVariables, freeVariable -> new CapturedVariable(freeVariable, fieldForCapture(freeVariable)))); Iterable<FieldDeclarationNode> captureFields = transform(capturedVariables, capture -> capture.field); List<FieldDeclarationNode> fields = ImmutableList.copyOf(concat( - anonymousClass.getFields(), + classNode.getFields(), captureFields)); - ClassNode classNode = ClassNode.declareClass( + ClassNode generatedClass = ClassNode.declareClass( + classNode.getName(), + classNode.getSuperTypes(), + fields, + // TODO: handle static constructor (or codify that this is a case that never needs handling) + list(), + // TODO: handle existing constructor + buildConstructor(scope, classNode.getName(), capturedVariables), + eagerMap(classNode.getMethods(), method -> + method.mapBody(body -> replaceCaptureReferences(classNode.getName(), body, capturedVariables)))); + return new GeneratedClosure(generatedClass, freeVariables); + } + + private GeneratedClosure classWithCapture( + Scope scope, + TypeName className, + AnonymousClass anonymousClass + ) { + return classWithCapture(scope, ClassNode.declareClass( className, anonymousClass.getSuperTypes(), - fields, + anonymousClass.getFields(), list(), - buildConstructor(scope, className, capturedVariables), - eagerMap(anonymousClass.getMethods(), method -> - method.mapBody(body -> replaceCaptureReferences(className, body, capturedVariables)))); - return new GeneratedClosure(classNode, freeVariables); + ConstructorNode.DEFAULT, + anonymousClass.getMethods())); } private FieldDeclarationNode fieldForCapture(ReferenceNode freeVariable) { @@ -249,23 +271,37 @@ public class JavaReader { TypeDeclarationBody.Builder body = TypeDeclarationBody.builder(); for (Object declaration : bodyDeclarations) { - Casts.tryCast(MethodDeclaration.class, declaration) + tryCast(MethodDeclaration.class, declaration) .ifPresent(method -> readMethod(body, scope, method)); - Casts.tryCast(Initializer.class, declaration) + tryCast(Initializer.class, declaration) .ifPresent(initializer -> body.addInitializer( Modifier.isStatic(initializer.getModifiers()), readStatement(scope, initializer.getBody()))); - Casts.tryCast(FieldDeclaration.class, declaration) + tryCast(FieldDeclaration.class, declaration) .ifPresent(field -> readField(body, scope, type, field)); - Casts.tryCast(TypeDeclaration.class, declaration) - .ifPresent(typeDeclaration -> classes.add(readTypeDeclaration(typeDeclaration))); + tryCast(TypeDeclaration.class, declaration) + .ifPresent(typeDeclaration -> classes.add(readNestedTypeDeclaration(type, typeDeclaration))); } return body.build(); } + private TypeNode readNestedTypeDeclaration(TypeName outerType, 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); + nestedClasses.put(node.getName(), outerType); + return closure.getClassNode(); + }) + .orElse(typeNode); + } + private void readField(TypeDeclarationBody.Builder builder, Scope scope, TypeName declaringType, FieldDeclaration field) { @SuppressWarnings("unchecked") List<VariableDeclarationFragment> fragments = field.fragments(); diff --git a/src/main/java/org/zwobble/couscous/util/ExtraMaps.java b/src/main/java/org/zwobble/couscous/util/ExtraMaps.java index 09339e6..55829cf 100644 --- a/src/main/java/org/zwobble/couscous/util/ExtraMaps.java +++ b/src/main/java/org/zwobble/couscous/util/ExtraMaps.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap; import java.util.AbstractMap; import java.util.Map; +import java.util.Optional; import java.util.function.Function; public class ExtraMaps { @@ -23,6 +24,10 @@ public class ExtraMaps { return ImmutableMap.of(key1, value1, key2, value2, key3, value3); } + public static <K, V> Optional<V> lookup(Map<K, V> map, K key) { + return Optional.ofNullable(map.get(key)); + } + public static <T, K, V> Map<K, V> toMap(Iterable<T> iterable, Function<T, Map.Entry<K, V>> function) { ImmutableMap.Builder<K, V> builder = ImmutableMap.builder(); |
