blob: 5aef4af0bc63c4ae68bd997d6d27dba446f57c9c [file] [log] [blame]
package com.googlecode.prolog_cafe.compiler;
import com.googlecode.prolog_cafe.exceptions.CompileException;
import com.googlecode.prolog_cafe.exceptions.PrologException;
import com.googlecode.prolog_cafe.lang.BufferingPrologControl;
import com.googlecode.prolog_cafe.lang.ListTerm;
import com.googlecode.prolog_cafe.lang.Prolog;
import com.googlecode.prolog_cafe.lang.PrologClassLoader;
import com.googlecode.prolog_cafe.lang.SymbolTerm;
import com.googlecode.prolog_cafe.lang.Term;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.LinkedList;
/**
* The <code>Compiler</code> class provides methods for
* translating Prolog programs into Java programs.
*
* The <code>Compiler</code> class supports the following compiler options.
* All of them are set to <code>true</code> in default setting.
* <ul>
* <li>Eliminate disjunctions
* <li>Arithmetic compilation
* <li>Inline expansion
* <li>Optimisation of recursive call
* <li>2nd. level indexing (<code>switch_on_hash</code>)
* </ul>
*
* Let us show a sample session for translating a Prolog program
* <code>$PLCAFEDIR/examples/prolog/list.pl</code> into Java.
* The <code>list.pl</code> contains predicates
* <code>append/3</code>, <code>nrev/2</code>, and <code>range/3</code>.
* <ul>
* <li>From Command line<br>
* <pre>
* % java -cp $PLCAFEDIR/plcafe.jar com.googlecode.prolog_cafe.compiler.Compiler:$CLASSPATH $PLCAFEDIR/examples/prolog/list.pl
* Prolog Cafe X.X.X (YYY)
* Copyright(C) 1997-200X M.Banbara and N.Tamura
* % ls
* PRED_append_3.java PRED_nrev_2.java PRED_range_3.java
* </pre>
* <li>From Java program<br>
* <pre>
* import com.googlecode.prolog_cafe.compiler.Compiler;
* public class T {
* public static void main(String argv[]) {
* Compiler comp = new Compiler();
* comp.prologToJava(argv[0], ".");
* }
* }
* </pre>
* <pre>
* % javac -classpath $PLCAFEDIR/plcafe.jar:$CLASSPATH T.java
* % java -classpath $PLCAFEDIR/plcafe.jar:$CLASSPATH T $PLCAFEDIR/examples/prolog/list.pl
* % ls
* PRED_append_3.java PRED_nrev_2.java PRED_range_3.java
* </pre>
* </ul>
*
* It is noted that
* our Prolog-to-Java translator is originally witten in Prolog, and then bootstrapped.
* Please see the following two Prolog programs in details.
* <ul>
* <li><code>$PLCAFEDIR/src/compiler/pl2am.pl</code><br>
* Translates a Prolog program into a WAM-based intermediate code.
* <li><code>$PLCAFEDIR/src/compiler/am2j.pl</code><br>
* Translates a WAM-based intermediate code generated by <code>pl2am.pl</code>
* into Java programs.
* </ul>
*
* @author Mutsunori Banbara (banbara@kobe-u.ac.jp)
* @author Naoyuki Tamura (tamura@kobe-u.ac.jp)
* @version 1.2
*/
public class Compiler {
public static enum Option {
eliminateDisjunctions("ed", true),
arithmeticCompilation("ac", true),
inlineExpansion("ie", true),
optimiseRecursiveCall("rc", true),
switchOnHash("idx", true),
generateClosure("clo", false);
final SymbolTerm symbol;
final boolean onByDefault;
Option(String symbol, boolean onByDefault) {
this.symbol = SymbolTerm.intern(symbol);
this.onByDefault = onByDefault;
}
}
/** Prolog context running the compiler/translater tools. */
private BufferingPrologControl pcl;
private EnumSet<Option> options;
/** Initialize a new compiler instance. */
public Compiler() {
pcl = new BufferingPrologControl();
pcl.setEnabled(EnumSet.allOf(Prolog.Feature.class), true);
pcl.setPrologClassLoader(new PrologClassLoader(Compiler.class.getClassLoader()));
pcl.setReductionLimit(Long.MAX_VALUE);
options = EnumSet.noneOf(Option.class);
enableDefaultOptions();
}
/**
* Translates a Prolog program into a WAM-based intermediate code.
*
* @param _prolog an input Prolog file
* @param _wam an output file for WAM-based intermediate code.
*/
public void prologToWAM(String _prolog, String _wam) throws CompileException {
if (! fileExists(_prolog))
throw new CompileException(new FileNotFoundException(_prolog));
// Create arguments
Term prolog = SymbolTerm.create(_prolog);
Term wam = SymbolTerm.create(_wam);
Term op = Prolog.Nil;
for (Option opt : options)
op = new ListTerm(opt.symbol, op);
ListTerm args = new ListTerm(prolog, new ListTerm(wam, new ListTerm(op, Prolog.Nil)));
try {
if (!pcl.execute("com.googlecode.prolog_cafe.compiler.pl2am", "pl2am", args))
throw new CompileException("Unknown Error");
} catch (PrologException err) {
throw new CompileException("Error compiling "+_prolog, err);
}
}
/**
* Translates WAM-based intermediate code into Java source.
*
* @param _wam an input file for WAM-based intermediate code.
* @param _dir a destination directory for java files.
* @see #prologToWAM(String, String)
*/
public void wamToJavaSource(String _wam, String _dir) throws CompileException {
if (! fileExists(_wam))
throw new CompileException(new FileNotFoundException(_wam));
if (! fileExists(_dir) && !new File(_dir).mkdirs())
throw new CompileException(new FileNotFoundException(_dir));
// Create arguments
Term wam = SymbolTerm.create(_wam);
Term dir = SymbolTerm.create(_dir);
ListTerm args = new ListTerm(wam, new ListTerm(dir, Prolog.Nil));
try {
if (!pcl.execute("com.googlecode.prolog_cafe.compiler.am2j", "am2j", args))
throw new CompileException("Unknown Error");
} catch (PrologException err) {
throw new CompileException("Error converting "+_wam, err);
}
}
/**
* Translates a Prolog program into Java source files.
*
* @param prolog an input Prolog file
* @param dir a destination directory for java files. The directory must already exist.
* @see #prologToWAM(String, String)
* @see #wamToJavaSource(String, String)
*/
public void prologToJavaSource(String prolog, String dir) throws CompileException {
File tmp;
try {
tmp = File.createTempFile("PrologCafe_", ".am");
} catch (IOException e) {
throw new CompileException("Cannot create temporary file", e);
}
try {
prologToWAM(prolog, tmp.getPath());
wamToJavaSource(tmp.getPath(), dir);
} finally {
if (!tmp.delete() && tmp.exists())
tmp.deleteOnExit();
}
}
public static void main(String argv[]) throws Exception {
Compiler comp = new Compiler();
String out = ".";
String amdir = null;
boolean stackTrace = false;
LinkedList<String> plsrc = new LinkedList<>();
int argi = 0;
for (; argi < argv.length; argi++) {
String a = argv[argi];
if (a.equals("--")) {
argi++;
break;
}
if (a.equals("-O")) {
comp.enableDefaultOptions();
} else if (a.equals("-O:none")) {
comp.options.clear();
} else if (a.startsWith("-O:")) {
String optname = a.substring("-O:".length());
Option opt = findOptionByName(optname);
if (opt != null)
comp.enable(opt);
} else if (a.equals("-s")) {
if (++argi == argv.length)
usage();
out = argv[argi];
} else if (a.equals("-am")) {
if (++argi == argv.length)
usage();
amdir = argv[argi];
} else if (a.equals("-h") || a.equals("--help") || a.equals("-help")) {
usage();
} else if (a.equals("--show-stack-trace")) {
stackTrace = true;
} else if (a.startsWith("-")) {
System.err.println("error: Unsupported flag '" + a + "'");
usage();
} else {
plsrc.add(a);
}
}
if (argi < argv.length)
plsrc.addAll(Arrays.asList(argv).subList(argi, argv.length));
if (plsrc.isEmpty())
usage();
banner();
for (String pl : plsrc) {
System.err.println("Translating " + pl);
try {
if (amdir != null) {
String base;
if (pl.endsWith(".pl"))
base = pl.substring(0, pl.length() - 3);
else
base = pl;
File am = new File(new File(amdir), base + ".am");
am.getParentFile().mkdirs();
comp.prologToWAM(pl, am.getPath());
comp.wamToJavaSource(am.getPath(), out);
} else {
comp.prologToJavaSource(pl, out);
}
} catch (CompileException err) {
if (stackTrace)
err.printStackTrace();
else
System.err.println("error: " + err.getMessage());
System.exit(1);
}
}
}
private static Option findOptionByName(String optname) {
for (Option opt : Option.values()) {
if (opt.toString().equalsIgnoreCase(optname))
return opt;
if (opt.symbol.name().equalsIgnoreCase(optname))
return opt;
}
System.err.println("error: Unsupported option '" + optname + "'");
System.exit(1);
throw new RuntimeException("System.exit(1)");
}
private static void usage() {
System.err.print("usage: ");
System.err.print("java ");
System.err.print(Compiler.class.getName());
System.err.print(" [options]");
System.err.print(" prolog_source...");
System.err.println();
banner();
String optfmt = " %-20s %s";
System.err.format(optfmt, "-s <directory>", "where to place generated source files");
System.err.println();
System.err.format(optfmt, "-am <directory>", "save WAM intermediate files");
System.err.println();
System.err.format(optfmt, "-O", "enable all optimizations");
System.err.println();
System.err.format(optfmt, "-O:none", "disable all optimizations");
System.err.println();
// Special options not related to building Prolog programs.
System.err.println();
System.err.format(optfmt, "-h, --help", "display this message");
System.err.println();
System.err.format(optfmt, "--show-stack-trace", "show Java stack trace on failure");
System.err.println();
System.exit(1);
}
private static void banner() {
System.err.println("Prolog Cafe");
System.err.println("Copyright(C) 1997-2009 M.Banbara and N.Tamura");
System.err.println();
}
private static boolean fileExists(String _file) {
try {
return new File(_file).exists();
} catch (SecurityException e) {}
return false;
}
public boolean isEnabled(Option opt) { return options.contains(opt); }
public void enable(Option opt) { options.add(opt); }
public void disable(Option opt) { options.remove(opt); }
public void setEnabled(Option opt, boolean on) {
if (on)
enable(opt);
else
disable(opt);
}
private void enableDefaultOptions() {
for (Option opt : Option.values())
if (opt.onByDefault)
options.add(opt);
}
}