package org.shedlang.compiler.backends; import org.shedlang.compiler.backends.paths.PartialTargetPath; import org.shedlang.compiler.backends.paths.TargetDir; import org.shedlang.compiler.backends.targets.ShedTarget; import org.shedlang.compiler.config.ProfileConfig; import org.shedlang.compiler.config.ProjectConfig; import org.shedlang.compiler.patio.PatioCompilationUnit; import org.shedlang.compiler.patio.PatioFunctionDeclaration; import org.shedlang.compiler.shid.ShidNamespace; import org.shedlang.compiler.typechecker.TypeInfos; import org.shedlang.compiler.types.NamespaceName; import java.io.IOException; import java.nio.file.Files; import java.util.List; import java.util.Optional; public class PatioNativeCHeadersBackend implements Backend { public static final BackendFactory FACTORY = new BackendFactory() { @Override public String name() { return "patio-native-c-headers"; } @Override public Optional backendForTarget(ShedTarget target) { if (target == ShedTarget.PATIO_NATIVE_C_HEADERS) { return Optional.of(PatioNativeCHeadersBackend.INSTANCE); } else { return Optional.empty(); } } }; public static final PatioNativeCHeadersBackend INSTANCE = new PatioNativeCHeadersBackend(); private PatioNativeCHeadersBackend() { } @Override public String targetName() { return "patio-native-c-headers"; } public void compile( ProjectConfig config, ProfileConfig profileConfig, List namespaces, TypeInfos typeInfos, TargetDir targetDir ) throws IOException, InterruptedException { PatioBackends.compile( config, profileConfig, namespaces, typeInfos, new PatioToNativeHeaders(targetDir) ); } private static class PatioToNativeHeaders implements PatioBackends.PatioToTarget { private final TargetDir targetDir; public PatioToNativeHeaders(TargetDir targetDir) { this.targetDir = targetDir; } @Override public PatioBackends.PatioToTarget namespace( NamespaceName namespaceName, PatioCompilationUnit patioCompilationUnit ) throws IOException, InterruptedException { var macroName = String.format( "SHED_NAMESPACES__%s__H", String.join("__", namespaceName.parts()) ); serializeToDisk( targetDir.namespace(namespaceName), macroName, patioCompilationUnit ); return this; } @Override public void bin( String binName, PatioCompilationUnit patioCompilationUnit, String mainFunctionName, ProfileConfig profileConfig ) throws IOException, InterruptedException { var macroName = String.format("SHED_BINS__%s__H", binName); serializeToDisk( targetDir.bin(binName), macroName, patioCompilationUnit ); } private void serializeToDisk( PartialTargetPath partialTargetPath, String macroName, PatioCompilationUnit patioCompilationUnit ) throws IOException { var empty = true; var builder = new StringBuilder(); // TODO: path to header file? Use angle brackets? builder.append(String.format("#ifndef %s\n", macroName)); builder.append(String.format("#define %s\n\n", macroName)); builder.append("#include \"shed/shed.h\"\n\n"); for (var element : patioCompilationUnit.elements()) { if (element instanceof PatioFunctionDeclaration patioFunctionDeclaration) { // TODO: pass this information through rather than having to inspect the string? // TODO: ignore dependency projects? if (patioFunctionDeclaration.name().startsWith("shed__runtime__namespace__")) { empty = false; builder.append("ShedObject "); builder.append(patioFunctionDeclaration.name()); builder.append("(ShedClosurePointer closurePointer"); for (var param : patioFunctionDeclaration.params()) { builder.append(", ShedObject "); builder.append(param.name()); } builder.append(");\n"); } } } builder.append("\n#endif\n"); if (empty) { return; } var compilationUnitString = builder.toString(); var path = partialTargetPath.withExtension(".h"); Files.createDirectories(path.getParent()); Files.writeString(path, compilationUnitString); } } }