| package jp.ac.kobe_u.cs.prolog.lang; |
| import java.util.Hashtable; |
| import java.io.*; |
| /** |
| * Prolog engine. |
| * |
| * @author Mutsunori Banbara (banbara@kobe-u.ac.jp) |
| * @author Naoyuki Tamura (tamura@kobe-u.ac.jp) |
| * @version 1.2 |
| */ |
| public class Prolog implements Serializable { |
| /** Prolog thread */ |
| public PrologControl control; |
| /** Argument registers */ |
| public Term[] aregs; |
| /** Continuation goal register */ |
| public Predicate cont; |
| /** Choice point frame stack */ |
| public CPFStack stack; |
| /** Trail stack */ |
| public Trail trail; |
| /* Push down list */ |
| // public PushDownList pdl; |
| /** 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>2</code> for <code>freeze/2</code> (not supported yet) |
| * </ul> |
| */ |
| public int exceptionRaised; |
| |
| /** <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 int maxInteger = Integer.MAX_VALUE; |
| /** Prolog implementation flag: <code>min_integer</code>. */ |
| protected 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 a list of frozen goals for <code>freeze/2</code> (not implemented yet). */ |
| protected Term pendingGoals; |
| /** 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 Hashtable<VariableTerm,VariableTerm> copyHash; |
| |
| /** The size of the pushback buffer used for creating input streams. */ |
| public static int PUSHBACK_SIZE = 3; |
| // public static int PUSHBACK_SIZE = 2; |
| |
| /** 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 HashtableOfTerm hashManager; |
| |
| /** Holds an atom <code>[]<code> (empty list). */ |
| public static SymbolTerm Nil = SymbolTerm.makeSymbol("[]"); |
| |
| /* Some symbols for stream options */ |
| static SymbolTerm SYM_MODE_1 = SymbolTerm.makeSymbol("mode", 1); |
| static SymbolTerm SYM_ALIAS_1 = SymbolTerm.makeSymbol("alias", 1); |
| static SymbolTerm SYM_TYPE_1 = SymbolTerm.makeSymbol("type", 1); |
| static SymbolTerm SYM_READ = SymbolTerm.makeSymbol("read"); |
| static SymbolTerm SYM_APPEND = SymbolTerm.makeSymbol("append"); |
| static SymbolTerm SYM_INPUT = SymbolTerm.makeSymbol("input"); |
| static SymbolTerm SYM_OUTPUT = SymbolTerm.makeSymbol("output"); |
| static SymbolTerm SYM_TEXT = SymbolTerm.makeSymbol("text"); |
| static SymbolTerm SYM_USERINPUT = SymbolTerm.makeSymbol("user_input"); |
| static SymbolTerm SYM_USEROUTPUT = SymbolTerm.makeSymbol("user_output"); |
| static SymbolTerm SYM_USERERROR = SymbolTerm.makeSymbol("user_error"); |
| |
| /** Constructs new Prolog engine. */ |
| public Prolog(PrologControl c) { |
| control = c; |
| aregs = new Term[maxArity]; |
| cont = null; |
| stack = new CPFStack(this); |
| trail = new Trail(this); |
| // pdl = new PushDownList(); |
| pcl = new PrologClassLoader(); |
| internalDB = new InternalDatabase(); |
| initOnce(); |
| } |
| |
| /** |
| * 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>hashManager</code> |
| * <li><code>streamManager</code> |
| * </ul> |
| */ |
| protected void initOnce() { |
| 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); |
| |
| copyHash = new Hashtable<VariableTerm,VariableTerm>(); |
| hashManager = new HashtableOfTerm(); |
| streamManager = new HashtableOfTerm(); |
| |
| 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)); |
| } |
| |
| /** Initializes this Prolog engine. */ |
| public void init() { |
| stack.init(); |
| trail.init(); |
| // pdl.init(); |
| B0 = stack.top(); |
| CPFTimeStamp = Long.MIN_VALUE; |
| |
| // Creates an initial choice point frame. |
| Term[] noarg = {}; |
| stack.create(noarg, null); |
| stack.setTR(trail.top()); |
| stack.setTimeStamp(++CPFTimeStamp); |
| stack.setBP(new Failure(control)); |
| stack.setB0(B0); |
| |
| exceptionRaised = 0; |
| |
| charConversion = "off"; |
| debug = "off"; |
| unknown = "error"; |
| doubleQuotes = "codes"; |
| printStackTrace = "off"; |
| |
| pendingGoals = Nil; |
| exception = SymbolTerm.makeSymbol("$none"); |
| startRuntime = System.currentTimeMillis(); |
| previousRuntime = 0; |
| |
| userOutput.flush(); |
| userError.flush(); |
| currentInput = userInput; |
| currentOutput = userOutput; |
| } |
| |
| /** Sets the top of choice porint stack to <code>B0</code> (cut pointer). */ |
| 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); |
| } |
| |
| /* |
| public boolean unify(Term a1, Term a2) { |
| Term d1, d2; |
| pdl.init(); |
| pdl.push(a1); |
| pdl.push(a2); |
| while (! pdl.empty()) { |
| d1 = pdl.pop().dereference(); |
| d2 = pdl.pop().dereference(); |
| if (d1 != d2) { |
| if (d1.isVariable()) { |
| ((VariableTerm)d1).bind(d2, trail); |
| } else if (d2.isVariable()) { |
| ((VariableTerm)d2).bind(d1, trail); |
| } else if (d2.isList()) { |
| if (! d1.isList()) |
| return false; |
| pdl.push(((ListTerm)d1).cdr()); |
| pdl.push(((ListTerm)d2).cdr()); |
| pdl.push(((ListTerm)d1).car()); |
| pdl.push(((ListTerm)d2).car()); |
| } else if (d2.isStructure()) { |
| if (! d1.isStructure()) |
| return false; |
| if (! ((StructureTerm)d1).functor.equals(((StructureTerm)d2).functor)) |
| return false; |
| for (int i=0; i<((StructureTerm)d1).arity; i++) { |
| pdl.push(((StructureTerm)d1).args[i]); |
| pdl.push(((StructureTerm)d2).args[i]); |
| } |
| } else if (! d1.equals(d2)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| */ |
| |
| /** |
| * Do backtrak. |
| * This method restores the value of <code>B0</code> |
| * and returns the backtrak point in current choice point. |
| */ |
| public Predicate fail() { |
| B0 = stack.getB0(); // restore B0 |
| return stack.getBP(); // 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 Predicate switch_on_term(Predicate var, |
| Predicate Int, |
| Predicate flo, |
| Predicate con, |
| Predicate str, |
| Predicate lis) { |
| Term arg1 = aregs[1].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 Predicate switch_on_hash(Hashtable<Term,Predicate> hash, Predicate otherwise) { |
| Term arg1 = aregs[1].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"); |
| } |
| Predicate 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() { |
| Term[] args = stack.getArgs(); |
| int i = args.length; |
| System.arraycopy(args, 0, aregs, 1, i); |
| cont = stack.getCont(); |
| } |
| |
| /** Creates a new choice point frame. */ |
| public Predicate jtry(Predicate p, Predicate next) { |
| int i = p.arity(); |
| Term[] args = new Term[i]; |
| System.arraycopy(aregs, 1, args, 0, i); |
| stack.create(args, cont); |
| stack.setTR(trail.top()); |
| stack.setTimeStamp(++CPFTimeStamp); |
| stack.setBP(next); |
| stack.setB0(B0); |
| 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 Predicate retry(Predicate p, Predicate next) { |
| restore(); |
| trail.unwind(stack.getTR()); |
| stack.setBP(next); |
| return p; |
| } |
| |
| /** |
| * Resets all necessary information from the current choice point frame, |
| * discard it, and then returns <code>p</code>. |
| */ |
| public Predicate trust(Predicate p) { |
| restore(); |
| trail.unwind(stack.getTR()); |
| 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; } |
| } |