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 .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(), 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()); } 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; } }