diff options
Diffstat (limited to 'src/main')
11 files changed, 103 insertions, 57 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/ConstructorCallNode.java b/src/main/java/org/zwobble/couscous/ast/ConstructorCallNode.java index ab4b1ea..cad359d 100644 --- a/src/main/java/org/zwobble/couscous/ast/ConstructorCallNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ConstructorCallNode.java @@ -10,16 +10,16 @@ import static org.zwobble.couscous.util.ExtraLists.eagerMap; public class ConstructorCallNode implements ExpressionNode { public static ConstructorCallNode constructorCall( TypeName type, - List<ExpressionNode> arguments) { + List<? extends ExpressionNode> arguments) { return new ConstructorCallNode(type, arguments); } private final TypeName type; - private final List<ExpressionNode> arguments; + private final List<? extends ExpressionNode> arguments; public ConstructorCallNode( TypeName type, - List<ExpressionNode> arguments) { + List<? extends ExpressionNode> arguments) { this.type = type; this.arguments = arguments; } @@ -29,7 +29,7 @@ public class ConstructorCallNode implements ExpressionNode { return type; } - public List<ExpressionNode> getArguments() { + public List<? extends ExpressionNode> getArguments() { return arguments; } diff --git a/src/main/java/org/zwobble/couscous/ast/ReferenceNode.java b/src/main/java/org/zwobble/couscous/ast/ReferenceNode.java new file mode 100644 index 0000000..6e76fcd --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/ReferenceNode.java @@ -0,0 +1,10 @@ +package org.zwobble.couscous.ast; + +public interface ReferenceNode extends ExpressionNode { + interface Visitor<T> { + T visit(VariableReferenceNode variableReference); + T visit(ThisReferenceNode thisReference); + } + + <T> T accept(Visitor<T> visitor); +} diff --git a/src/main/java/org/zwobble/couscous/ast/ThisReferenceNode.java b/src/main/java/org/zwobble/couscous/ast/ThisReferenceNode.java index 3ef5527..a4851c8 100644 --- a/src/main/java/org/zwobble/couscous/ast/ThisReferenceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ThisReferenceNode.java @@ -4,7 +4,7 @@ import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import java.util.function.Function; -public class ThisReferenceNode implements ExpressionNode { +public class ThisReferenceNode implements ExpressionNode, ReferenceNode { public static ThisReferenceNode thisReference(TypeName type) { return new ThisReferenceNode(type); } @@ -30,6 +30,11 @@ public class ThisReferenceNode implements ExpressionNode { } @Override + public <T> T accept(Visitor<T> visitor) { + return visitor.visit(this); + } + + @Override public String toString() { return "ThisReferenceNode(type=" + type + ")"; } diff --git a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java index 50462e9..c1b022a 100644 --- a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java +++ b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java @@ -6,7 +6,7 @@ import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; import java.util.function.Function; -public class VariableReferenceNode implements AssignableExpressionNode { +public class VariableReferenceNode implements AssignableExpressionNode, ReferenceNode { public static VariableReferenceNode reference(VariableNode variable) { return new VariableReferenceNode(variable.getDeclaration()); } @@ -50,6 +50,11 @@ public class VariableReferenceNode implements AssignableExpressionNode { } @Override + public <T> T accept(Visitor<T> visitor) { + return visitor.visit(this); + } + + @Override public String toString() { return "VariableReferenceNode(referent=" + referent + ")"; } diff --git a/src/main/java/org/zwobble/couscous/ast/structure/NodeStructure.java b/src/main/java/org/zwobble/couscous/ast/structure/NodeStructure.java index 6054711..47ba9b6 100644 --- a/src/main/java/org/zwobble/couscous/ast/structure/NodeStructure.java +++ b/src/main/java/org/zwobble/couscous/ast/structure/NodeStructure.java @@ -70,7 +70,7 @@ public class NodeStructure { } @Override - public Stream<ExpressionNode> visit(ConstructorCallNode call) { + public Stream<? extends ExpressionNode> visit(ConstructorCallNode call) { return call.getArguments().stream(); } diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java index 574854d..913b1b7 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java @@ -194,7 +194,7 @@ public class PythonCodeGenerator { return expression.accept(EXPRESSION_GENERATOR); } - private static List<PythonExpressionNode> generateExpressions(Iterable<ExpressionNode> expressions) { + private static List<PythonExpressionNode> generateExpressions(Iterable<? extends ExpressionNode> expressions) { return ImmutableList.copyOf(Iterables.transform(expressions, PythonCodeGenerator::generateExpression)); } private static final ExpressionGenerator EXPRESSION_GENERATOR = new ExpressionGenerator(); 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 99cc9b4..00184b1 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/FreeVariables.java @@ -1,9 +1,9 @@ package org.zwobble.couscous.frontends.java; -import com.google.common.collect.Sets; import org.zwobble.couscous.ast.*; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -12,19 +12,23 @@ import static org.zwobble.couscous.util.Casts.tryCast; import static org.zwobble.couscous.util.ExtraStreams.toStream; public class FreeVariables { - public static List<VariableDeclaration> findFreeVariables(List<? extends Node> body) { - Stream<VariableDeclaration> referencedDeclarations = body.stream() - .flatMap(FreeVariables::findReferencedDeclarations); - Stream<VariableDeclaration> declarations = body.stream().flatMap(FreeVariables::findDeclarations); - return Sets.difference( - referencedDeclarations.collect(Collectors.toSet()), - declarations.collect(Collectors.toSet())).stream().collect(Collectors.toList()); + public static List<ReferenceNode> findFreeVariables(List<? extends Node> body) { + Set<VariableDeclaration> declarations = body.stream() + .flatMap(FreeVariables::findDeclarations) + .collect(Collectors.toSet()); + + return body.stream() + .flatMap(FreeVariables::findReferences) + .filter(reference -> + tryCast(VariableReferenceNode.class, reference) + .map(variableReference -> !declarations.contains(variableReference.getReferent())) + .orElse(true)) + .collect(Collectors.toList()); } - private static Stream<VariableDeclaration> findReferencedDeclarations(Node root) { - Stream<VariableReferenceNode> references = descendantNodesAndSelf(root).flatMap(node -> - toStream(tryCast(VariableReferenceNode.class, node))); - return references.map(VariableReferenceNode::getReferent); + private static Stream<ReferenceNode> findReferences(Node root) { + return descendantNodesAndSelf(root).flatMap(node -> + toStream(tryCast(ReferenceNode.class, node))); } private static Stream<VariableDeclaration> findDeclarations(Node root) { 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 88d214c..9386ff6 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/GeneratedClosure.java @@ -1,6 +1,7 @@ package org.zwobble.couscous.frontends.java; import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ReferenceNode; import org.zwobble.couscous.ast.TypeName; import org.zwobble.couscous.ast.VariableDeclaration; @@ -8,9 +9,9 @@ import java.util.List; public class GeneratedClosure { private final ClassNode classNode; - private final List<VariableDeclaration> captures; + private final List<ReferenceNode> captures; - public GeneratedClosure(ClassNode classNode, List<VariableDeclaration> captures) { + public GeneratedClosure(ClassNode classNode, List<ReferenceNode> captures) { this.classNode = classNode; this.captures = captures; } @@ -23,7 +24,7 @@ public class GeneratedClosure { return classNode.getName(); } - public List<VariableDeclaration> getCaptures() { + public List<ReferenceNode> getCaptures() { return captures; } } 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 13bc519..367a34f 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaExpressionReader.java @@ -17,7 +17,6 @@ import static org.zwobble.couscous.ast.MethodCallNode.not; import static org.zwobble.couscous.frontends.java.JavaOperators.readOperator; import static org.zwobble.couscous.frontends.java.JavaTypes.typeOf; import static org.zwobble.couscous.util.ExtraLists.concat; -import static org.zwobble.couscous.util.ExtraLists.eagerMap; public class JavaExpressionReader { private final Scope scope; @@ -232,8 +231,8 @@ public class JavaExpressionReader { captureArguments(closure)); } - private List<ExpressionNode> captureArguments(GeneratedClosure closure) { - return eagerMap(closure.getCaptures(), VariableReferenceNode::reference); + private List<? extends ExpressionNode> captureArguments(GeneratedClosure closure) { + return closure.getCaptures(); } private List<ExpressionNode> readArguments(IMethodBinding method, List<Expression> javaArguments) { 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 1c53a44..5e81ec9 100644 --- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java +++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java @@ -32,7 +32,6 @@ 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.toMap; @@ -109,51 +108,47 @@ public class JavaReader { private List<StatementNode> replaceCaptureReferences( TypeName className, List<StatementNode> body, - List<VariableDeclaration> freeVariables) + List<CapturedVariable> freeVariables) { - Map<Identifier, ExpressionNode> freeVariablesById = Maps.transformValues( - Maps.uniqueIndex(freeVariables, VariableDeclaration::getId), + Map<ExpressionNode, ExpressionNode> replacements = Maps.transformValues( + Maps.uniqueIndex(freeVariables, variable -> variable.freeVariable), freeVariable -> captureAccess(className, freeVariable)); - Function<ExpressionNode, ExpressionNode> replaceExpression = new CaptureReplacer(freeVariablesById); + Function<ExpressionNode, ExpressionNode> replaceExpression = new CaptureReplacer(replacements); return eagerMap(body, statement -> statement.replaceExpressions(replaceExpression)); } private class CaptureReplacer implements Function<ExpressionNode, ExpressionNode> { - private final Map<Identifier, ExpressionNode> freeVariablesById; + private final Map<ExpressionNode, ExpressionNode> replacements; - public CaptureReplacer(Map<Identifier, ExpressionNode> freeVariablesById) { - this.freeVariablesById = freeVariablesById; + public CaptureReplacer(Map<ExpressionNode, ExpressionNode> replacements) { + this.replacements = replacements; } @Override public ExpressionNode apply(ExpressionNode expression) { - return tryCast(VariableReferenceNode.class, expression) - .flatMap(variableNode -> Optional.ofNullable(freeVariablesById.get(variableNode.getReferentId()))) + return Optional.ofNullable(replacements.get(expression)) .orElseGet(() -> expression.replaceExpressions(this)); } } - private ConstructorNode buildConstructor(Scope outerScope, TypeName type, List<VariableDeclaration> freeVariables) { + private ConstructorNode buildConstructor(Scope outerScope, TypeName type, List<CapturedVariable> freeVariables) { Scope scope = outerScope.enterConstructor(); - Map<Identifier, VariableDeclaration> argumentDeclarationsById = toMap( - freeVariables, - freeVariable -> immutableEntry( - freeVariable.getId(), - scope.generateVariable(freeVariable.getName(), freeVariable.getType()))); - List<FormalArgumentNode> arguments = eagerMap( - freeVariables, - freeVariable -> formalArg(argumentDeclarationsById.get(freeVariable.getId()))); + ImmutableList.Builder<FormalArgumentNode> arguments = ImmutableList.builder(); + ImmutableList.Builder<StatementNode> body = ImmutableList.builder(); - List<StatementNode> body = eagerMap(freeVariables, freeVariable -> assignStatement( - captureAccess(type, freeVariable), - reference(argumentDeclarationsById.get(freeVariable.getId())))); + 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))); + } - return constructor(arguments, body); + return constructor(arguments.build(), body.build()); } - private FieldAccessNode captureAccess(TypeName type, VariableDeclaration freeVariable) { - return fieldAccess(thisReference(type), freeVariable.getName(), freeVariable.getType()); + private FieldAccessNode captureAccess(TypeName type, CapturedVariable freeVariable) { + FieldDeclarationNode field = freeVariable.field; + return fieldAccess(thisReference(type), field.getName(), field.getType()); } GeneratedClosure readAnonymousClass(Scope outerScope, AnonymousClassDeclaration declaration) { @@ -170,6 +165,16 @@ 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, TypeName className, @@ -177,10 +182,11 @@ public class JavaReader { List<FieldDeclarationNode> declaredFields, List<MethodNode> methods ) { - List<VariableDeclaration> freeVariables = findFreeVariables(ExtraLists.concat(declaredFields, methods)); - Iterable<FieldDeclarationNode> captureFields = transform( + List<ReferenceNode> freeVariables = findFreeVariables(ExtraLists.concat(declaredFields, methods)); + List<CapturedVariable> capturedVariables = ImmutableList.copyOf(transform( freeVariables, - freeVariable -> field(freeVariable.getName(), freeVariable.getType())); + freeVariable -> new CapturedVariable(freeVariable, fieldForCapture(freeVariable)))); + Iterable<FieldDeclarationNode> captureFields = transform(capturedVariables, capture -> capture.field); List<FieldDeclarationNode> fields = ImmutableList.copyOf(concat(declaredFields, captureFields)); @@ -188,12 +194,28 @@ public class JavaReader { className, superTypes, fields, - buildConstructor(scope, className, freeVariables), + buildConstructor(scope, className, capturedVariables), eagerMap(methods, method -> - method.mapBody(body -> replaceCaptureReferences(className, body, freeVariables)))); + method.mapBody(body -> replaceCaptureReferences(className, body, capturedVariables)))); return new GeneratedClosure(classNode, freeVariables); } + 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) { + TypeName type = thisReference.getType(); + String name = "this_" + type.getQualifiedName().replace(".", "__"); + return field(name, type); + } + }); + } + private TypeName generateAnonymousName(ASTNode node) { ITypeBinding type = findDeclaringClass(node); while (type.isAnonymous()) { diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java index 548f731..3e01f36 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java @@ -108,7 +108,7 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> { return left.getField(fieldAccess.getFieldName()); } - private List<InterpreterValue> evalArguments(List<ExpressionNode> arguments) { + private List<InterpreterValue> evalArguments(List<? extends ExpressionNode> arguments) { return arguments.stream() .map(argument -> eval(argument)) .collect(Collectors.toList()); |
