package org.sugarj;
import static org.sugarj.common.ATermCommands.getApplicationSubterm;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.sugarj.common.ATermCommands;
import org.sugarj.common.CommandExecution;
import org.sugarj.common.CommandExecution.ExecutionError;
import org.sugarj.common.Environment;
import org.sugarj.common.FileCommands;
import org.sugarj.common.Log;
import org.sugarj.common.StringCommands;
import org.sugarj.common.path.AbsolutePath;
import org.sugarj.common.path.Path;
import org.sugarj.common.path.RelativePath;
/**
* @author Sebastian Erdweg <seba at informatik uni-marburg de>
*/
public class HaskellProcessor extends AbstractBaseProcessor {
private static final long serialVersionUID = -8916207157344649789L;
private final String GHC_COMMAND = "ghc";
private String moduleHeader;
private List<String> imports = new LinkedList<String>();
private List<String> body = new LinkedList<String>();
private boolean hasExtension = false;
private Environment environment;
private RelativePath sourceFile;
private Path outFile;
private Set<RelativePath> generatedModules = new HashSet<RelativePath>();
private String relNamespaceName;
private String moduleName;
private IStrategoTerm ppTable;
@Override
public String getGeneratedSource() {
if (moduleHeader == null)
return "";
if (hasExtension && body.isEmpty())
return "";
return moduleHeader + "\n"
+ StringCommands.printListSeparated(imports, "\n") + "\n"
+ StringCommands.printListSeparated(body, "\n");
}
@Override
public Path getGeneratedSourceFile() {
return outFile;
}
@Override
public String getNamespace() {
return relNamespaceName;
}
@Override
public HaskellLanguage getLanguage() {
return HaskellLanguage.getInstance();
}
/*
* processing stuff follows here
*/
@Override
public void init(RelativePath sourceFile, Environment environment) {
this.environment = environment;
this.sourceFile = sourceFile;
outFile = environment.createOutPath(FileCommands.dropExtension(sourceFile.getRelativePath()) + "." + HaskellLanguage.getInstance().getBaseFileExtension());
}
private void processNamespaceDecl(IStrategoTerm toplevelDecl) throws IOException {
String qualifiedModuleName = prettyPrint(getApplicationSubterm(toplevelDecl, "ModuleDec", 0));
String qualifiedModulePath = qualifiedModuleName.replace('.', '/');
String declaredModuleName = FileCommands.fileName(qualifiedModulePath);
moduleName = FileCommands.dropExtension(FileCommands.fileName(sourceFile.getRelativePath()));
String declaredRelNamespaceName = FileCommands.dropFilename(qualifiedModulePath);
relNamespaceName = FileCommands.dropFilename(sourceFile.getRelativePath());
RelativePath objectFile = environment.createOutPath(getRelativeNamespaceSep() + moduleName + "." + HaskellLanguage.getInstance().getBinaryFileExtension());
generatedModules.add(objectFile);
moduleHeader = prettyPrint(toplevelDecl);
if (!declaredRelNamespaceName.equals(relNamespaceName))
throw new RuntimeException(
"The declared namespace '" + declaredRelNamespaceName + "'" +
" does not match the expected namespace '" + relNamespaceName + "'.");
if (!declaredModuleName.equals(moduleName))
throw new RuntimeException(
"The declared module name '" + declaredModuleName + "'" +
" does not match the expected module name '" + moduleName + "'.");
}
@Override
public List<String> processBaseDecl(IStrategoTerm toplevelDecl) throws IOException {
if (getLanguage().isNamespaceDec(toplevelDecl)) {
processNamespaceDecl(toplevelDecl);
return Collections.emptyList();
}
IStrategoTerm term = getApplicationSubterm(toplevelDecl, "HaskellBody", 0);
String text = null;
try {
text = prettyPrint(term);
} catch (NullPointerException e) {
ATermCommands.setErrorMessage(toplevelDecl, "pretty printing Haskell failed");
}
if (text != null)
body.add(text);
return Collections.emptyList();
}
@Override
public String getModulePathOfImport(IStrategoTerm toplevelDecl) {
return prettyPrint(getApplicationSubterm(toplevelDecl, "Import", 2)).replace('.', '/');
}
@Override
public void processModuleImport(IStrategoTerm toplevelDecl) throws IOException {
imports.add(prettyPrint(toplevelDecl));
}
@Override
public String getExtensionName(IStrategoTerm decl) throws IOException {
hasExtension = true;
return moduleName;
}
private String prettyPrint(IStrategoTerm term) {
if (ppTable == null)
ppTable = ATermCommands.readPrettyPrintTable(getLanguage().ensureFile("org/sugarj/languages/Haskell.pp").getAbsolutePath());
return ATermCommands.prettyPrint(ppTable, term, interp);
}
@Override
public List<Path> compile(List<Path> outFiles, Path bin, List<Path> includePaths) throws IOException {
List<String> cmds = new LinkedList<String>();
cmds.add(GHC_COMMAND);
cmds.add("-outputdir");
cmds.add(bin.getAbsolutePath());
List<Path> generatedFiles = new LinkedList<Path>();
for (Path outFile : outFiles) {
cmds.add(outFile.getAbsolutePath());
String noExtPath = FileCommands.dropExtension(outFile.getAbsolutePath());
if (FileCommands.fileExists(new AbsolutePath(noExtPath)))
generatedFiles.add(new AbsolutePath(noExtPath));
if (FileCommands.fileExists(new AbsolutePath(noExtPath + ".exe")))
generatedFiles.add(new AbsolutePath(noExtPath + ".exe"));
if (FileCommands.fileExists(new AbsolutePath(noExtPath + ".hi")))
generatedFiles.add(new AbsolutePath(noExtPath + ".hi"));
if (FileCommands.fileExists(new AbsolutePath(noExtPath + ".o")))
generatedFiles.add(new AbsolutePath(noExtPath + ".o"));
}
if (!includePaths.isEmpty()) {
StringBuilder searchPath = new StringBuilder("-i");
for (Path path : includePaths)
if (new File(path.getAbsolutePath()).isDirectory())
searchPath.append(path.getAbsolutePath()).append(":");
searchPath.deleteCharAt(searchPath.length() - 1);
cmds.add(searchPath.toString());
}
new CommandExecution(false).execute(cmds.toArray(new String[cmds.size()]));
return generatedFiles;
}
@Override
public boolean isModuleExternallyResolvable(String relModulePath) {
boolean oldSilent = CommandExecution.SILENT_EXECUTION;
CommandExecution.SILENT_EXECUTION = true;
String[] cmds = new String[]{
"ghc-pkg",
"find-module", relModulePath.replace('/', '.'),
"--simple-output"
};
String[][] msg;
try {
msg = new CommandExecution(true).execute(cmds);
} catch (ExecutionError e) {
Log.log.logErr("Command execution failed: " + Arrays.toString(e.getCmds()), Log.ALWAYS);
return false;
} finally {
CommandExecution.SILENT_EXECUTION = oldSilent;
}
return msg.length > 0 && msg[0].length > 0;
}
@Override
public IStrategoTerm getExtensionBody(IStrategoTerm decl) {
return getApplicationSubterm(decl, "ExtensionBody", 0);
}
}