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>(); 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 collectSourceFilePaths(Path sourceRoot) throws IOException { var paths = new ArrayList(); Files.walkFileTree(sourceRoot, new SimpleFileVisitor() { @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; } }