aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/zwobble/couscous/ast/NodeTypes.java5
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java8
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java33
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpNodeTypes.java6
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpPrimitiveMethods.java140
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java10
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/primitives/CsharpPrimitiveMethods.java195
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethod.java25
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethodCall.java51
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethodGenerator.java10
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethod.java25
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethodCall.java46
-rw-r--r--src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethodGenerator.java10
-rw-r--r--src/test/java/org/zwobble/couscous/tests/ast/visitors/NodeTransformerTests.java57
14 files changed, 446 insertions, 175 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/NodeTypes.java b/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
index 41ba8af..9a9a244 100644
--- a/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
+++ b/src/main/java/org/zwobble/couscous/ast/NodeTypes.java
@@ -3,6 +3,9 @@ package org.zwobble.couscous.ast;
import com.google.common.collect.ImmutableMap;
import org.zwobble.couscous.ast.sugar.SwitchCaseNode;
import org.zwobble.couscous.ast.sugar.SwitchNode;
+import org.zwobble.couscous.backends.csharp.CsharpNodeTypes;
+import org.zwobble.couscous.backends.csharp.primitives.PrimitiveInstanceMethodCall;
+import org.zwobble.couscous.backends.csharp.primitives.PrimitiveStaticMethodCall;
import java.util.Map;
@@ -80,6 +83,8 @@ public class NodeTypes {
.put(TypeCoercionNode.class, TYPE_COERCION)
.put(VariableReferenceNode.class, VARIABLE_REFERENCE)
.put(WhileNode.class, WHILE)
+ .put(PrimitiveInstanceMethodCall.class, CsharpNodeTypes.PRIMITIVE_INSTANCE_METHOD_CALL)
+ .put(PrimitiveStaticMethodCall.class, CsharpNodeTypes.PRIMITIVE_STATIC_METHOD_CALL)
.build();
public static int forClass(Class<?> nodeClass) {
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 78bd6ef..296e5c7 100644
--- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeTransformer.java
@@ -126,8 +126,8 @@ public class NodeTransformer {
}
public ExpressionNode transformExpression(ExpressionNode expression) {
- return transformExpression.apply(expression)
- .orElseGet(() -> expression.transform(this));
+ ExpressionNode transformed = expression.transform(this);
+ return transformExpression.apply(transformed).orElse(transformed);
}
public Receiver transformReceiver(Receiver receiver) {
@@ -139,8 +139,8 @@ public class NodeTransformer {
}
public final Iterable<StatementNode> transformStatement(StatementNode statement) {
- return transformStatement.apply(statement)
- .orElseGet(() -> list(statement.transform(this)));
+ StatementNode transformed = statement.transform(this);
+ return transformStatement.apply(transformed).orElseGet(() -> list(transformed));
}
public FormalTypeParameterNode transformFormalTypeParameter(FormalTypeParameterNode typeParameter) {
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 537184c..4dba10e 100644
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpCodeGenerator.java
@@ -1,19 +1,14 @@
package org.zwobble.couscous.backends.csharp;
import com.google.common.collect.ImmutableMap;
-import org.zwobble.couscous.ast.ExpressionNode;
-import org.zwobble.couscous.ast.MethodCallNode;
import org.zwobble.couscous.ast.Node;
-import org.zwobble.couscous.ast.Receiver;
import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.backends.csharp.primitives.CsharpPrimitiveMethods;
import org.zwobble.couscous.backends.naming.Naming;
import org.zwobble.couscous.types.*;
import java.util.Map;
-import java.util.Optional;
-import static org.zwobble.couscous.types.Types.erasure;
-import static org.zwobble.couscous.util.Casts.tryCast;
import static org.zwobble.couscous.util.ExtraLists.eagerMap;
public class CsharpCodeGenerator {
@@ -41,14 +36,13 @@ public class CsharpCodeGenerator {
this.namespace = namespace;
nodeTransformer = NodeTransformer.builder()
.transformType(this::transformType)
- .transformExpression(this::transformExpression)
.transformMethodName(naming::methodName)
.transformFieldName(name -> "_" + name)
.build();
}
private Node generateCode(Node node) {
- return node.transform(nodeTransformer);
+ return node.transform(CsharpPrimitiveMethods.TRANSFORMER).transform(nodeTransformer);
}
private Type transformType(Type type) {
@@ -87,29 +81,6 @@ public class CsharpCodeGenerator {
});
}
- private Optional<ExpressionNode> transformExpression(ExpressionNode expression) {
- return tryCast(MethodCallNode.class, expression)
- .flatMap(this::transformMethodCall);
- }
-
- private Optional<ExpressionNode> transformMethodCall(MethodCallNode call) {
- return call.getReceiver().accept(new Receiver.Mapper<Optional<ExpressionNode>>() {
- @Override
- public Optional<ExpressionNode> visit(ExpressionNode receiver) {
- return CsharpPrimitiveMethods.getPrimitiveMethod(erasure(receiver.getType()), call.getMethodName())
- .map(generator -> generator.generate(
- nodeTransformer.transformExpression(receiver),
- nodeTransformer.transformExpressions(call.getArguments())));
- }
-
- @Override
- public Optional<ExpressionNode> visit(ScalarType receiver) {
- return CsharpPrimitiveMethods.getPrimitiveStaticMethod(receiver, call.getMethodName())
- .map(generator -> generator.generate(nodeTransformer.transformExpressions(call.getArguments())));
- }
- });
- }
-
private Type addPrefix(Type type, String namespace) {
return type.accept(new Type.Visitor<Type>() {
@Override
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpNodeTypes.java b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpNodeTypes.java
new file mode 100644
index 0000000..11be7e5
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpNodeTypes.java
@@ -0,0 +1,6 @@
+package org.zwobble.couscous.backends.csharp;
+
+public class CsharpNodeTypes {
+ public static final int PRIMITIVE_INSTANCE_METHOD_CALL = 20000;
+ public static final int PRIMITIVE_STATIC_METHOD_CALL = 20001;
+}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpPrimitiveMethods.java b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpPrimitiveMethods.java
deleted file mode 100644
index e5c9260..0000000
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpPrimitiveMethods.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package org.zwobble.couscous.backends.csharp;
-
-import com.google.common.collect.ImmutableMap;
-import org.zwobble.couscous.ast.ExpressionNode;
-import org.zwobble.couscous.ast.Operator;
-import org.zwobble.couscous.types.ScalarType;
-import org.zwobble.couscous.types.Types;
-import org.zwobble.couscous.values.InternalCouscousValue;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-import static org.zwobble.couscous.ast.FieldAccessNode.fieldAccess;
-import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
-import static org.zwobble.couscous.ast.MethodCallNode.staticMethodCall;
-import static org.zwobble.couscous.ast.OperationNode.operation;
-import static org.zwobble.couscous.ast.Operations.integerSubtract;
-import static org.zwobble.couscous.util.ExtraLists.list;
-
-public class CsharpPrimitiveMethods {
- private static final Map<String, PrimitiveMethodGenerator> BOOLEAN_METHODS;
-
- static {
- ImmutableMap.Builder<String, PrimitiveMethodGenerator> methods = ImmutableMap.builder();
- BOOLEAN_METHODS = methods.build();
- }
-
- private static final Map<String, PrimitiveMethodGenerator> BOXED_INT_METHODS;
-
- static {
- ImmutableMap.Builder<String, PrimitiveMethodGenerator> methods = ImmutableMap.builder();
- methods.put("toString", ((receiver, arguments) -> methodCall(
- receiver,
- "ToString",
- list(),
- Types.STRING)));
- BOXED_INT_METHODS = methods.build();
- }
-
- private static final Map<String, PrimitiveMethodGenerator> STRING_METHODS;
-
- static {
- ImmutableMap.Builder<String, PrimitiveMethodGenerator> methods = ImmutableMap.builder();
- methods.put("length", (receiver, arguments) -> fieldAccess(receiver, "Length", Types.INT));
- methods.put("substring", (receiver, arguments) -> {
- ExpressionNode startIndex = arguments.get(0);
- ExpressionNode endIndex = arguments.get(1);
- ExpressionNode length = integerSubtract(endIndex, startIndex);
- // TODO: handle length going beyond end-of-string
- return methodCall(
- receiver,
- "Substring",
- list(startIndex, length),
- Types.STRING);
- });
- methods.put("toLowerCase", (receiver, arguments) -> methodCall(
- receiver,
- "ToLower",
- list(),
- Types.STRING));
- methods.put("equals", (receiver, arguments) -> methodCall(
- receiver,
- "Equals",
- arguments,
- Types.STRING));
- STRING_METHODS = methods.build();
- }
-
- private static final Map<ScalarType, Map<String, PrimitiveMethodGenerator>> METHODS =
- ImmutableMap.<ScalarType, Map<String, PrimitiveMethodGenerator>>builder()
- .put(Types.BOOLEAN, BOOLEAN_METHODS)
- .put(Types.STRING, STRING_METHODS)
- .put(Types.BOXED_INT, BOXED_INT_METHODS)
- .build();
-
- private static final Map<String, PrimitiveStaticMethodGenerator> STATIC_INT_METHODS =
- ImmutableMap.<String, PrimitiveStaticMethodGenerator>builder()
-
- .put("toString", arguments -> methodCall(
- arguments.get(0),
- "ToString",
- list(),
- Types.STRING))
-
- .put("parseInt", arguments -> staticMethodCall(
- ScalarType.of("int"),
- "Parse",
- list(arguments.get(0)),
- ScalarType.of("int")))
-
- .put("valueOf", arguments -> arguments.get(0))
-
- .build();
-
- private static final Map<String, PrimitiveStaticMethodGenerator> STATIC_STRING_METHODS =
- ImmutableMap.<String, PrimitiveStaticMethodGenerator>builder()
-
- .put("join", arguments -> staticMethodCall(
- ScalarType.of("java.lang.String"),
- "join",
- arguments,
- Types.STRING))
-
- .build();
-
- private static final Map<String, PrimitiveStaticMethodGenerator> STATIC_INTERNAL_METHODS =
- ImmutableMap.<String, PrimitiveStaticMethodGenerator>builder()
-
- .put("same", arguments -> operation(Operator.EQUALS, arguments, Types.BOOLEAN))
-
- .build();
-
- private static final Map<ScalarType, Map<String, PrimitiveStaticMethodGenerator>> STATIC_METHODS =
- ImmutableMap.<ScalarType, Map<String, PrimitiveStaticMethodGenerator>>builder()
- .put(Types.BOXED_INT, STATIC_INT_METHODS)
- .put(Types.STRING, STATIC_STRING_METHODS)
- .put(InternalCouscousValue.REF, STATIC_INTERNAL_METHODS)
- .build();
-
- public static Optional<PrimitiveMethodGenerator> getPrimitiveMethod(ScalarType type, String methodName) {
- return Optional.ofNullable(METHODS.get(type))
- .flatMap(methodsForType -> Optional.ofNullable(methodsForType.get(methodName)));
- }
-
- @FunctionalInterface
- public interface PrimitiveMethodGenerator {
- ExpressionNode generate(ExpressionNode receiver, List<ExpressionNode> arguments);
- }
-
- public static Optional<PrimitiveStaticMethodGenerator> getPrimitiveStaticMethod(ScalarType type, String methodName) {
- return Optional.ofNullable(STATIC_METHODS.get(type))
- .flatMap(methodsForType -> Optional.ofNullable(methodsForType.get(methodName)));
- }
-
- @FunctionalInterface
- public interface PrimitiveStaticMethodGenerator {
- ExpressionNode generate(List<ExpressionNode> arguments);
- }
-}
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 6164d04..e343071 100644
--- a/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/CsharpSerializer.java
@@ -7,6 +7,8 @@ import org.zwobble.couscous.ast.sugar.SwitchNode;
import org.zwobble.couscous.ast.visitors.DynamicNodeMapper;
import org.zwobble.couscous.ast.visitors.DynamicNodeVisitor;
import org.zwobble.couscous.backends.SourceCodeWriter;
+import org.zwobble.couscous.backends.csharp.primitives.PrimitiveInstanceMethodCall;
+import org.zwobble.couscous.backends.csharp.primitives.PrimitiveStaticMethodCall;
import org.zwobble.couscous.types.*;
import org.zwobble.couscous.util.Action;
import org.zwobble.couscous.values.PrimitiveValue;
@@ -141,6 +143,14 @@ public class CsharpSerializer {
write.accept(node);
}
+ public void visit(PrimitiveStaticMethodCall expression) {
+ write(expression.generate());
+ }
+
+ public void visit(PrimitiveInstanceMethodCall expression) {
+ write(expression.generate());
+ }
+
public void visit(LiteralNode literal) {
literal.getValue().accept(new PrimitiveValue.Visitor<Void>() {
@Override
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/primitives/CsharpPrimitiveMethods.java b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/CsharpPrimitiveMethods.java
new file mode 100644
index 0000000..6a5c084
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/CsharpPrimitiveMethods.java
@@ -0,0 +1,195 @@
+package org.zwobble.couscous.backends.csharp.primitives;
+
+import com.google.common.collect.ImmutableMap;
+import org.zwobble.couscous.ast.*;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.types.ScalarType;
+import org.zwobble.couscous.types.Types;
+import org.zwobble.couscous.values.InternalCouscousValue;
+
+import java.util.Map;
+import java.util.Optional;
+
+import static org.zwobble.couscous.ast.FieldAccessNode.fieldAccess;
+import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
+import static org.zwobble.couscous.ast.MethodCallNode.staticMethodCall;
+import static org.zwobble.couscous.ast.OperationNode.operation;
+import static org.zwobble.couscous.ast.Operations.boxInt;
+import static org.zwobble.couscous.ast.Operations.integerSubtract;
+import static org.zwobble.couscous.types.Types.erasure;
+import static org.zwobble.couscous.util.Casts.tryCast;
+import static org.zwobble.couscous.util.ExtraLists.list;
+
+public class CsharpPrimitiveMethods {
+ public static final NodeTransformer TRANSFORMER = NodeTransformer.builder()
+ .transformExpression(CsharpPrimitiveMethods::transformExpression)
+ .build();
+
+
+ private static Optional<ExpressionNode> transformExpression(ExpressionNode expression) {
+ return tryCast(MethodCallNode.class, expression)
+ .flatMap(CsharpPrimitiveMethods::transformMethodCall);
+ }
+
+ private static Optional<ExpressionNode> transformMethodCall(MethodCallNode call) {
+ return call.getReceiver().accept(new Receiver.Mapper<Optional<ExpressionNode>>() {
+ @Override
+ public Optional<ExpressionNode> visit(ExpressionNode receiver) {
+ return CsharpPrimitiveMethods.getPrimitiveMethod(erasure(receiver.getType()), call.getMethodName())
+ .map(method -> new PrimitiveInstanceMethodCall(method, receiver, call.getArguments()));
+ }
+
+ @Override
+ public Optional<ExpressionNode> visit(ScalarType receiver) {
+ return CsharpPrimitiveMethods.getPrimitiveStaticMethod(receiver, call.getMethodName())
+ .map(method -> new PrimitiveStaticMethodCall(method, call.getArguments()));
+ }
+ });
+ }
+
+ private static final Map<String, PrimitiveInstanceMethod> BOOLEAN_METHODS;
+
+ static {
+ ImmutableMap.Builder<String, PrimitiveInstanceMethod> methods = ImmutableMap.builder();
+ BOOLEAN_METHODS = methods.build();
+ }
+
+ private static final Map<String, PrimitiveInstanceMethod> BOXED_INT_METHODS;
+
+ static {
+ ImmutableMap.Builder<String, PrimitiveInstanceMethod> methods = ImmutableMap.builder();
+ methods.put("toString", new PrimitiveInstanceMethod(
+ (receiver, arguments) -> methodCall(
+ receiver,
+ "ToString",
+ list(),
+ Types.STRING
+ ),
+ Types.STRING
+ ));
+ BOXED_INT_METHODS = methods.build();
+ }
+
+ private static final Map<String, PrimitiveInstanceMethod> STRING_METHODS;
+
+ static {
+ ImmutableMap.Builder<String, PrimitiveInstanceMethod> methods = ImmutableMap.builder();
+ methods.put("length", new PrimitiveInstanceMethod(
+ (receiver, arguments) -> fieldAccess(receiver, "Length", Types.INT),
+ Types.INT
+ ));
+ methods.put("substring", new PrimitiveInstanceMethod(
+ (receiver, arguments) -> {
+ ExpressionNode startIndex = arguments.get(0);
+ ExpressionNode endIndex = arguments.get(1);
+ ExpressionNode length = integerSubtract(endIndex, startIndex);
+ // TODO: handle length going beyond end-of-string
+ return methodCall(
+ receiver,
+ "Substring",
+ list(startIndex, length),
+ Types.STRING);
+ },
+ Types.STRING
+ ));
+ methods.put("toLowerCase", new PrimitiveInstanceMethod(
+ (receiver, arguments) -> methodCall(
+ receiver,
+ "ToLower",
+ list(),
+ Types.STRING
+ ),
+ Types.STRING
+ ));
+ methods.put("equals", new PrimitiveInstanceMethod(
+ (receiver, arguments) -> methodCall(
+ receiver,
+ "Equals",
+ arguments,
+ Types.STRING
+ ),
+ Types.STRING
+ ));
+ STRING_METHODS = methods.build();
+ }
+
+ private static final Map<ScalarType, Map<String, PrimitiveInstanceMethod>> METHODS =
+ ImmutableMap.<ScalarType, Map<String, PrimitiveInstanceMethod>>builder()
+ .put(Types.BOOLEAN, BOOLEAN_METHODS)
+ .put(Types.STRING, STRING_METHODS)
+ .put(Types.BOXED_INT, BOXED_INT_METHODS)
+ .build();
+
+ private static final Map<String, PrimitiveStaticMethod> STATIC_INT_METHODS =
+ ImmutableMap.<String, PrimitiveStaticMethod>builder()
+
+ .put("toString", new PrimitiveStaticMethod(
+ arguments -> methodCall(
+ arguments.get(0),
+ "ToString",
+ list(),
+ Types.STRING
+ ),
+ Types.STRING
+ ))
+
+ .put("parseInt", new PrimitiveStaticMethod(
+ arguments -> staticMethodCall(
+ ScalarType.of("int"),
+ "Parse",
+ list(arguments.get(0)),
+ ScalarType.of("int")
+ ),
+ Types.INT
+ ))
+
+ .put("valueOf", new PrimitiveStaticMethod(
+ arguments -> boxInt(arguments.get(0)),
+ Types.BOXED_INT
+ ))
+
+ .build();
+
+ private static final Map<String, PrimitiveStaticMethod> STATIC_STRING_METHODS =
+ ImmutableMap.<String, PrimitiveStaticMethod>builder()
+
+ .put("join", new PrimitiveStaticMethod(
+ arguments -> staticMethodCall(
+ ScalarType.of("java.lang.String"),
+ "join",
+ arguments,
+ Types.STRING
+ ),
+ Types.STRING
+ ))
+
+ .build();
+
+ private static final Map<String, PrimitiveStaticMethod> STATIC_INTERNAL_METHODS =
+ ImmutableMap.<String, PrimitiveStaticMethod>builder()
+
+ .put("same", new PrimitiveStaticMethod(
+ arguments -> operation(Operator.EQUALS, arguments, Types.BOOLEAN),
+ Types.BOOLEAN
+ ))
+
+ .build();
+
+ private static final Map<ScalarType, Map<String, PrimitiveStaticMethod>> STATIC_METHODS =
+ ImmutableMap.<ScalarType, Map<String, PrimitiveStaticMethod>>builder()
+ .put(Types.BOXED_INT, STATIC_INT_METHODS)
+ .put(Types.STRING, STATIC_STRING_METHODS)
+ .put(InternalCouscousValue.REF, STATIC_INTERNAL_METHODS)
+ .build();
+
+ private static Optional<PrimitiveInstanceMethod> getPrimitiveMethod(ScalarType type, String methodName) {
+ return Optional.ofNullable(METHODS.get(type))
+ .flatMap(methodsForType -> Optional.ofNullable(methodsForType.get(methodName)));
+ }
+
+ private static Optional<PrimitiveStaticMethod> getPrimitiveStaticMethod(ScalarType type, String methodName) {
+ return Optional.ofNullable(STATIC_METHODS.get(type))
+ .flatMap(methodsForType -> Optional.ofNullable(methodsForType.get(methodName)));
+ }
+
+}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethod.java b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethod.java
new file mode 100644
index 0000000..bb94145
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethod.java
@@ -0,0 +1,25 @@
+package org.zwobble.couscous.backends.csharp.primitives;
+
+import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.Node;
+import org.zwobble.couscous.types.Type;
+
+import java.util.List;
+
+class PrimitiveInstanceMethod {
+ private final PrimitiveInstanceMethodGenerator generate;
+ private final Type returnType;
+
+ PrimitiveInstanceMethod(PrimitiveInstanceMethodGenerator generate, Type returnType) {
+ this.generate = generate;
+ this.returnType = returnType;
+ }
+
+ public Type getReturnType() {
+ return returnType;
+ }
+
+ public Node generate(ExpressionNode receiver, List<ExpressionNode> arguments) {
+ return generate.generate(receiver, arguments);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethodCall.java b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethodCall.java
new file mode 100644
index 0000000..daa6de8
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethodCall.java
@@ -0,0 +1,51 @@
+package org.zwobble.couscous.backends.csharp.primitives;
+
+import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.Node;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.backends.csharp.CsharpNodeTypes;
+import org.zwobble.couscous.types.Type;
+
+import java.util.List;
+
+import static org.zwobble.couscous.util.ExtraIterables.lazyCons;
+
+public class PrimitiveInstanceMethodCall implements ExpressionNode {
+ private final PrimitiveInstanceMethod method;
+ private final ExpressionNode receiver;
+ private final List<ExpressionNode> arguments;
+
+ public PrimitiveInstanceMethodCall(PrimitiveInstanceMethod method, ExpressionNode receiver, List<ExpressionNode> arguments) {
+ this.method = method;
+ this.receiver = receiver;
+ this.arguments = arguments;
+ }
+
+ @Override
+ public Type getType() {
+ return method.getReturnType();
+ }
+
+ @Override
+ public int type() {
+ return CsharpNodeTypes.PRIMITIVE_INSTANCE_METHOD_CALL;
+ }
+
+ @Override
+ public Iterable<? extends Node> childNodes() {
+ return lazyCons(receiver, arguments);
+ }
+
+ @Override
+ public ExpressionNode transform(NodeTransformer transformer) {
+ return new PrimitiveInstanceMethodCall(
+ method,
+ transformer.transformExpression(receiver),
+ transformer.transformExpressions(arguments)
+ );
+ }
+
+ public Node generate() {
+ return method.generate(receiver, arguments);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethodGenerator.java b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethodGenerator.java
new file mode 100644
index 0000000..99e3a46
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveInstanceMethodGenerator.java
@@ -0,0 +1,10 @@
+package org.zwobble.couscous.backends.csharp.primitives;
+
+import org.zwobble.couscous.ast.ExpressionNode;
+
+import java.util.List;
+
+@FunctionalInterface
+interface PrimitiveInstanceMethodGenerator {
+ ExpressionNode generate(ExpressionNode receiver, List<ExpressionNode> arguments);
+}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethod.java b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethod.java
new file mode 100644
index 0000000..23cceb0
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethod.java
@@ -0,0 +1,25 @@
+package org.zwobble.couscous.backends.csharp.primitives;
+
+import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.Node;
+import org.zwobble.couscous.types.Type;
+
+import java.util.List;
+
+class PrimitiveStaticMethod {
+ private final PrimitiveStaticMethodGenerator generate;
+ private final Type returnType;
+
+ PrimitiveStaticMethod(PrimitiveStaticMethodGenerator generate, Type returnType) {
+ this.generate = generate;
+ this.returnType = returnType;
+ }
+
+ public Type getReturnType() {
+ return returnType;
+ }
+
+ public Node generate(List<ExpressionNode> arguments) {
+ return generate.generate(arguments);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethodCall.java b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethodCall.java
new file mode 100644
index 0000000..d7953ce
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethodCall.java
@@ -0,0 +1,46 @@
+package org.zwobble.couscous.backends.csharp.primitives;
+
+import org.zwobble.couscous.ast.ExpressionNode;
+import org.zwobble.couscous.ast.Node;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+import org.zwobble.couscous.backends.csharp.CsharpNodeTypes;
+import org.zwobble.couscous.types.Type;
+
+import java.util.List;
+
+public class PrimitiveStaticMethodCall implements ExpressionNode {
+ private final PrimitiveStaticMethod method;
+ private final List<ExpressionNode> arguments;
+
+ public PrimitiveStaticMethodCall(PrimitiveStaticMethod method, List<ExpressionNode> arguments) {
+ this.method = method;
+ this.arguments = arguments;
+ }
+
+ @Override
+ public Type getType() {
+ return method.getReturnType();
+ }
+
+ @Override
+ public int type() {
+ return CsharpNodeTypes.PRIMITIVE_STATIC_METHOD_CALL;
+ }
+
+ @Override
+ public Iterable<? extends Node> childNodes() {
+ return arguments;
+ }
+
+ @Override
+ public ExpressionNode transform(NodeTransformer transformer) {
+ return new PrimitiveStaticMethodCall(
+ method,
+ transformer.transformExpressions(arguments)
+ );
+ }
+
+ public Node generate() {
+ return method.generate(arguments);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethodGenerator.java b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethodGenerator.java
new file mode 100644
index 0000000..1a2e2e0
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/backends/csharp/primitives/PrimitiveStaticMethodGenerator.java
@@ -0,0 +1,10 @@
+package org.zwobble.couscous.backends.csharp.primitives;
+
+import org.zwobble.couscous.ast.ExpressionNode;
+
+import java.util.List;
+
+@FunctionalInterface
+interface PrimitiveStaticMethodGenerator {
+ ExpressionNode generate(List<ExpressionNode> arguments);
+}
diff --git a/src/test/java/org/zwobble/couscous/tests/ast/visitors/NodeTransformerTests.java b/src/test/java/org/zwobble/couscous/tests/ast/visitors/NodeTransformerTests.java
new file mode 100644
index 0000000..7e42580
--- /dev/null
+++ b/src/test/java/org/zwobble/couscous/tests/ast/visitors/NodeTransformerTests.java
@@ -0,0 +1,57 @@
+package org.zwobble.couscous.tests.ast.visitors;
+
+import org.junit.Test;
+import org.zwobble.couscous.ast.OperationNode;
+import org.zwobble.couscous.ast.Operations;
+import org.zwobble.couscous.ast.Operator;
+import org.zwobble.couscous.ast.ReturnNode;
+import org.zwobble.couscous.ast.visitors.NodeTransformer;
+
+import java.util.Optional;
+
+import static org.junit.Assert.assertEquals;
+import static org.zwobble.couscous.ast.ExpressionStatementNode.expressionStatement;
+import static org.zwobble.couscous.ast.LiteralNode.literal;
+import static org.zwobble.couscous.ast.OperationNode.operation;
+import static org.zwobble.couscous.ast.ReturnNode.returns;
+import static org.zwobble.couscous.util.ExtraLists.list;
+
+public class NodeTransformerTests {
+ @Test
+ public void subNodesAreTransformedBeforeExpression() {
+ NodeTransformer transformer = NodeTransformer.builder()
+ .transformExpression(node -> {
+ if (node.equals(literal(1))) {
+ return Optional.of(literal(2));
+ } else if (node instanceof OperationNode) {
+ return Optional.of(operation(Operator.ADD, ((OperationNode) node).getArguments(), node.getType()));
+ } else {
+ return Optional.empty();
+ }
+ })
+ .build();
+
+ assertEquals(
+ Operations.integerAdd(literal(2), literal(3)),
+ transformer.transformExpression(Operations.integerMultiply(literal(1), literal(3)))
+ );
+ }
+ @Test
+ public void subNodesAreTransformedBeforeStatement() {
+ NodeTransformer transformer = NodeTransformer.builder()
+ .transformExpression(node -> Optional.of(literal(1)))
+ .transformStatement(node -> {
+ if (node instanceof ReturnNode) {
+ return Optional.of(list(expressionStatement(((ReturnNode) node).getValue())));
+ } else {
+ return Optional.empty();
+ }
+ })
+ .build();
+
+ assertEquals(
+ list(expressionStatement(literal(1))),
+ transformer.transformStatement(returns(literal(0)))
+ );
+ }
+}