diff options
author | Shawn O. Pearce <sop@google.com> | 2011-06-02 09:28:18 -0700 |
---|---|---|
committer | Shawn O. Pearce <sop@google.com> | 2011-06-03 10:12:35 -0700 |
commit | f39dc418888a620115b2e2d3773b93623ba7b495 (patch) | |
tree | 9da300b20d11e7e531c7587db5f7c4d9e8419688 | |
parent | fba28633841da9ede153a35da8db9a384bd06883 (diff) | |
download | prolog-cafe-f39dc418888a620115b2e2d3773b93623ba7b495.tar.gz |
Rewrite Compiler command line handling
Enable performing more than one translation at a time, and support
passing in the path for the output directory.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | src/compiler/CompileException.java | 16 | ||||
-rw-r--r-- | src/compiler/Compiler.java | 424 | ||||
-rw-r--r-- | src/compiler/Makefile | 2 | ||||
-rw-r--r-- | src/lang/BufferingPrologControl.java | 148 |
5 files changed, 406 insertions, 186 deletions
@@ -51,7 +51,7 @@ compiler: plcafe: $(JAVAC) $(JAVACOPTS) src/lang/*.java src/builtin/*.java \ target/generated-sources/prologcafe-builtin/com/googlecode/prolog_cafe/builtin/*.java \ - src/compiler/pl2am/*.java src/compiler/am2j/*.java src/compiler/Compiler.java + src/compiler/pl2am/*.java src/compiler/am2j/*.java src/compiler/*.java $(JAR) $(JAROPTS) plcafe.jar com/googlecode/prolog_cafe plj: diff --git a/src/compiler/CompileException.java b/src/compiler/CompileException.java new file mode 100644 index 0000000..f9d515c --- /dev/null +++ b/src/compiler/CompileException.java @@ -0,0 +1,16 @@ +package com.googlecode.prolog_cafe.compiler; + +/** Indicates compiling did not succeed. */ +public class CompileException extends Exception { + public CompileException(String message) { + super(message); + } + + public CompileException(Throwable cause) { + super(cause.getMessage(), cause); + } + + public CompileException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/compiler/Compiler.java b/src/compiler/Compiler.java index c0d94a6..bfd3b9d 100644 --- a/src/compiler/Compiler.java +++ b/src/compiler/Compiler.java @@ -1,8 +1,19 @@ package com.googlecode.prolog_cafe.compiler; -import com.googlecode.prolog_cafe.lang.*; +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.PrologException; +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 + * The <code>Compiler</code> class provides methods for * translating Prolog programs into Java programs. * * The <code>Compiler</code> class supports the following compiler options. @@ -22,7 +33,7 @@ import java.io.File; * <ul> * <li>From Command line<br> * <pre> - * % java -cp $PLCAFEDIR/plcafe.jar com.googlecode.prolog_cafe.compiler.Compiler:$CLASSPATH $PLCAFEDIR/examples/prolog/list.pl + * % 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 @@ -40,20 +51,20 @@ import java.io.File; * </pre> * <pre> * % javac -classpath $PLCAFEDIR/plcafe.jar:$CLASSPATH T.java - * % java -classpath $PLCAFEDIR/plcafe.jar:$CLASSPATH T $PLCAFEDIR/examples/prolog/list.pl + * % 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 + * 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> + * Translates a WAM-based intermediate code generated by <code>pl2am.pl</code> * into Java programs. * </ul> * @@ -62,216 +73,261 @@ import java.io.File; * @version 1.2 */ public class Compiler { - /** Version information */ - public static String VERSION = "Prolog Cafe 1.2.5 (mantis)"; - /** Copyright information */ - public static String COPYRIGHT = "Copyright(C) 1997-2009 M.Banbara and N.Tamura"; - - /** Compiler option for eliminating disjunctions. Its initial value is <code>true</code> */ - protected boolean eliminateDisjunctions = true; - /** Compiler option for arithmetic compilation. Its initial value is <code>true</code> */ - protected boolean arithmeticCompilation = true; - /** Compiler option for inline expansion. Its initial value is <code>true</code> */ - protected boolean inlineExpansion = true; - /** Compiler option for optimising recursive call. Its initial value is <code>true</code> */ - protected boolean optimiseRecursiveCall = true; - /** Compiler option for second-level indexing. Its initial value is <code>true</code> */ - protected boolean switchOnHash = true; - /** Non-standard option. Compiler option for closure generation. Its initial value is <code>false</code> */ - protected boolean generateClosure = false; - - /** - * Translates a Prolog program into a WAM-based intermediate code. + 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.makeSymbol(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(); + 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. - * @return <code>true</code> if succeeds, otherwise <code>false</code>. + * @param _wam an output file for WAM-based intermediate code. */ - public boolean prologToWAM(String _prolog, String _wam) { - try { - if (! fileExists(_prolog)) { - System.out.println("**ERROR: file " + _prolog + " does not exist"); - return false; - } - if (fileExists(_wam)) { - System.out.println("**ERROR: file " + _wam + " already exists"); - return false; - } + public void prologToWAM(String _prolog, String _wam) throws CompileException { + if (! fileExists(_prolog)) + throw new CompileException(new FileNotFoundException(_prolog)); + // Create arguments Term prolog = SymbolTerm.makeSymbol(_prolog); Term wam = SymbolTerm.makeSymbol(_wam); Term op = Prolog.Nil; - if (eliminateDisjunctions) - op = new ListTerm(SymbolTerm.makeSymbol("ed"), op); - if (arithmeticCompilation) - op = new ListTerm(SymbolTerm.makeSymbol("ac"), op); - if (inlineExpansion) - op = new ListTerm(SymbolTerm.makeSymbol("ie"), op); - if (optimiseRecursiveCall) - op = new ListTerm(SymbolTerm.makeSymbol("rc"), op); - if (switchOnHash) - op = new ListTerm(SymbolTerm.makeSymbol("idx"), op); - if (generateClosure) - op = new ListTerm(SymbolTerm.makeSymbol("clo"), op); - BlockingPrologControl ctl = new BlockingPrologControl(); - return ctl.execute("com.googlecode.prolog_cafe.compiler.pl2am", "pl2am", - new ListTerm(prolog, new ListTerm(wam, new ListTerm(op, Prolog.Nil)))); - } catch (Exception e){ - e.printStackTrace(); - } - return false; + 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 programs. + /** + * 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. The directory must already exist. - * @return <code>true</code> if succeeds, otherwise <code>false</code>. + * @param _wam an input file for WAM-based intermediate code. + * @param _dir a destination directory for java files. * @see #prologToWAM(String, String) */ - public boolean wamToJava(String _wam, String _dir) { - try { - if (! fileExists(_wam)) { - System.out.println("**ERROR: file " + _wam + " does not exist"); - return false; - } - if (! fileExists(_dir)) { - System.out.println("**ERROR: directory " + _dir + " does not exist"); - return false; - } + 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.makeSymbol(_wam); Term dir = SymbolTerm.makeSymbol(_dir); - BlockingPrologControl ctl = new BlockingPrologControl(); - return ctl.execute("com.googlecode.prolog_cafe.compiler.am2j", "am2j", - new ListTerm(wam, new ListTerm(dir, Prolog.Nil))); - } catch (Exception e){ - e.printStackTrace(); - } - return false; + 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 programs. + /** + * 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. - * @return <code>true</code> if succeeds, otherwise <code>false</code>. * @see #prologToWAM(String, String) - * @see #wamToJava(String, String) + * @see #wamToJavaSource(String, String) */ - public boolean prologToJava(String prolog, String dir) { - String wam = prolog + ".am"; - if (! prologToWAM(prolog, wam)) - return false; - if (! wamToJava(wam, dir)) - return false; - try { - File f = new File(wam); - if (f.exists()) - f.delete(); - } catch (SecurityException e) {} - return true; + 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[]) { - try { - System.err.println("\n" + VERSION); - System.err.println(COPYRIGHT); - if (argv.length != 1) { - usage(); - System.exit(999); - } - Compiler comp = new Compiler(); - if (! comp.prologToJava(argv[0], ".")) - System.exit(1); - System.exit(0); - } catch (Exception e){ - e.printStackTrace(); - System.exit(1); - } - } + 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<String>(); + int argi = 0; + for (; argi < argv.length; argi++) { + String a = argv[argi]; + if (a.equals("--")) { + argi++; + break; + } - protected static boolean fileExists(String _file) { - try { - File file = new File(_file); - return file.exists(); - } catch (SecurityException e) {} - return false; + 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); + } + } } - /** Shows usage */ - protected static void usage() { - String s = "Usage:\n"; - s += "java -cp $PLCAFEDIR/plcafe.jar"; - s += " com.googlecode.prolog_cafe.compiler.Compiler prolog_file\n"; - System.out.println(s); + 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)"); } - /** - * Returns the boolean value of <code>eliminateDisjunctions</code>. - * @see #eliminateDisjunctions - */ - public boolean getEliminateDisjunctions() { return eliminateDisjunctions; } - /** - * The <code>eliminateDisjunctions</code> field is set to <code>b</code>. - * @see #eliminateDisjunctions - */ - public void setEliminateDisjunctions(boolean b) { eliminateDisjunctions = b; } + 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(); - /** - * Returns the boolean value of <code>arithmeticCompilation</code>. - * @see #arithmeticCompilation - */ - public boolean getArithmeticCompilation() { return arithmeticCompilation; } - /** - * The <code>arithmeticCompilation</code> field is set to <code>b</code>. - * @see #arithmeticCompilation - */ - public void setArithmeticCompilation(boolean b) { arithmeticCompilation = b; } + 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(); - /** - * Returns the boolean value of <code>inlineExpansion</code>. - * @see #inlineExpansion - */ - public boolean getInlineExpansion() { return inlineExpansion; } - /** - * The <code>inlineExpansion</code> field is set to <code>b</code>. - * @see #inlineExpansion - */ - public void setInlineExpansion(boolean b) { inlineExpansion = b; } + System.err.format(optfmt, "-O", "enable all optimizations"); + System.err.println(); + System.err.format(optfmt, "-O:none", "disable all optimizations"); + System.err.println(); - /** - * Returns the boolean value of <code>optimiseRecursiveCall</code>. - * @see #optimiseRecursiveCall - */ - public boolean getOptimiseRecursiveCall() { return optimiseRecursiveCall; } - /** - * The <code>optimiseRecursiveCall</code> field is set to <code>b</code>. - * @see #optimiseRecursiveCall - */ - public void setOptimiseRecursiveCall(boolean b) { optimiseRecursiveCall = b; } + // 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(); - /** - * Returns the boolean value of <code>switchOnHash</code>. - * @see #switchOnHash - */ - public boolean getSwitchOnHash() { return switchOnHash; } - /** - * The <code>switchOnHash</code> field is set to <code>b</code>. - * @see #switchOnHash - */ - public void setSwitchOnHash(boolean b) { switchOnHash = b; } + System.exit(1); + } - /** - * Returns the boolean value of <code>generateClosure</code>. - * @see #generateClosure - */ - public boolean getGenerateClosure() { return generateClosure; } - /** - * The <code>generateClosure</code> field is set to <code>b</code>. - * @see #generateClosure - */ - public void setGenerateClosure(boolean b) { generateClosure = b; } + 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); + } } diff --git a/src/compiler/Makefile b/src/compiler/Makefile index c6d2c3d..20ef2fd 100644 --- a/src/compiler/Makefile +++ b/src/compiler/Makefile @@ -42,7 +42,7 @@ JAVA = java plc: pl2am.plc am2j.plc compiler:pl2am.am am2j.am - $(JAVAC) $(JAVACOPTS) pl2am/*.java am2j/*.java Compiler.java + $(JAVAC) $(JAVACOPTS) pl2am/*.java am2j/*.java *.java $(JAR) $(JAROPTS) compiler.jar com/googlecode/prolog_cafe/compiler plj: pl2am.plj am2j.plj diff --git a/src/lang/BufferingPrologControl.java b/src/lang/BufferingPrologControl.java new file mode 100644 index 0000000..3e1f423 --- /dev/null +++ b/src/lang/BufferingPrologControl.java @@ -0,0 +1,148 @@ +package com.googlecode.prolog_cafe.lang; + +import java.util.ArrayList; +import java.util.List; + +/** + * Executes Prolog on the current thread, buffering all solutions. + * <p> + * Whenever a solution is found for the predicate the arguments are deep-copied + * and buffered in a result collection. + */ +public class BufferingPrologControl extends PrologControl { + private int resLimit; + private List resBuffer; + + private boolean resSingle; + private Term[] resTemplate; + + /** + * Determine if the predicate completes successfully. + * + * @param pkg package the functor is declared in. Typically "user". + * @param functor a prolog predicate to execute. + * @param args arguments to pass. + * @return true if the function has at least one solution; false otherwise. + */ + public boolean execute(String pkg, String functor, Term... args) { + return once(pkg, functor, args) != null; + } + + /** + * Execute a function and return one solution. + * + * @param pkg package the functor is declared in. Typically "user". + * @param functor a prolog predicate to execute. + * @param arg argument to pass in, and template to return the result with. If + * this is an {@link VariableTerm} the value will be saved and returned + * on success. If this is a structure or list containing variables, the + * variables will be materialized in the result. + * @return a deep copy of {@code arg} for the first solution; null on failure. + */ + public Term once(String pkg, String functor, Term arg) { + setPredicate(pkg, functor, arg); + setResultTemplate(arg); + return (Term) (run(1) ? resBuffer.get(0) : null); + } + + /** + * Execute a function and return one solution. + * + * @param pkg package the functor is declared in. Typically "user". + * @param functor a prolog predicate to execute. + * @param args argument to pass in, and template to return the result with. If + * this is an {@link VariableTerm} the value will be saved and returned + * on success. If this is a structure or list containing variables, the + * variables will be materialized in the result. + * @return a deep copy of {@code args} for the first solution; null on + * failure. + */ + public Term[] once(String pkg, String functor, Term... args) { + setPredicate(pkg, functor, args); + setResultTemplate(args); + return (Term[]) (run(1) ? resBuffer.get(0) : null); + } + + /** + * Execute a function and return all solutions. + * + * @param pkg package the functor is declared in. Typically "user". + * @param functor a prolog predicate to execute. + * @param arg argument to pass in, and template to return the result with. If + * this is an {@link VariableTerm} the value will be saved and returned + * on success. If this is a structure or list containing variables, the + * variables will be materialized in the result. + * @return a deep copy of {@code arg} for each solution found. Empty list if + * there are no solutions. + */ + public List<Term> all(String pkg, String functor, Term arg) { + setPredicate(pkg, functor, arg); + setResultTemplate(arg); + run(Integer.MAX_VALUE); + return resBuffer; + } + + /** + * Execute a function and return all solutions. + * + * @param pkg package the functor is declared in. Typically "user". + * @param functor a prolog predicate to execute. + * @param args argument to pass in, and template to return the result with. If + * this is an {@link VariableTerm} the value will be saved and returned + * on success. If this is a structure or list containing variables, the + * variables will be materialized in the result. + * @return a deep copy of {@code args} for each solution found. Empty list if + * there are no solutions. + */ + public List<Term[]> all(String pkg, String functor, Term... args) { + setPredicate(pkg, functor, args); + setResultTemplate(args); + run(Integer.MAX_VALUE); + return resBuffer; + } + + private void setResultTemplate(Term t) { + resTemplate = new Term[] {t}; + resSingle = true; + } + + private void setResultTemplate(Term[] t) { + resTemplate = t; + resSingle = false; + } + + /** + * Execute until the limit is reached. + * + * @param newLimit maximum number of results. Set to 1 if only a single + * attempt is required. + * @return true if at least one result was discovered; false if there are no + * solutions to the predicate. + */ + private boolean run(int newLimit) { + resLimit = newLimit; + resBuffer = new ArrayList(Math.min(newLimit, 16)); + + executePredicate(); + return 0 < resBuffer.size(); + } + + @Override + public boolean isEngineStopped() { + return resLimit <= resBuffer.size(); + } + + @Override + protected void success() { + Term[] r = new Term[resTemplate.length]; + for (int i = 0; i < resTemplate.length; i++) { + r[i] = engine.copy(resTemplate[i]); + } + resBuffer.add(resSingle ? r[0] : r); + } + + @Override + protected void fail() { + resLimit = 0; + } +} |