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);
}
}
}