package scotch.compiler; import static java.util.stream.Collectors.toList; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import com.google.common.collect.ImmutableList; import org.antlr.v4.runtime.ANTLRFileStream; import org.antlr.v4.runtime.CommonTokenStream; import scotch.compiler.error.CompileException; import scotch.compiler.output.GeneratedClass; import scotch.compiler.parser.ModulesMapper; import scotch.compiler.parser.ScotchLayoutLexer; import scotch.compiler.parser.ScotchLexer; import scotch.compiler.parser.ScotchParser; import scotch.compiler.parser.ScotchParser.ModuleContext; import scotch.compiler.parser.ScotchParser.ModulesContext; import scotch.symbol.SymbolResolver; public class PathCompiler { private final SymbolResolver symbolResolver; private final File outputPath; private final List<File> inputFiles; public PathCompiler(SymbolResolver symbolResolver, File outputPath, List<File> inputFiles) { this.symbolResolver = symbolResolver; this.outputPath = outputPath; this.inputFiles = ImmutableList.copyOf(inputFiles); } public List<File> compile() throws IOException, CompileException, IllegalFileNameException { checkInputFiles(); createOutputPath(); return generateBytecode().stream() .map(generatedClass -> { File file = new File(outputPath, generatedClass.getClassName().replace('.', '/') + ".class"); if (!file.getParentFile().mkdirs() && !file.getParentFile().exists()) { throw new RuntimeException("Failed to create path: " + file.getParentFile()); } try (FileOutputStream outputStream = new FileOutputStream(file)) { byte[] bytes = generatedClass.getBytes(); outputStream.write(bytes); } catch (IOException exception) { throw new RuntimeException(exception); } return file; }) .collect(toList()); } private void checkInputFiles() { inputFiles.stream() .filter(file -> !file.getAbsolutePath().endsWith(".scotch")) .findFirst() .ifPresent(file -> { throw new IllegalFileNameException("Illegal file name: " + file.getAbsolutePath()); }); } private void createOutputPath() throws IOException { if (!outputPath.mkdirs() && !outputPath.exists()) { throw new IOException("Failed to create output path: " + outputPath); } } private List<GeneratedClass> generateBytecode() throws IOException, CompileException { return new Compiler(symbolResolver, parseInputFiles()).generateBytecode(); } private Map<String, List<ModuleContext>> parseInputFiles() throws IOException { List<ModulesContext> moduleCtxsList = new ArrayList<>(); for (File input : inputFiles) { ANTLRFileStream fileStream = new ANTLRFileStream(input.getAbsolutePath()); ScotchLayoutLexer tokenSource = new ScotchLayoutLexer(new ScotchLexer(fileStream)); moduleCtxsList.add(new ScotchParser(new CommonTokenStream(tokenSource)).modules()); } return new ModulesMapper().map(moduleCtxsList); } public static final class IllegalFileNameException extends RuntimeException { public IllegalFileNameException(String message) { super(message); } } }