aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java5
-rw-r--r--src/main/java/org/zwobble/couscous/ast/InterfaceNode.java50
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java16
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java19
-rw-r--r--src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java25
-rw-r--r--src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java68
-rw-r--r--src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java23
7 files changed, 172 insertions, 34 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
index a567254..4735a2e 100644
--- a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
+++ b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
@@ -127,7 +127,10 @@ public class ClassNodeBuilder {
name,
typeParameters.build(),
superTypes.build(),
- methods.build());
+ fields.build(),
+ staticConstructor,
+ methods.build()
+ );
}
public class MethodBuilder<T> {
diff --git a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
index de151e4..c0444a4 100644
--- a/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/InterfaceNode.java
@@ -12,26 +12,34 @@ public class InterfaceNode implements TypeNode {
ScalarType name,
List<FormalTypeParameterNode> typeParameters,
Set<Type> superTypes,
+ List<FieldDeclarationNode> fields,
+ List<StatementNode> staticConstructor,
List<MethodNode> methods)
{
- return new InterfaceNode(name, typeParameters, superTypes, methods);
+ return new InterfaceNode(name, typeParameters, superTypes, fields, staticConstructor, methods);
}
private final ScalarType name;
private final List<FormalTypeParameterNode> typeParameters;
private final Set<Type> superTypes;
+ private final List<FieldDeclarationNode> fields;
+ private final List<StatementNode> staticConstructor;
private final List<MethodNode> methods;
private InterfaceNode(
ScalarType name,
List<FormalTypeParameterNode> typeParameters,
Set<Type> superTypes,
- List<MethodNode> methodNodes)
+ List<FieldDeclarationNode> fields,
+ List<StatementNode> staticConstructor,
+ List<MethodNode> methods)
{
this.name = name;
this.typeParameters = typeParameters;
this.superTypes = superTypes;
- methods = methodNodes;
+ this.fields = fields;
+ this.staticConstructor = staticConstructor;
+ this.methods = methods;
}
@Override
@@ -49,19 +57,17 @@ public class InterfaceNode implements TypeNode {
return superTypes;
}
- @Override
- public List<MethodNode> getMethods() {
- return methods;
+ public List<FieldDeclarationNode> getFields() {
+ return fields;
+ }
+
+ public List<StatementNode> getStaticConstructor() {
+ return staticConstructor;
}
@Override
- public String toString() {
- return "InterfaceNode(" +
- "name=" + name +
- ", typeParameters=" + typeParameters +
- ", superTypes=" + superTypes +
- ", methods=" + methods +
- ')';
+ public List<MethodNode> getMethods() {
+ return methods;
}
@Override
@@ -80,10 +86,24 @@ public class InterfaceNode implements TypeNode {
transformer.transform(name),
transformer.transformFormalTypeParameters(typeParameters),
transformer.transformTypes(superTypes),
+ transformer.transformFields(fields),
+ transformer.transformStatements(staticConstructor),
transformer.transformMethods(methods));
}
@Override
+ public String toString() {
+ return "InterfaceNode(" +
+ "name=" + name +
+ ", typeParameters=" + typeParameters +
+ ", superTypes=" + superTypes +
+ ", fields=" + fields +
+ ", staticConstructor=" + staticConstructor +
+ ", methods=" + methods +
+ ')';
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -93,6 +113,8 @@ public class InterfaceNode implements TypeNode {
if (!name.equals(that.name)) return false;
if (!typeParameters.equals(that.typeParameters)) return false;
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);
}
@@ -102,6 +124,8 @@ public class InterfaceNode implements TypeNode {
int result = name.hashCode();
result = 31 * result + typeParameters.hashCode();
result = 31 * result + superTypes.hashCode();
+ result = 31 * result + fields.hashCode();
+ result = 31 * result + staticConstructor.hashCode();
result = 31 * result + methods.hashCode();
return result;
}
diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java
index b2255d2..bcc920e 100644
--- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java
@@ -45,6 +45,7 @@ public class NodeTransformer {
private Function<Type, Type> transformType = type -> type;
private Function<MethodSignature, String> transformMethodName = MethodSignature::getName;
private Function<String, String> transformFieldName = name -> name;
+ private Function<Receiver, Receiver> transformReceiver = receiver -> receiver;
public Builder transformExpression(
Function<ExpressionNode, Optional<ExpressionNode>> transformExpression)
@@ -77,13 +78,19 @@ public class NodeTransformer {
return this;
}
+ public Builder transformReceiver(Function<Receiver, Receiver> receiver) {
+ this.transformReceiver = receiver;
+ return this;
+ }
+
public NodeTransformer build() {
return new NodeTransformer(
transformExpression,
transformStatement,
transformType,
transformMethodName,
- transformFieldName
+ transformFieldName,
+ transformReceiver
);
}
}
@@ -93,19 +100,22 @@ public class NodeTransformer {
private final Function<Type, Type> transformType;
private final Function<MethodSignature, String> transformMethodName;
private final Function<String, String> transformFieldName;
+ private final Function<Receiver, Receiver> transformReceiver;
private NodeTransformer(
Function<ExpressionNode, Optional<ExpressionNode>> transformExpression,
Function<StatementNode, Optional<List<StatementNode>>> transformStatement,
Function<Type, Type> transformType,
Function<MethodSignature, String> transformMethodName,
- Function<String, String> transformFieldName)
+ Function<String, String> transformFieldName,
+ Function<Receiver, Receiver> transformReceiver)
{
this.transformExpression = transformExpression;
this.transformStatement = transformStatement;
this.transformType = transformType;
this.transformMethodName = transformMethodName;
this.transformFieldName = transformFieldName;
+ this.transformReceiver = transformReceiver;
}
public MethodSignature transform(MethodSignature signature) {
@@ -142,7 +152,7 @@ public class NodeTransformer {
}
public Receiver transformReceiver(Receiver receiver) {
- return receiver.transformSubtree(this);
+ return transformReceiver.apply(receiver.transformSubtree(this));
}
public VariableDeclaration transform(VariableDeclaration declaration) {
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 e576102..47a7635 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.SplitStaticsFromInterface;
import org.zwobble.couscous.types.*;
import java.util.List;
@@ -47,14 +48,16 @@ public class CsharpCodeGenerator {
}
private List<TypeNode> generateCode(List<TypeNode> types) {
- return NodeTransformer.applyAll(
- list(
- DesugarForEachToFor.transformer(),
- DesugarForToWhile.transformer(),
- CsharpPrimitiveMethods.TRANSFORMER,
- nodeTransformer
- ),
- types
+ return SplitStaticsFromInterface.transform(
+ NodeTransformer.applyAll(
+ list(
+ DesugarForEachToFor.transformer(),
+ DesugarForToWhile.transformer(),
+ CsharpPrimitiveMethods.TRANSFORMER,
+ nodeTransformer
+ ),
+ types
+ )
);
}
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 1c4ac81..5202127 100644
--- a/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
+++ b/src/main/java/org/zwobble/couscous/frontends/java/JavaReader.java
@@ -115,11 +115,18 @@ public class JavaReader {
ScalarType name = erasure(typeOf(type.resolveBinding()));
Scope scope = topScope.enterClass(name);
List<FormalTypeParameterNode> typeParameters = readTypeParameters(scope.getIdentifier(), type);
- TypeDeclarationBody body = readTypeDeclarationBody(scope, name, type.bodyDeclarations());
+ TypeDeclarationBody body = readTypeDeclarationBody(scope, name, type.bodyDeclarations(), type.isInterface());
Set<Type> superTypes = superTypes(type);
if (type.isInterface()) {
// TODO: throw exception if other parts of body are declared
- return InterfaceNode.declareInterface(name, typeParameters, superTypes, body.getMethods());
+ return InterfaceNode.declareInterface(
+ name,
+ typeParameters,
+ superTypes,
+ body.getFields(),
+ body.getStaticConstructor(),
+ body.getMethods()
+ );
} else {
return ClassNode.declareClass(
name,
@@ -233,7 +240,7 @@ public class JavaReader {
GeneratedClosure readAnonymousClass(Scope outerScope, AnonymousClassDeclaration declaration) {
ScalarType className = generateAnonymousName(declaration);
Scope scope = outerScope.enterClass(className);
- TypeDeclarationBody bodyDeclarations = readTypeDeclarationBody(scope, className, declaration.bodyDeclarations());
+ TypeDeclarationBody bodyDeclarations = readTypeDeclarationBody(scope, className, declaration.bodyDeclarations(), false);
AnonymousClass anonymousClass = new AnonymousClass(
Optional.of((AnonymousType) typeOf(declaration.resolveBinding())),
superTypes(declaration),
@@ -357,7 +364,7 @@ public class JavaReader {
return ((AbstractTypeDeclaration)node).resolveBinding();
}
- private TypeDeclarationBody readTypeDeclarationBody(Scope scope, ScalarType type, List<Object> bodyDeclarations) {
+ private TypeDeclarationBody readTypeDeclarationBody(Scope scope, ScalarType type, List<Object> bodyDeclarations, boolean isInterface) {
TypeDeclarationBody.Builder body = TypeDeclarationBody.builder();
for (Object declaration : bodyDeclarations) {
@@ -370,7 +377,7 @@ public class JavaReader {
readStatementBody(scope, initializer.getBody())));
tryCast(FieldDeclaration.class, declaration)
- .ifPresent(field -> readField(body, scope, type, field));
+ .ifPresent(field -> readField(body, scope, type, field, isInterface));
tryCast(TypeDeclaration.class, declaration)
.ifPresent(typeDeclaration -> classes.add(readNestedTypeDeclaration(body, typeDeclaration)));
@@ -406,22 +413,22 @@ public class JavaReader {
.orElse(typeNode);
}
- private void readField(TypeDeclarationBody.Builder builder, Scope scope, ScalarType declaringType, FieldDeclaration field) {
+ private void readField(TypeDeclarationBody.Builder builder, Scope scope, ScalarType declaringType, FieldDeclaration field, boolean isInterface) {
@SuppressWarnings("unchecked")
List<VariableDeclarationFragment> fragments = field.fragments();
+ boolean isStatic = isInterface || Modifier.isStatic(field.getModifiers());
Type type = typeOf(field.getType());
fragments.forEach(fragment -> {
- builder.field(field(Modifier.isStatic(field.getModifiers()), fragment.getName().getIdentifier(), type));
+ builder.field(field(isStatic, fragment.getName().getIdentifier(), type));
if (fragment.getInitializer() != null) {
ExpressionNode value = readExpression(scope, type, fragment.getInitializer());
String name = fragment.getName().getIdentifier();
- boolean isStatic = Modifier.isStatic(field.getModifiers());
Receiver receiver = isStatic
? staticReceiver(declaringType)
: instanceReceiver(thisReference(declaringType));
StatementNode assignment = assignStatement(fieldAccess(receiver, name, type), value);
- builder.addInitializer(isStatic,assignment);
+ builder.addInitializer(isStatic, assignment);
}
});
diff --git a/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java b/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java
new file mode 100644
index 0000000..b2c88ea
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/transforms/SplitStaticsFromInterface.java
@@ -0,0 +1,68 @@
+package org.zwobble.couscous.transforms;
+
+import com.google.common.collect.Iterables;
+import org.zwobble.couscous.ast.*;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.types.ScalarType;
+
+import java.util.*;
+
+import static org.zwobble.couscous.util.ExtraLists.eagerFilter;
+import static org.zwobble.couscous.util.ExtraLists.list;
+import static org.zwobble.couscous.util.ExtraSets.set;
+
+public class SplitStaticsFromInterface {
+ public static List<TypeNode> transform(List<TypeNode> typeNodes) {
+ List<TypeNode> result = new ArrayList<>();
+ Set<ScalarType> splitInterfaces = new HashSet<>();
+
+ for (TypeNode typeNode : typeNodes) {
+ if (isSplittable(typeNode)) {
+ splitInterfaces.add(typeNode.getName());
+ InterfaceNode interfaceNode = (InterfaceNode) typeNode;
+ result.add(ClassNode.declareClass(
+ staticTypeFor(interfaceNode.getName()),
+ list(),
+ set(),
+ interfaceNode.getFields(),
+ interfaceNode.getStaticConstructor(),
+ ConstructorNode.DEFAULT,
+ eagerFilter(interfaceNode.getMethods(), method -> method.isStatic())
+ ));
+ result.add(InterfaceNode.declareInterface(
+ interfaceNode.getName(),
+ interfaceNode.getTypeParameters(),
+ interfaceNode.getSuperTypes(),
+ list(),
+ list(),
+ eagerFilter(interfaceNode.getMethods(), method -> !method.isStatic())
+ ));
+ } else {
+ result.add(typeNode);
+ }
+ }
+
+ NodeTransformer transformer = NodeTransformer.builder()
+ .transformReceiver((Receiver receiver) -> {
+ if (receiver instanceof StaticReceiver && splitInterfaces.contains(((StaticReceiver) receiver).getType())) {
+ return StaticReceiver.staticReceiver(staticTypeFor(((StaticReceiver) receiver).getType()));
+ } else {
+ return receiver;
+ }
+ })
+ .build();
+ return NodeTransformer.apply(transformer, result);
+ }
+
+ private static ScalarType staticTypeFor(ScalarType name) {
+ return ScalarType.of(name.getQualifiedName() + "_static");
+ }
+
+ private static boolean isSplittable(TypeNode typeNode) {
+ if (!(typeNode instanceof InterfaceNode)) {
+ return false;
+ }
+ InterfaceNode interfaceNode = (InterfaceNode) typeNode;
+ return !interfaceNode.getFields().isEmpty() || Iterables.any(interfaceNode.getMethods(), MethodNode::isStatic);
+ }
+}
diff --git a/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java b/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java
index 73745b2..7b208f5 100644
--- a/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/frontends/java/JavaReaderTests.java
@@ -18,6 +18,7 @@ import static org.zwobble.couscous.ast.AssignmentNode.assignStatement;
import static org.zwobble.couscous.ast.ConstructorCallNode.constructorCall;
import static org.zwobble.couscous.ast.ExpressionStatementNode.expressionStatement;
import static org.zwobble.couscous.ast.FieldAccessNode.fieldAccess;
+import static org.zwobble.couscous.ast.FieldDeclarationNode.staticField;
import static org.zwobble.couscous.ast.FormalTypeParameterNode.formalTypeParameter;
import static org.zwobble.couscous.ast.IfStatementNode.ifStatement;
import static org.zwobble.couscous.ast.LiteralNode.UNIT;
@@ -479,6 +480,28 @@ public class JavaReaderTests {
}
@Test
+ public void canReadInterfaceWithField() {
+ String source = "package com.example;" +
+ "public interface Example {" +
+ "final int VALUE = 1;" +
+ "}";
+ List<TypeNode> classes = readSource("com/example/Example.java", source);
+ assertThat(classes, hasSize(1));
+ InterfaceNode classNode = (InterfaceNode) classes.get(0);
+
+ assertEquals(list(staticField("VALUE", Types.INT)), classNode.getFields());
+ assertEquals(
+ list(
+ assignStatement(
+ FieldAccessNode.fieldAccess(ScalarType.of("com.example.Example"), "VALUE", Types.INT),
+ literal(1)
+ )
+ ),
+ classNode.getStaticConstructor()
+ );
+ }
+
+ @Test
public void canReadEmptyEnum() {
String source = "package com.example;" +
"public enum Example {}";