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