package org.sugarj;
import static org.sugarj.common.ATermCommands.getApplicationSubterm;
import static org.sugarj.common.ATermCommands.isApplication;
import static org.sugarj.common.Log.log;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.strategoxt.lang.Context;
import org.strategoxt.stratego_gpp.parse_pptable_file_0_0;
import org.sugarj.common.ATermCommands;
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.Path;
import org.sugarj.common.path.RelativePath;
public class PrologProcessor extends AbstractBaseProcessor implements Serializable {
private static final long serialVersionUID = 6271882490466636509L;
private String moduleHeader;
private List<String> imports = new LinkedList<String>();
private List<String> body = new LinkedList<String>();
private Environment environment;
private RelativePath sourceFile;
private Path prologOutFile;
private String decName;
private String relNamespaceName;
private IStrategoTerm pptable = null;
private Path prettyPrint = null;
private Path getPrettyPrint() {
if (prettyPrint == null)
prettyPrint = getLanguage().ensureFile("org/sugarj/languages/Prolog.pp");
return prettyPrint;
}
@Override
public String getGeneratedSource() {
return moduleHeader + "\n"
+ StringCommands.printListSeparated(imports, "\n") + "\n"
+ StringCommands.printListSeparated(body, "\n");
}
@Override
public Path getGeneratedSourceFile() {
return prologOutFile;
}
@Override
public List<String> processBaseDecl(IStrategoTerm toplevelDecl) throws IOException {
if (getLanguage().isNamespaceDec(toplevelDecl)) {
processNamespaceDecl(toplevelDecl);
return Collections.emptyList();
}
// Nothing to do here for prolog
IStrategoTerm dec = toplevelDecl;
// TODO: Implement reexport handling in a more sensible way
if (isApplication(dec, "ModuleReexport"))
imports.add(prettyPrint(dec));
else
body.add(prettyPrint(dec));
return Collections.emptyList();
}
private IStrategoTerm initializePrettyPrinter(Context ctx) {
if (pptable == null) {
IStrategoTerm pptable_file = ATermCommands.makeString(getPrettyPrint().getAbsolutePath());
pptable = parse_pptable_file_0_0.instance.invoke(org.strategoxt.stratego_gpp.stratego_gpp.init(), pptable_file);
}
return pptable;
}
public String prettyPrint(IStrategoTerm term) {
IStrategoTerm ppTable = initializePrettyPrinter(interp.getCompiledContext());
return ATermCommands.prettyPrint(ppTable, term, interp);
}
@Override
public void init(RelativePath sourceFile, Environment environment) {
this.environment = environment;
this.sourceFile = sourceFile;
prologOutFile = environment.createOutPath(FileCommands.dropExtension(sourceFile.getRelativePath()) + "." + PrologLanguage.getInstance().getBinaryFileExtension());
}
@Override
public String getNamespace() {
// XXX: Is there a namespace separator in prolog? Or even any notion of
// compound namespaces?
// XXX: From swi prolog doc: Modules are organised in a single and flat
// namespace and therefore module names must be chosen with some care to
// avoid conflicts.
// XXX: SugarProlog will implement different namespace handling.
return relNamespaceName;
}
private void processNamespaceDecl(IStrategoTerm toplevelDecl) throws IOException {
String moduleName = null;
if (isApplication(toplevelDecl, "ModuleDec")) {
moduleName = prettyPrint(getApplicationSubterm(toplevelDecl, "ModuleDec", 0));
moduleHeader = prettyPrint(toplevelDecl);
} else if (isApplication(toplevelDecl, "SugarModuleDec")) {
moduleName = prettyPrint(getApplicationSubterm(toplevelDecl, "SugarModuleDec", 0));
moduleHeader = ":-module(" + moduleName + ", []).";
}
relNamespaceName = FileCommands.dropFilename(sourceFile.getRelativePath());
decName = getRelativeModulePath(moduleName);
log.log("The SDF / Stratego package name is '" + relNamespaceName + "'.", Log.DETAIL);
if (prologOutFile == null)
prologOutFile = environment.createOutPath(getRelativeNamespaceSep() + FileCommands.fileName(sourceFile) + "." + PrologLanguage.getInstance().getBinaryFileExtension());
}
@Override
public PrologLanguage getLanguage() {
return PrologLanguage.getInstance();
}
@Override
public List<Path> compile(List<Path> sourceFiles, Path bin, List<Path> path) throws IOException {
// no compilation neccessary for Prolog
return Collections.emptyList();
}
@Override
public String getModulePathOfImport(IStrategoTerm toplevelDecl) {
String modulePath = prettyPrint(toplevelDecl.getSubterm(0).getSubterm(0));
return modulePath;
}
private String getRelativeModulePath(String moduleName) {
return moduleName.replace("/", Environment.sep);
}
private String getImportedModuleString(IStrategoTerm moduleDecl) {
String importedModuleName = prettyPrint(moduleDecl.getSubterm(0).getSubterm(0));
// XXX: hacky, remove first directory. Should be replaced by a more robust implementation.
String importName = importedModuleName.substring(importedModuleName.indexOf("/") + 1);
String importString = ":- use_module(";
importString += importName;
if (moduleDecl.getSubtermCount() > 1) { // :- use_module(foo, bar/1).
importString += ", " + prettyPrint(moduleDecl.getSubterm(1));
}
importString += importString + ").";
return importString;
}
@Override
public void processModuleImport(IStrategoTerm toplevelDecl) throws IOException {
imports.add(getImportedModuleString(toplevelDecl));
}
@Override
public String getExtensionName(IStrategoTerm decl) throws IOException {
return decName;
}
@Override
public boolean isModuleExternallyResolvable(String relModulePath) {
// TODO: look for pre-installed SWI libraries?
return false;
}
@Override
public IStrategoTerm getExtensionBody(IStrategoTerm decl) {
IStrategoTerm sugarBody = getApplicationSubterm(decl, "ExtensionBody", 0);
return sugarBody;
}
}