| package com.googlecode.prolog_cafe.lang; |
| import java.io.BufferedReader; |
| import java.io.BufferedWriter; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.PushbackReader; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.IdentityHashMap; |
| import java.util.Map; |
| /** |
| * Prolog engine. |
| * |
| * @author Mutsunori Banbara (banbara@kobe-u.ac.jp) |
| * @author Naoyuki Tamura (tamura@kobe-u.ac.jp) |
| * @version 1.2 |
| */ |
| public final class Prolog { |
| private static final SymbolTerm NONE = SymbolTerm.intern("$none"); |
| |
| /** Prolog thread */ |
| public PrologControl control; |
| |
| /** Argument registers */ |
| public Term areg1, areg2, areg3, areg4, areg5, areg6, areg7, areg8; |
| public Term[] aregs; |
| private static final Term[] NO_REGISTERS = {}; |
| |
| /** Continuation goal register */ |
| public Operation cont; |
| /** Choice point frame stack */ |
| public final ChoicePointStack stack; |
| /** Trail stack */ |
| public final Trail trail; |
| /** Cut pointer */ |
| public int B0; |
| /** Class loader */ |
| public PrologClassLoader pcl; |
| /** Internal Database */ |
| public InternalDatabase internalDB; |
| |
| /** Current time stamp of choice point frame */ |
| protected long CPFTimeStamp; |
| |
| /** |
| * Exception level of continuation passing loop: |
| * <li><code>0</code> for no exception, |
| * <li><code>1</code> for <code>halt/0</code>, |
| * <li><code>1+N</code> for <code>halt(N)</code>. |
| * </ul> |
| */ |
| public int halt; |
| |
| /** <font color="red">Not supported yet</font>. Prolog implementation flag: <code>bounded</code>. */ |
| protected boolean bounded = false; |
| /** Prolog implementation flag: <code>max_integer</code>. */ |
| protected static final int maxInteger = Integer.MAX_VALUE; |
| /** Prolog implementation flag: <code>min_integer</code>. */ |
| protected static final int minInteger = Integer.MIN_VALUE; |
| /** Prolog implementation flag: <code>integer_rounding_function</code>. */ |
| protected String integerRoundingFunction = "down"; |
| /** <font color="red">Not supported yet</font>. Prolog implementation flag: <code>char_conversion</code>. */ |
| protected String charConversion; |
| /** Prolog implementation flag: <code>debug</code>. */ |
| protected String debug; |
| /** Prolog implementation flag: <code>max_arity</code>. */ |
| protected int maxArity = 255; |
| /** Prolog implementation flag: <code>unknown</code>. */ |
| protected String unknown; |
| /** <font color="red">Not supported yet</font>. Prolog implementation flag: <code>double_quotes</code>. */ |
| protected String doubleQuotes; |
| /** Prolog implementation flag: <code>print_stack_trace</code>. */ |
| protected String printStackTrace; |
| |
| /** Holds an exception term for <code>catch/3</code> and <code>throw/1</code>. */ |
| protected Term exception; |
| |
| /** Holds the start time as <code>long</code> for <code>statistics/2</code>. */ |
| protected long startRuntime; |
| /** Holds the previous time as <code>long</code> for <code>statistics/2</code>. */ |
| protected long previousRuntime; |
| |
| /** Hashtable for creating a copy of term. */ |
| protected final IdentityHashMap<VariableTerm,VariableTerm> copyHash; |
| |
| /** The size of the pushback buffer used for creating input streams. */ |
| public static final int PUSHBACK_SIZE = 3; |
| |
| /** Standard input stream. */ |
| protected transient PushbackReader userInput; |
| /** Standard output stream. */ |
| protected transient PrintWriter userOutput; |
| /** Standard error stream. */ |
| protected transient PrintWriter userError; |
| /** Current input stream. */ |
| protected transient PushbackReader currentInput; |
| /** Current output stream. */ |
| protected transient PrintWriter currentOutput; |
| /** Hashtable for managing input and output streams. */ |
| protected HashtableOfTerm streamManager; |
| |
| /** Hashtable for managing internal databases. */ |
| protected final HashtableOfTerm hashManager; |
| |
| /** Name of the builtin package. */ |
| public static final String BUILTIN = "com.googlecode.prolog_cafe.builtin"; |
| |
| /** Holds an atom <code>[]<code> (empty list). */ |
| public static final SymbolTerm Nil = SymbolTerm.intern("[]"); |
| |
| /* Some symbols for stream options */ |
| static final SymbolTerm SYM_MODE_1 = SymbolTerm.intern("mode", 1); |
| static final SymbolTerm SYM_ALIAS_1 = SymbolTerm.intern("alias", 1); |
| static final SymbolTerm SYM_TYPE_1 = SymbolTerm.intern("type", 1); |
| static final SymbolTerm SYM_READ = SymbolTerm.intern("read"); |
| static final SymbolTerm SYM_APPEND = SymbolTerm.intern("append"); |
| static final SymbolTerm SYM_INPUT = SymbolTerm.intern("input"); |
| static final SymbolTerm SYM_OUTPUT = SymbolTerm.intern("output"); |
| static final SymbolTerm SYM_TEXT = SymbolTerm.intern("text"); |
| static final SymbolTerm SYM_USERINPUT = SymbolTerm.intern("user_input"); |
| static final SymbolTerm SYM_USEROUTPUT = SymbolTerm.intern("user_output"); |
| static final SymbolTerm SYM_USERERROR = SymbolTerm.intern("user_error"); |
| |
| private static final PrintWriter NO_OUTPUT = new PrintWriter(new Writer() { |
| @Override |
| public void write(char[] cbuf, int off, int len) throws IOException { |
| throw new IOException("Prolog.Feature.IO disabled"); |
| } |
| |
| @Override |
| public void flush() throws IOException { |
| } |
| |
| @Override |
| public void close() throws IOException { |
| } |
| }); |
| |
| private static final PushbackReader NO_INPUT = new PushbackReader(new Reader() { |
| @Override |
| public int read(char[] cbuf, int off, int len) throws IOException { |
| return -1; |
| } |
| |
| @Override |
| public void close() throws IOException { |
| } |
| }); |
| |
| public static enum Feature { |
| /** Enable the {@code java_*} predicates, supporting reflection in Prolog. */ |
| JAVA_REFLECTION, |
| |
| /** Access to the local filesystem and console. */ |
| IO, |
| |
| /** Track the running time of evaluations */ |
| STATISTICS_RUNTIME; |
| } |
| protected final EnumSet<Feature> features = EnumSet.allOf(Feature.class); |
| |
| Prolog(PrologControl c) { |
| control = c; |
| trail = new Trail(); |
| stack = new ChoicePointStack(trail); |
| copyHash = new IdentityHashMap<VariableTerm, VariableTerm>(); |
| hashManager = new HashtableOfTerm(); |
| } |
| |
| Prolog(PrologControl c, PrologMachineCopy pmc) { |
| control = c; |
| trail = new Trail(); |
| stack = new ChoicePointStack(trail); |
| copyHash = new IdentityHashMap<VariableTerm, VariableTerm>(); |
| |
| // During restore there is no need to copy terms. clause/2 inside of |
| // builtins.pl copies the predicate when it reads from internalDB. |
| hashManager = PrologMachineCopy.copyShallow(pmc.hashManager); |
| internalDB = new InternalDatabase(this, pmc.internalDB, false); |
| } |
| |
| /** |
| * Initializes some local instances only once. |
| * This <code>initOnce</code> method is invoked in the constructor |
| * and initializes the following instances: |
| * <ul> |
| * <li><code>userInput</code> |
| * <li><code>userOutput</code> |
| * <li><code>userError</code> |
| * <li><code>copyHash</code> |
| * <li><code>streamManager</code> |
| * </ul> |
| */ |
| private void initOnce() { |
| if (8 < maxArity) |
| aregs = new Term[maxArity - 8]; |
| else |
| aregs = NO_REGISTERS; |
| |
| if (pcl == null) pcl = new PrologClassLoader(); |
| if (internalDB == null) internalDB = new InternalDatabase(); |
| |
| streamManager = new HashtableOfTerm(); |
| |
| if (features.contains(Feature.IO)) { |
| userInput = new PushbackReader(new BufferedReader(new InputStreamReader(System.in)), PUSHBACK_SIZE); |
| userOutput = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)), true); |
| userError = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.err)), true); |
| |
| streamManager.put(SYM_USERINPUT, new JavaObjectTerm(userInput)); |
| streamManager.put(new JavaObjectTerm(userInput), |
| makeStreamProperty(SYM_READ, SYM_INPUT, SYM_USERINPUT, SYM_TEXT)); |
| |
| streamManager.put(SYM_USEROUTPUT, new JavaObjectTerm(userOutput)); |
| streamManager.put(new JavaObjectTerm(userOutput), |
| makeStreamProperty(SYM_APPEND, SYM_OUTPUT, SYM_USEROUTPUT, SYM_TEXT)); |
| |
| streamManager.put(SYM_USERERROR, new JavaObjectTerm(userError)); |
| streamManager.put(new JavaObjectTerm(userError), |
| makeStreamProperty(SYM_APPEND, SYM_OUTPUT, SYM_USERERROR, SYM_TEXT)); |
| } else { |
| userInput = NO_INPUT; |
| userOutput = NO_OUTPUT; |
| userError = userOutput; |
| } |
| } |
| |
| /** Initializes this Prolog engine. */ |
| public void init() { |
| if (aregs == null) |
| initOnce(); |
| stack.init(); |
| trail.init(); |
| B0 = stack.top(); |
| CPFTimeStamp = Long.MIN_VALUE; |
| |
| // Creates an initial choice point frame. |
| ChoicePointFrame initialFrame = ChoicePointFrame.S0(null); |
| initialFrame.b0 = B0; |
| initialFrame.bp = Failure.FAILURE; |
| initialFrame.tr = trail.top(); |
| initialFrame.timeStamp = ++CPFTimeStamp; |
| stack.push(initialFrame); |
| |
| halt = 0; |
| |
| charConversion = "off"; |
| debug = "off"; |
| unknown = "error"; |
| doubleQuotes = "codes"; |
| printStackTrace = "off"; |
| |
| exception = NONE; |
| startRuntime = features.contains(Feature.STATISTICS_RUNTIME) |
| ? System.currentTimeMillis() |
| : 0; |
| previousRuntime = 0; |
| |
| currentInput = userInput; |
| currentOutput = userOutput; |
| } |
| |
| /** Ensure a feature is enabled, throwing if not. */ |
| public void requireFeature(Prolog.Feature f, Operation goal, Term arg) { |
| if (!features.contains(f)) { |
| throw new PermissionException(goal, "use", f.toString().toLowerCase(), arg, "disabled"); |
| } |
| } |
| |
| /** Sets B0 to the top of the choice point stack.. */ |
| public void setB0() { B0 = stack.top(); } |
| |
| /** Discards all choice points after the value of <code>i</code>. */ |
| public void cut(int i) { stack.cut(i); } |
| |
| /** Discards all choice points after the value of <code>B0</code>. */ |
| public void neckCut() { stack.cut(B0); } |
| |
| /** |
| * Returns a copy of term <code>t</code>. |
| * @param t a term to be copied. It must be dereferenced. |
| */ |
| public Term copy(Term t) { |
| copyHash.clear(); |
| return t.copy(this); |
| } |
| |
| /** |
| * Do backtrak. |
| * This method restores the value of <code>B0</code> |
| * and returns the backtrak point in current choice point. |
| */ |
| public Operation fail() { |
| ChoicePointFrame top = stack.top; |
| B0 = top.b0; // restore B0 |
| return top.bp; // execute next clause |
| } |
| |
| /** |
| * Returns the <code>Predicate</code> object refered, respectively, |
| * <code>var</code>, <code>Int</code>, <code>flo</code>, |
| * <code>con</code>, <code>str</code>, or <code>lis</code>, |
| * depending on whether the dereferenced value of argument |
| * register <code>areg[1]</code> is a |
| * variable, integer, float, |
| * atom, compound term, or non-empty list, respectively. |
| */ |
| public Operation switch_on_term(Operation var, |
| Operation Int, |
| Operation flo, |
| Operation con, |
| Operation str, |
| Operation lis) { |
| Term arg1 = areg1.dereference(); |
| if (arg1.isVariable()) |
| return var; |
| if (arg1.isInteger()) |
| return Int; |
| if (arg1.isDouble()) |
| return flo; |
| if (arg1.isSymbol()) |
| return con; |
| if (arg1.isStructure()) |
| return str; |
| if (arg1.isList()) |
| return lis; |
| return var; |
| } |
| |
| /** |
| * If the dereferenced value of arugment register <code>areg[1]</code> |
| * is an integer, float, atom, or compound term (except for non-empty list), |
| * this returns the <code>Predicate</code> object to which its key is mapped |
| * in hashtable <code>hash</code>. |
| * |
| * The key is calculated as follows: |
| * <ul> |
| * <li>integer - itself |
| * <li>float - itself |
| * <li>atom - itself |
| * <li>compound term - functor/arity |
| * </ul> |
| * |
| * If there is no mapping for the key of <code>areg[1]</code>, |
| * this returns <code>otherwise</code>. |
| */ |
| public Operation switch_on_hash(HashMap<Term,Operation> hash, Operation otherwise) { |
| Term arg1 = areg1.dereference(); |
| Term key; |
| if (arg1.isInteger() || arg1.isDouble() || arg1.isSymbol()) { |
| key = arg1; |
| } else if (arg1.isStructure()) { |
| key = ((StructureTerm) arg1).functor(); |
| } else { |
| throw new SystemException("Invalid argument in switch_on_hash"); |
| } |
| Operation p = hash.get(key); |
| if (p != null) |
| return p; |
| else |
| return otherwise; |
| } |
| |
| /** Restores the argument registers and continuation goal register from the current choice point frame. */ |
| public void restore() { |
| stack.top.restore(this); |
| } |
| |
| /** Creates a new choice point frame. */ |
| public Operation jtry0(Operation p, Operation next) { return finishjtry(p, next, ChoicePointFrame.S0(cont)); } |
| public Operation jtry1(Operation p, Operation next) { return finishjtry(p, next, new ChoicePointFrame.S1(this)); } |
| public Operation jtry2(Operation p, Operation next) { return finishjtry(p, next, new ChoicePointFrame.S2(this)); } |
| public Operation jtry3(Operation p, Operation next) { return finishjtry(p, next, new ChoicePointFrame.S3(this)); } |
| public Operation jtry4(Operation p, Operation next) { return finishjtry(p, next, new ChoicePointFrame.S4(this)); } |
| public Operation jtry5(Operation p, Operation next) { return finishjtry(p, next, new ChoicePointFrame.S5(this)); } |
| public Operation jtry6(Operation p, Operation next) { return finishjtry(p, next, new ChoicePointFrame.S6(this)); } |
| public Operation jtry7(Operation p, Operation next) { return finishjtry(p, next, new ChoicePointFrame.S7(this)); } |
| public Operation jtry8(Operation p, Operation next) { return finishjtry(p, next, new ChoicePointFrame.S8(this)); } |
| public Operation jtry(int arity, Operation p, Operation next) { |
| return finishjtry(p, next, new ChoicePointFrame.S9(arity, this)); |
| } |
| |
| private Operation finishjtry(Operation p, Operation next, ChoicePointFrame entry) { |
| entry.b0 = B0; |
| entry.bp = next; |
| entry.tr = trail.top(); |
| entry.timeStamp = ++CPFTimeStamp; |
| stack.push(entry); |
| return p; |
| } |
| |
| /** |
| * Resets all necessary information from the current choice point frame, |
| * updates its next clause field to <code>next</code>, |
| * and then returns <code>p</code>. |
| */ |
| public Operation retry(Operation p, Operation next) { |
| restore(); |
| ChoicePointFrame top = stack.top; |
| trail.unwind(top.tr); |
| top.bp = next; |
| return p; |
| } |
| |
| /** |
| * Resets all necessary information from the current choice point frame, |
| * discard it, and then returns <code>p</code>. |
| */ |
| public Operation trust(Operation p) { |
| restore(); |
| trail.unwind(stack.top.tr); |
| stack.delete(); |
| return p; |
| } |
| |
| Term makeStreamProperty(SymbolTerm _mode, SymbolTerm io, SymbolTerm _alias, SymbolTerm _type) { |
| Term[] mode = {_mode}; |
| Term[] alias = {_alias}; |
| Term[] type = {_type}; |
| |
| Term t = Nil; |
| t = new ListTerm(new StructureTerm(SYM_MODE_1, mode ), t); |
| t = new ListTerm(io, t); |
| t = new ListTerm(new StructureTerm(SYM_ALIAS_1, alias), t); |
| t = new ListTerm(new StructureTerm(SYM_TYPE_1, type ), t); |
| return t; |
| } |
| |
| /** Returns the current time stamp of choice point frame. */ |
| public long getCPFTimeStamp() { return CPFTimeStamp; } |
| |
| /** Returns the value of Prolog implementation flag: <code>bounded</code>. */ |
| public boolean isBounded() { return bounded; } |
| |
| /** Returns the value of Prolog implementation flag: <code>max_integer</code>. */ |
| public int getMaxInteger() { return maxInteger; } |
| |
| /** Returns the value of Prolog implementation flag: <code>min_integer</code>. */ |
| public int getMinInteger() { return minInteger; } |
| |
| /** Returns the value of Prolog implementation flag: <code>integer_rounding_function</code>. */ |
| public String getIntegerRoundingFunction() { return integerRoundingFunction; } |
| |
| /** Returns the value of Prolog implementation flag: <code>char_conversion</code>. */ |
| public String getCharConversion() { return charConversion; } |
| /** Sets the value of Prolog implementation flag: <code>char_conversion</code>. */ |
| public void setCharConversion(String mode) { charConversion = mode;} |
| |
| /** Returns the value of Prolog implementation flag: <code>debug</code>. */ |
| public String getDebug() { return debug; } |
| /** Sets the value of Prolog implementation flag: <code>debug</code>. */ |
| public void setDebug(String mode) { debug = mode;} |
| |
| /** Returns the value of Prolog implementation flag: <code>max_arity</code>. */ |
| public int getMaxArity() { return maxArity; } |
| |
| /** Returns the value of Prolog implementation flag: <code>unknown</code>. */ |
| public String getUnknown() { return unknown; } |
| /** Sets the value of Prolog implementation flag: <code>unknown</code>. */ |
| public void setUnknown(String mode) { unknown = mode;} |
| |
| /** Returns the value of Prolog implementation flag: <code>double_quotes</code>. */ |
| public String getDoubleQuotes() { return doubleQuotes; } |
| /** Sets the value of Prolog implementation flag: <code>double_quotes</code>. */ |
| public void setDoubleQuotes(String mode) { doubleQuotes = mode;} |
| |
| /** Returns the value of Prolog implementation flag: <code>print_stack_trace</code>. */ |
| public String getPrintStackTrace() { return printStackTrace; } |
| /** Sets the value of Prolog implementation flag: <code>print_stack_trace</code>. */ |
| public void setPrintStackTrace(String mode) { printStackTrace = mode;} |
| |
| /** Returns the value of <code>exception</code>. This is used in <code>catch/3</code>. */ |
| public Term getException() { return exception; } |
| /** Sets the value of <code>exception</code>. This is used in <code>throw/1</code>. */ |
| public void setException(Term t) { exception = t;} |
| |
| /** Returns the value of <code>startRuntime</code>. This is used in <code>statistics/2</code>. */ |
| public long getStartRuntime() { return startRuntime; } |
| |
| /** Returns the value of <code>previousRuntime</code>. This is used in <code>statistics/2</code>. */ |
| public long getPreviousRuntime() { return previousRuntime; } |
| /** Sets the value of <code>previousRuntime</code>. This is used in <code>statistics/2</code>. */ |
| public void setPreviousRuntime(long t) { previousRuntime = t; } |
| |
| /** Returns the standard input stream. */ |
| public PushbackReader getUserInput() { return userInput; } |
| /** Returns the standard output stream. */ |
| public PrintWriter getUserOutput() { return userOutput; } |
| /** Returns the standard error stream. */ |
| public PrintWriter getUserError() { return userError; } |
| |
| /** Returns the current input stream. */ |
| public PushbackReader getCurrentInput() { return currentInput; } |
| /** Sets the current input stream to <code>in</code>. */ |
| public void setCurrentInput(PushbackReader in) { currentInput = in; } |
| |
| /** Returns the current output stream. */ |
| public PrintWriter getCurrentOutput() { return currentOutput; } |
| /** Sets the current output stream to <code>out</code>. */ |
| public void setCurrentOutput(PrintWriter out) { currentOutput = out; } |
| |
| /** Returns the stream manager. */ |
| public HashtableOfTerm getStreamManager() { return streamManager; } |
| |
| /** Returns the hash manager. */ |
| public HashtableOfTerm getHashManager() { return hashManager; } |
| } |