aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/shedlang/compiler/ShedCompiler.java
blob: d727aea550150966227891fb604e2fd2c1a874b3 (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
package org.shedlang.compiler;

import org.shedlang.compiler.backends.Backend;
import org.shedlang.compiler.config.ProjectConfig;
import org.shedlang.compiler.parser.Parser;
import org.shedlang.compiler.sources.Sources;
import org.shedlang.compiler.stackir.StackCompiler;
import org.shedlang.compiler.stackir.instructions.Instruction;
import org.shedlang.compiler.typechecker.TypeChecker;
import org.shedlang.compiler.typechecker.TypeCheckerContext;
import org.shedlang.compiler.types.Effects;
import org.shedlang.compiler.types.FunctionType;
import org.shedlang.compiler.types.NamespaceName;
import org.shedlang.compiler.types.Types;
import org.shedlang.compiler.util.P;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.StreamSupport;

import static org.shedlang.compiler.parser.Tokeniser.tokenise;

public class ShedCompiler {
    public void compile(Path projectRoot, Backend backend) throws IOException {
        var config = ProjectConfig.read(projectRoot);

        var sourceRoot = projectRoot.resolve("src");

        var sourceFilePaths = collectSourceFilePaths(sourceRoot);

        var namespaces = new LinkedHashMap<NamespaceName, List<Instruction>>();

        var typeCheckerContext = TypeCheckerContext.EMPTY
            // TODO: remove duplication of builtin namespace definition
            .addNamespaceFields(NamespaceName.of("Shed", "Builtins"), P.orderedMap(
                Map.entry("print", new FunctionType(
                    List.of(Types.STRING),
                    List.of(),
                    Effects.IO,
                    Types.UNIT
                ))
            ))
            .enter(P.map(
                Map.entry("Io", Types.effectType(Effects.IO)),
                Map.entry("Unit", Types.metaType(Types.UNIT)),

                Map.entry("print", new FunctionType(
                    List.of(Types.STRING),
                    List.of(),
                    Effects.IO,
                    Types.UNIT
                ))
            ));

        for (var sourceFilePath : sourceFilePaths) {
            var namespaceNameParts = StreamSupport.stream(sourceRoot.relativize(sourceFilePath).spliterator(), false)
                .map(element -> {
                    var string = element.toString();
                    return string.substring(0, string.lastIndexOf('.'));
                })
                .toList();
            var namespaceName = new NamespaceName(namespaceNameParts);

            var sourceContents = Files.readString(sourceFilePath, StandardCharsets.UTF_8);
            var source = Sources.file(sourceFilePath.toString(), sourceContents);

            var tokens = tokenise(source);

            var untypedNode = Parser.parseNamespace(tokens, namespaceName);
            var typeCheckNamespaceResult = TypeChecker.typeCheckNamespace(untypedNode, typeCheckerContext);
            typeCheckerContext = typeCheckNamespaceResult.context();

            var instructions = StackCompiler.compileNamespace(typeCheckNamespaceResult.value());
            namespaces.put(namespaceName, instructions);
        }

        backend.compile(config, namespaces, typeCheckerContext.toTypeInfo(), config.targetDir().resolve(backend.name()));
    }

    private List<Path> collectSourceFilePaths(Path sourceRoot) throws IOException {
        var paths = new ArrayList<Path>();
        Files.walkFileTree(sourceRoot, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(
                Path file,
                BasicFileAttributes attrs
            ) {
                if (file.getFileName().toString().endsWith(".shed")) {
                    paths.add(file);
                }
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(
                Path dir,
                IOException exc
            ) {
                return FileVisitResult.CONTINUE;
            }
        });
        paths.sort(Comparator.naturalOrder());
        return paths;
    }
}