aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble/couscous/backends/csharp/CsharpBackend.java
blob: 3fe1d5e8142a8a6519ad246ab39bb092347817f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package org.zwobble.couscous.backends.csharp;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.io.Resources;
import org.zwobble.couscous.Backend;
import org.zwobble.couscous.ast.*;
import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
import org.zwobble.couscous.values.PrimitiveValueVisitor;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import static com.google.common.collect.Iterables.transform;

public class CsharpBackend implements Backend {
    private static final List<String> RUNTIME_FILES = ImmutableList.of(
        "java/lang/String.cs");

    private final Path directoryPath;
    private final String namespace;

    public CsharpBackend(Path directoryPath, String namespace) {
        this.directoryPath = directoryPath;
        this.namespace = namespace;
    }

    @Override
    public void compile(List<ClassNode> classes) throws IOException {
        Files.write(
            directoryPath.resolve("Program.cs"),
            Iterables.concat(
                transform(
                    classes,
                    classNode -> compileClass(classNode)),
                transform(
                    RUNTIME_FILES,
                    runtimeFile -> readRuntimeFile(runtimeFile))));
    }

    private String readRuntimeFile(String runtimeFile) {
        try {
            return Resources.toString(Resources.getResource("csharp/runtime/" + runtimeFile), StandardCharsets.UTF_8);
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    private String compileClass(ClassNode classNode) {
        String namespace = this.namespace +
            classNode.getName().getPackage()
                .map(packageName -> packageName + ".")
                .orElse("");
        return "namespace " + namespace + " {" +
            "    internal class " + classNode.getSimpleName() + " {" +
            String.join("\n", transform(classNode.getMethods(), method -> compileMethod(method))) +
            "    }" +
            "}";
    }

    private String compileMethod(MethodNode method) {
        ReturnNode returnNode = (ReturnNode) method.getBody().get(0);
        return
            "public static dynamic " + method.getName() + "() {" +
            "    return " + compileExpression(returnNode.getValue()) + ";" +
            "}";
    }

    private String compileExpression(ExpressionNode value) {
        return value.accept(new ExpressionNodeMapper<String>() {
            @Override
            public String visit(LiteralNode literal) {
                return literal.getValue().accept(new PrimitiveValueVisitor<String>() {
                    @Override
                    public String visitInteger(int value) {
                        return Integer.toString(value);
                    }

                    @Override
                    public String visitString(String value) {
                        // TODO: escaping
                        return "\"" + value + "\"";
                    }

                    @Override
                    public String visitBoolean(boolean value) {
                        return value ? "true" : "false";
                    }

                    @Override
                    public String visitUnit() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public String visitType(TypeName value) {
                        return "typeof(" + namespace + "." + value.getQualifiedName() + ")";
                    }
                });
            }

            @Override
            public String visit(VariableReferenceNode variableReference) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String visit(ThisReferenceNode reference) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String visit(AssignmentNode assignment) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String visit(TernaryConditionalNode ternaryConditional) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String visit(MethodCallNode methodCall) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String visit(ConstructorCallNode call) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String visit(FieldAccessNode fieldAccess) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String visit(TypeCoercionNode typeCoercion) {
                throw new UnsupportedOperationException();
            }

            @Override
            public String visit(CastNode cast) {
                throw new UnsupportedOperationException();
            }
        });
    }
}