//tabstop=4 //*****************************************************************************/ // Project: jpl // // File: $Id$ // Date: $Date$ // Author: Fred Dushin // // // Description: // // // ------------------------------------------------------------------------- // Copyright (c) 1998 Fred Dushin // All rights reserved. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library Public License for more details. //*****************************************************************************/ package jpl; import java.util.Hashtable; import jpl.fli.*; //----------------------------------------------------------------------/ // Term /** * A Term is a base class for the many different kinds of Term * (Atom, Variable, Compound, etc.). Programmers do not create * instances of Term classes directly; rather, they should create * instances of Term subclasses (Atom, Variable, Compound, etc.), * which will inherit functionality from this class. * *
* Copyright (C) 1998 Fred Dushin

* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version.

* * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library Public License for more details.

*


* @author Fred Dushin * @version $Revision$ */ // Implementation notes: // //----------------------------------------------------------------------/ public abstract class Term { //==================================================================/ // Attributes //==================================================================/ //==================================================================/ // Contructors and Initialization //==================================================================/ //------------------------------------------------------------------/ // Term /** * This default constructor is provided in order for subclasses * to be able to define their own default constructors. */ // Implementation notes: // //------------------------------------------------------------------/ protected Term() { } //==================================================================/ // Converting Terms to term_ts // // To convert a Term to a term_t, we need to traverse the Term // structure and build a corresponding Prolog term_t object. // There are some issues: // // - Prolog term_ts rely on the *consecutive* nature of term_t // references. In particular, to build a compound structure // in the Prolog FLI, one must *first* determine the arity of the // compound, create a *sequence* of term_t references, and then // put atoms, functors, etc. into those term references. We // do this in these methods first determinint the arity of the // Compound, and then by "puting" a type into a term_t. // The "put" methd is defined differently for each Term subclass. // // - What if we are trying to make a term_t from a Term, but the // Term has multiple instances of the same Variable? We want // to ensure that one Prolog variable will be created, or else // queries will give incorrect answers. We thus pass a Hashtable // (var_table) through these methods. The table contains term_t // instances, keyed on Variable instances. //==================================================================/ //------------------------------------------------------------------/ // put /** * Cache the reference to the Prolog term_t here. * * @param var_table A Hashtable containing term_t's that are * bound to (have been put in) Prolog variables as elements. The * Elements are keyed by jpl.Variable instances. Cf. the put() * implementation in the Variable class. * @param term A (previously created) term_t which is to be * put with a Prolog term-type appropriate to the Term type * (e.g., Atom, Variable, Compound, etc.) on which the method is * invoked.) */ // Implementation notes: This method is over-ridden in each of // the Term's subclasses, but it is always called from them, as well. //------------------------------------------------------------------/ protected abstract void put( Hashtable var_table, term_t term ); //------------------------------------------------------------------/ // terms_to_term_ts /** * This static method converts an array of Terms to a *consecutive* * sequence of term_t objects. Note that the first term_t object * returned is a term_t class (structure); the succeeding term_t * objects are consecutive references obtained by incrementing the * *value* field of the term_t. * * @param var_table A Hashtable containing term_t's that are * bound to (have be put in in) Prolog variables as elements. The * Elements are keyed by jpl.Variable instances. Cf. the put() * implementation in the Variable class. * @param arg An array of jpl.Term references. * @return consecutive term_t references (first of which is * a structure) */ // Implementation notes: // //------------------------------------------------------------------/ protected static term_t terms_to_term_ts( Hashtable var_table, Term arg[] ) { // // first create a sequence of term_ts. The 0th term_t // will be a jpl.fli.term_t. Successive Prolog term_t // references will reside in the Prolog engine, and // can be obtained by term0.value+i. // term_t term0 = Prolog.new_term_refs( arg.length ); // // for each new term reference, construct a prolog term // by puting an appropriate Prolog type into the reference. // This is more or less the protocol for building terms in // Prolog. // long ith_term_t = term0.value; for ( int i = 0; i < arg.length; ++i, ++ith_term_t ){ term_t term = new term_t(); term.value = ith_term_t; arg[i].put( var_table, term ); } return term0; } //==================================================================/ // Converting term_ts to Terms // // Converting back to Terms from term_ts is complex; one // issue is that there is not as much type information in a // term_t as one would expect. We can learn that a term_t // is a compund term, but not, for example, that it is a list // or a tuple. In this case, we must inspect the name of the // term_t ("." = list; "," = tuple). // // Another problem concerns variable bindings. We illustrate // with several examples. First, consider the prolog fact // // p( f( X, X ) ). // // And the query ?- p( Y ). A solution should be y = f( X, X ), // and indeed, if this query is run, the term_t to which Y will // be unified is a compound, f( X, X ). The problem is, how do // we know, in converting the term_ts to Terms in the compound f // whether we should create one Variable or two? This begs the // question, how do we _identify_ Variables in JPL? The answer // to the latter question is, by reference; two Variable (java) // references refer to the same variable iff they are, in memory, // the same Variable. That is, they satisfy the Java == relation. // (Note that this condition is _not_ true of the other Term types.) // // Given this design decision, therefore, we should create a // single Variable instance and a Compound instance whose 2 arg // values point to the same Variable. We therefore need to keep // track, in converting a term_t to a Term (in particular, in // converting a term_t whose type is variable to a Variable), of // which Variables have been created. We do this by using the vars // Hashtable, which gets passed recursively though the from_term_t // methods; this table holds the Variable instances that have been // created, keyed by the unique and internal-to-Prolog string // representation of the variable. //==================================================================/ //------------------------------------------------------------------/ // from_term_t /** * This method calls from_term_t on each term in the consecutive list * of term_ts. A temporary jpl.term_t structure must be created * in order to extract type information from the Prolog engine. * * @param vars A Hashtable containing jpl.Variable instances * as elements, indexed by their *Prolog* names, which are guaranteed * to be unique. Cf. the from_term_t method of the Variable class. * @param n The number of consecutive term_ts * @param term0 The 0th term_t (structure); subsequent * term_ts are not structures. * @return An array of converted Terms */ // Implementation notes: // //------------------------------------------------------------------/ protected static Term[] from_term_t( Hashtable vars, int n, term_t term0 ) { // create an array on n term references Term rval[] = new Term[n]; // // for each term_t (from 0...n-1), create a term_t // (temporary) structure and dispatch the translation // to a Term to the static from_term_t method of the Term // class. This will perform (Prolog) type analysis on the // term_t and call the appropriate static method to create // a Term of the right type (e.g., Atom, Variable, List, etc.) // long ith_term_t = term0.value; for ( int i = 0; i < n; ++i, ++ith_term_t ){ term_t term = new term_t(); term.value = ith_term_t; rval[i] = Term.from_term_t( vars, term ); } return rval; } //------------------------------------------------------------------/ // from_term_t /** * We do some type analysis on the term_t then forward the * call to the appropriate class * * @param vars A Hashtable containing jpl.Variable instances * as elements, indexed by their *Prolog* names, which are guaranteed * to be unique. Cf. the from_term_t method of the Variable class. * @param term The term_t to convert * @return The converted class. */ // Implementation notes: // //------------------------------------------------------------------/ protected static Term from_term_t( Hashtable vars, term_t term ) { int type = Prolog.term_type( term ); switch( type ){ case Prolog.VARIABLE: return Variable.from_term_t( vars, term ); case Prolog.ATOM: return Atom.from_term_t( vars, term ); case Prolog.INTEGER: return Integer.from_term_t( vars, term ); case Prolog.FLOAT: return Float.from_term_t( vars, term ); case Prolog.STRING: return String.from_term_t( vars, term ); case Prolog.TERM: return Compound.from_term_t( vars, term ); default: System.err.println( "Term.from_term_t: unknown term type" ); return null; } } //==================================================================/ // Computing Substitutions // // Once a solution has been found, the Prolog term_t references // will have been unified and will refer to new terms. To compute // a substitution, we traverse the (original) Term structure, looking // at the term_t reference in the Term. The only case we really care // about is if the (original) Term is a Variable; if so, the term_t // back in the Prolog engine contains the unified term. In this case, // we can store this term in a Hashtable, keyed by the Variable with // which the term was unified. //==================================================================/ //------------------------------------------------------------------/ // computeSubstitution /** * This method computes a substitution from a Term. The bindings * Hashtable stores Terms, keyed by Variables. Thus, a * substitution is as it is in mathematical logic, a sequence * of the form \sigma = {t_0/x_0, ..., t_n/x_n}. Once the * substitution is computed, the substitution should satisfy * * \sigma T = t * * where T is the Term from which the substitution is computed, * and t is the term_t which results from the Prolog query.

* * A second Hashtable, vars, is required; this table holds * the Variables that occur (thus far) in the unified term. * The Variable instances in this table are guaranteed to be * unique and are keyed on Strings which are Prolog internal * representations of the variables. * * @param bingings table holding Term substitutions, keyed on * Variables. * @param vars A Hashtable holding the Variables that occur * thus far in the term; keyed by internal (Prolog) string rep. */ // Implementation notes: // //------------------------------------------------------------------/ protected abstract void computeSubstitution( Hashtable bindings, Hashtable vars ); //------------------------------------------------------------------/ // computeSubstitutions /** * Just calls computeSubstitution in each Term in the list. * * @param table table holding Term substitutions, keyed on * Variables. * @param arg a list of Terms */ // Implementation notes: // //------------------------------------------------------------------/ protected static void computeSubstitutions( Hashtable bindings, Hashtable vars, Term arg[] ) { for ( int i = 0; i < arg.length; ++i ){ arg[i].computeSubstitution( bindings, vars ); } } //------------------------------------------------------------------/ // terms_equals /** * This method is used to determine the Terms in two Term arrays * are pairwise equal, where two Terms are equal if they satisfy * the equals predicate (defined differently in each Term subclass). * * @param t1 an array of Terms * @param t2 an array of Terms * @return true if all of the Terms in the arrays are pairwise equal */ // Implementation notes: // //------------------------------------------------------------------/ protected static boolean terms_equals( Term[] t1, Term[] t2 ) { if ( t1.length != t2.length ){ return false; } for ( int i = 0; i < t1.length; ++i ){ if ( !t1[i].equals( t2[i] ) ){ return false; } } return true; } // //------------------------------------------------------------------/ // // equals // /** // * Terms (Variables, actually) are keys in Hashtables. This // * method overrides the Object implementation so that // * // * @param obj The Object to compare. // * @return true if the Object is the same as this or if // * the Object's term_t is equal to this's. // */ // // Implementation notes: I'm not sure if this is needed any more... // // // //------------------------------------------------------------------/ // public boolean // equals( Object obj ) // { // if ( this == obj ){ // return true; // } // // if ( ! (obj instanceof Term) ){ // return false; // } // // if ( term == null || ((Term)obj).term == null ){ // return false; // } // // return this.term.equals( ((Term)obj).term ); // } //------------------------------------------------------------------/ // toString /** * Converts a list of Terms to a String. * * @param arg An aaray of Terms to convert * @return String representation of a list of Terems */ // Implementation notes: // //------------------------------------------------------------------/ public static java.lang.String toString( Term arg[] ) { java.lang.String s = ""; for ( int i = 0; i < arg.length; ++i ){ s += arg[i].toString(); if ( i != arg.length - 1 ){ s += ", "; } } return s; } public abstract java.lang.String debugString(); public static java.lang.String debugString( Term arg[] ) { java.lang.String s = "["; for ( int i = 0; i < arg.length; ++i ){ s += arg[i].debugString(); if ( i != arg.length - 1 ){ s += ", "; } } return s + "]"; } } //345678901234567890123456789012346578901234567890123456789012345678901234567890