package org.shedlang.compiler; import org.shedlang.compiler.ast.untyped.UntypedNamespaceNode; import org.shedlang.compiler.backends.Backend; import org.shedlang.compiler.backends.paths.TargetDir; import org.shedlang.compiler.config.ConfigChecker; import org.shedlang.compiler.config.ProfileConfig; import org.shedlang.compiler.config.ProjectConfig; import org.shedlang.compiler.parser.Parser; import org.shedlang.compiler.shid.ShidCodeGen; import org.shedlang.compiler.shid.ShidNamespace; import org.shedlang.compiler.sources.Sources; import org.shedlang.compiler.typechecker.TypeChecker; import org.shedlang.compiler.typechecker.TypeCheckerContext; import org.shedlang.compiler.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; import static org.shedlang.compiler.typechecker.NamespaceDependencyLineariser.orderNamespaces; public class ShedCompiler { public void compile( Path projectRoot, Backend backend, Optional profileName ) throws IOException, InterruptedException { var config = ProjectConfig.read(projectRoot); var sourceRoot = projectRoot.resolve("src"); var sourceFilePaths = collectSourceFilePaths(sourceRoot); var untypedNamespaceNodes = parseSource(sourceRoot, sourceFilePaths); var namespaces = new ArrayList(); var typeCheckerContext = TypeCheckerContext.EMPTY .addNamespaceTypeInfo( ShedBuiltins.NAMESPACE_NAME, new NamespaceTypeInfo(FieldsInfo.of( FieldsInfo.freeField(ShedBuiltins.PRINT_NAME, ShedBuiltins.PRINT_TYPE) )) ) .enterGlobal(P.map( Map.entry("Bool", Types.metaType(Types.BOOL)), Map.entry("Int", Types.metaType(Types.INT)), Map.entry("Io", Types.effectType(Effects.IO)), Map.entry("String", Types.metaType(Types.STRING)), Map.entry("Unit", Types.metaType(Types.UNIT)), Map.entry(ShedBuiltins.PRINT_NAME, ShedBuiltins.PRINT_TYPE) )); for (var untypedNamespaceNode : orderNamespaces(untypedNamespaceNodes)) { var typeCheckNamespaceResult = TypeChecker.typeCheckNamespace(untypedNamespaceNode, typeCheckerContext); typeCheckerContext = typeCheckNamespaceResult.context(); var shidNamespace = ShidCodeGen.compileNamespace(typeCheckNamespaceResult.value()); namespaces.add(shidNamespace); } var typeInfos = typeCheckerContext.toTypeInfos(); ConfigChecker.configCheck(config, typeInfos); var profileConfig = profileName.map(config::profile) .orElse(ProfileConfig.DEFAULT); backend.compile( config, profileConfig, namespaces, typeInfos, new TargetDir( config.targetDir() .resolve(backend.targetName()) .resolve(profileConfig.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; } private List parseSource( Path sourceRoot, List sourceFilePaths ) throws IOException { var untypedNamespaceNodes = new ArrayList(); 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); untypedNamespaceNodes.add(untypedNode); } return untypedNamespaceNodes; } }