Document #5630003 January 30, 1986 47 Pages WATERLOO CORE PROLOG USER'S MANUAL Version 1.7 Intralogic Inc. 330 Weber St. N. Waterloo, Ontario, Canada N2J 3H6 Tel. (519) 888-6671 Table of Contents 1 Introduction . . . . . . . . . . . . . . . . . . . 2 2 Syntax . . . . . . . . . . . . . . . . . . . . . . 3 3 Extensions to Built-in Predicates . . . . . . . . 7 4 Additional Predicates . . . . . . . . . . . . . . 11 5 I/O Predicates . . . . . . . . . . . . . . . . . . 21 6 Debugging Predicates . . . . . . . . . . . . . . . 26 7 Named-variable Predicates . . . . . . . . . . . . 28 8 System Dependent Predicates . . . . . . . . . . . 30 9 Workspace Predicates . . . . . . . . . . . . . . . 32 10 User Defined Subroutines . . . . . . . . . . . . 35 11 Execution Stack Description . . . . . . . . . . . 40 12 Getting Started . . . . . . . . . . . . . . . . . 41 13 Sample Terminal Session . . . . . . . . . . . . . 43 - 1 - 1 Introduction Waterloo Core Prolog is based on the "core" syntax described in Clocksin and Mellish's Programming in Prolog (PIP). Certain built-in predicates described in the book have been enhanced to provide more flexibility. Also, there are several additional predicates provided. This manual is intended to be an addenda to PIP. - 2 - 2 Syntax Operators Operator declarations are slightly restricted. The permissable operator types are: fx - prefix operator xf - postfix operator yfx - infix (left associative) xfy - infix (right associative) An operator may simultaneously be declared as prefix and infix, but not prefix and postfix, or postfix and infix. The following is a list of the initial system operators: op(255,yfx,:-). op(255,fx,?-). op(254,xfy,;). op(253,xfy,','). op(250,fx,spy). op(250,fx,nospy). op(60,fx,not) op(51,xfy,.). op(40,yfx,is). op(40,yfx,=..). op(40,yfx,=). op(40,yfx,\=). op(40,yfx,==). op(40,yfx,\==). op(40,yfx,<). op(40,yfx,=<). op(40,yfx,>). op(40,yfx,>=). op(40,yfx,=:=). op(40,yfx,=\=). op(31,yfx,-). op(31,yfx,+). op(21,yfx,*). op(21,yfx,/). op(11,yfx,mod). op(10,fx,+). op(10,fx,-). - 3 - Atoms The definition of an atom has been extended to allow atoms to begin with a digit. However, if the term is made up of only digits, it is interpreted as an integer. For example, 123ab is a valid atom but, 12345 is an integer. Integers Signed integers are supported and must be enclosed in quotes. If no sign is used, the quotes may be discarded. The valid integers are in the range -2**23 <= I < 2**23. The following integers represent the same integer: '+00001' 001 1 Infinite Terms A special notation is provided to allow the representation of infinite terms. An example of an infinite term is the result of unifying X with f(X), which is f(f(f(...))). Representations of infinite terms can be read, written, unified, asserted or retracted. A special notation is introduced to simplify the representation. The notation has the format ##n## where n is the length of the loop. For example, X unified with f(X) gives f(##1##) X unified with f(g(1,X,3)) gives f(g(1,##2##,3)) X unified with f(g(h(X))) gives f(g(h(##3##))) X unified with f(X,X) gives f(##1##,##1##) If an input term contains an invalid infinite term reference, an error message is printed. For example, the term f(g(##3##)) is invalid because the loop length is too large. When a term is written, the loop length specified is not necessarily minimal. For example, if the result of unifying X and f(f(X)) is printed, the result will be f(f(##2##)), even though the format f(##1##) is more compact. - 4 - Meta Variables Variables may be used directly as goals. Instead of invoking a goal X through : ..., call(X), ... the variable may be specified on its own : ..., X, ... The call predicate is still valid and is defined by the PROLOG clause: call(X) :- X. Reals Real numbers are supported using the IBM/370 long format representation. The range of reals is 16**-65 <= R <= (1-16**-14)*16**63. Reals must always be quoted and are denoted by: a) an int followed by an exponential part b) an int followed by a decimal followed by an int c) as b), followed by an exponential part d) as a), b) or c) preceded by a '+' or '-' An int is defined as a sequence of 1 or more digits. An exponential part is defined by an 'e' or 'E', followed by an optional '+' or '-', followed by an int. Upper Case Syntax An alternative syntax is provided for terminals which do not support lower case letters. In this mode of operation, variables are distinguished from atoms by starting with the underscore character. For example, the clauses append([],L,L). append([X|L1],L2,[X|L3]):-append(L1,L2,L3). - 5 - in uppercase notation would be: APPEND([],_L,_L). APPEND([_X|_L1],_L2,[_X|_L3]):-APPEND(_L1,_L2,_L3). In order to operate in this mode, an option must be specified when PROLOG in invoked (See Section 11 - Getting Started). - 6 - 3 Extensions to Built-in Predicates ! The cut predicate can be used with no arguments as described in PIP, or with one or two arguments. The two arguments allow the specification of certain goals to be eliminated as backtrackable points. The first argument specifies an ancestor and the second argument specifies the ancestor index (see the ancestor predicate). For example, for the following clauses: a:-b,c,d. a. b. c:-!(a). d:-fail. if we attempt to prove a, the proof would fail because a's second clause has been eliminated from the proof by the goal !(a). But, if c:-!(a) is changed to c, the second clause for a could be used in the proof. An alternate form for the c:-!(a) clause is c:-!(_,2) c:-!(a) c:-!(_,2) because a is the second ancestor of the goal !. arg The arg(N,T,A) predicate must have T instantiated and either N or A instantiated. When N is instantiated, it unifies the Nth argument of T with A. When A is instantiated, it unifies A's argument number in T with N. For example, arg(2,f(a,b,c),X) gives X = b arg(X,f(a,b,c),c) gives X = 3 atomic PIP describes atomics as integers and atoms. This has been extended to include real numbers and hence, the atomic predicate succeeds if its argument is an atom, integer or real number. - 7 - clause The clause predicate has an optional third argument which is the clause index. The index refers to the clause's database order (proof order). For example, if the following clauses were in the database: a(1):-u. a(2):-v. a(3):-w. then, clause(a(2),v,2). would succeed. consult, reconsult The consult and reconsult predicates allow the adding of clauses with their variable names. This is controlled by the control declaration var_names. If var_names var_names is on, variable names are preserved. If var_names on var_names is off, the variable names are lost and the var_names off variables will be represented by the _n notation. _n Expressions Arithmetic expressions as used in the is predicate may be used by the comparative predicates <, =<, >, >= and the put predicate. Also, two new predicates =:= and =\= are introduced as the equal and not equal equivalents. For example, the goal 2*3 > 12/(6-2). succeeds since 6>3. A list consisting of a single number is considered to be an arithmetic expression with the same value as the number. This allows goals such as put("a") to be valid. Remember that "a" is equivalent to the list [129] in EBCDIC and [97] in ASCII. Three additional operators are provided in - 8 - expressions: abs(X) - absolute value of X fix(X) - fixed point conversion of X float(X) - floating point conversion of X Comparing Atomics An atomic is an atom, integer or real number. The comparative predicates <, =<, >, >=, =:= and =\= have been enhanced to allow atomics as arguments. This allows you to freely compare atoms, integers and reals. Integers and reals are treated as being greater than atoms and the EBCDIC code determines the comparative result between atoms. listing The listing predicate has been enhanced to allow the displaying of all predicates in the database. This can be done by calling listing with no arguments or with one argument, an uninstantiated variable. retract The retract predicate has been enhanced to allow a second argument, the index of the clause in the database. For instance, if we had the following clauses in the database: a(1). a(2). a(3). the goal retract(a(_),2). would delete a(2) from the database because it is the second clause in the database for a with 1 argument. repeat The repeat predicate can be used with zero to four arguments to perform looping in a proof. repeat with no arguments works as described in PIP. The optional - 9 - arguments of repeat are: argument 1 - loop counter argument 2 - initial value argument 3 - stopping value argument 4 - step value For example, the goal repeat(X,2,8,2), write(hello), nl, fail. writes hello 4 times and then fails. The loop counter must be a variable and is instantiatied to the loop count. The initial value, stopping value and step value must all be integers. If no step value is specified, it defaults to 1. - 10 - 4 Additional Predicates =:=,=\= These two predicates are used to test the equality and inequality of expressions, integers and atoms. They are different than == and \== because they allow arithmetic expressions as used in the is predicate. ancestor The parent of a goal G is the goal which invoked the clause containing goal G. The ancestors of a goal include its parent and its parent's ancestors. For the following clauses, a:-b. b:-c,d. d:-e,f. the first ancestor of e is d, the second ancestor of e is b and the third ancestor of e is a. The ancestor predicate is used to examine the ancestors in a proof. ancestor may be resatified. It has two arguments : argument 1 - ancestor argument 2 - ancestor index (optional) When ancestor is called with one argument, the ancestor index defaults to 1 and the ancestor is unified with argument 1. For example, the following clause lists all ancestors of the ancestor predicate and then fails. listanc :- ancestor(A), write(A), nl, fail. When the ancestor predicate is used with 2 arguments, the ancestor index is unified with the second argument. For example, the following clause lists the five most recent ancestors: list5anc :- ancestor(A,N), write(A), N==5. - 11 - assert The assert predicate may be used with one or two arguments. When used with 1 argument, it is equivalent to assertz. That is, it adds a clause at the end of the database. When used with two arguments, the second argument specifies the index of where the clause should be added in the database. If the second argument is an uninstantiated variable, it is unified with the clause's index. attn The attn predicate is a user-definable 'hook'. It is called when the attention key or break key is pressed on the terminal. User clauses may be added for attn to provide whatever exception handling is desired. The initial set of clauses when PROLOG is invoked defines attn by the clause: attn :- error. avar The avar predicate has one argument. It succeeds if the argument is an anonymous variable. The avar predicate is used to distinguish anonymous variables from regular variables since the var predicate succeeds for both. Anonymous variables are denoted by a single underline character. They are 'don't care' variables for arguments not being used in a clause. The use of anonymous variables is recommended because they are quick during unification and are memory efficient during a proof. ax, axiom, axn These predicates are used to display clauses in the database. All three predicates may be resatisfied. The axn predicate has the following formats: axn(,,) axn(,,,) For example, the following goal - 12 - axn(f,2,C), write(C), nl, fail. writes all clauses having clause head f with 2 arguments. The following goal axn(f,1,C,3), write(C), nl. writes the third clause in the database having clause head f with 1 argument. The axiom predicate has the following formats: axiom(,). axiom(,,) For example, the following goal axiom(f(a),C,2), write(C), nl. writes the second clause in the database with clause head f(a). The ax predicate has the following formats: ax(,) ax(,,) It is similar to the axiom predicate except for the first argument. The first argument of ax is an implicit head whereas axiom's is explicit. For example, ax(f(1,2),C), write(C), nl, fail. writes all clauses in the database with head f and 2 arguments. Whereas, axiom(f(1,2),C), write(C), nl, fail. writes all clauses in the database with head f having first and second arguments equal to 1 and 2 respectively. - 13 - axver The axver predicate has one argument and succeeds if the argument represents a valid clause. A valid clause requires the head to be an atom or a skeleton. Also, the subgoals must be composed of atoms, skeletons and variables. boot boot is a user-definable predicate which is executed when all initialization is completed. User clauses may be added for boot in the initial set of clauses when PROLOG is invoked. control The control predicate is an efficient way of providing special global facilities. control has two arguments, a key and a result. A key must be an atom, and a result can be an integer or atom. assert and retract can be used to add and delete control declaration. For example, assert(control(heat,on)). adds the control declaration with key-result pair (heat,on). To display the result of key head, the following goal control(heat,X) unifies on with X. To change the result from on to off, the following goal can be used: assert(control(heat,off)). To display all control declarations, the following goal can be used: control(Key,Result), write(control(Key,Result)), nl, fail. Several special control declarations are provided : - 14 - random - the seed for the random predicate (initial result : 464643) - valid results : any integer syntax_errors - incremented whenever a syntax error occurs (initial result : 0) - valid results : any integer tro - controls the use of tail recursion optimization (See Section 11 - Execution Stack Description) (initial result : on) - valid results : on, off var_names - control the storing of variable names when using predicates consult and reconsult. If var_names is on, clauses are added to the database with their corresponding variable names. (initial result : off) - valid results : on, off verbose - controls the printing of a successful proof (initial result : on) - valid results : on, off write_suffix - controls the printing of the '.' and newline for the wwrite predicate (initial result : on) - valid results : on, off delop The delop predicate deletes operator declarations. It has three arguments which correspond to op's arguments. The first argument represents the priority and must be an integer or an uninstantiated variable. The second argument represents the associativity and must be one of fx, yfx, xfy, or xf. The third argument is the operator name. It must be instantiated to an atom. delop fails if an attempt is made to delete a non-existant operator declaration. digit The digit predicate has one argument and tests whether a term is a single digit. - 15 - error The error predicate differs from the other predicates in the system in that it is not a built-in predicate definition but a special interface which can be used to call a user-defined predicate. The error predicate is called when certain non-disasterous errors occur in a proof. A useful set of clauses for error are included in the initial set of clauses when PROLOG is invoked. findop The findop predicate is used to display the current operator declarations. It has three arguments that correspond to the op predicate. The first argument is the operator priority, the second is the associativity and the third argument is the operator name. findop is resatisfiable. For example, the following goal displays all operator declarations: findop(P,A,N),write([P,A,N]),nl,fail. freeclause_ freeclause_ The freeclause_ predicate is normally of use only freeclause_ in very specialized instances, usually when writing second level interpreters in PROLOG. When using a second level interpreter which 'never finishes', certain anomalies occur in the recovery of space from deleted clauses. When a clause is deleted in a proof, the space for the clause is placed on a 'deferred free list'. The space is not freed directly since the clause may still be used in the proof. Space on this deferred free list is freed when the proof is completed. Thus in a second level interpreter which is continually adding and deleting clauses, a large deferred free list may be built up and the interpreter can run out of space. To provide for this situation, the freeclause_ freeclause_ predicate is provided. Invoking the freeclause_ goal freeclause_ causes all space on the deferred free list to be freed. It is the responsibility of the programmer to ensure that the current proof does not contain any references to freed clauses. Otherwise, disastrous results are likely! - 16 - letter The letter predicate has one argument and tests whether a term is a single letter. no_clause no_clause The no_clause predicate is a user-definable no_clause 'hook'. It is called when there are no clauses in the database whose head matches the goal. no_clause has no_clause one argument which is the goal's head. The initial set of clauses provided by PROLOG defines no_clause as: no_clause no_clause(Head) :- functor(Head,F,N), wwritech('no clauses - '), wwritech(F), wtab(1), wwritech(N), wnl, fail. numeric The numeric predicate has one argument. It succeeds if the argument is an integer or a real number. random The random predicate has two arguments. The first argument must be a positive integer R and the second argument must be a variable. random instantiates the second argument to a randomly chosen integer between 1 and R. A "seed" is used to compute the random integer. It is updated at each call to random. The seed's value is stored in the control declaration with key random. It may be updated manually using the goal : assert(control(random,Seed)). where Seed must be an integer. real The real predicate has one argument. It succeeds if the argument is a real number. - 17 - retracting retracting has zero or one arguments. When used with no arguments or with the one argument being an uninstantiated variable, retracting deletes all non- builtin clauses in the database. retracting with the argument being an atom P deletes all clauses with predicate P. retractnb retractnb is a non-backtracking retract. It behaves as retract except that it does not attempt to resatisfy the clause deletion on failure. retry The retry predicate is provided to facilitate recovery from an error situation. After a correction has been made, the proof may be restarted from some point before the error. retry has one or two arguments which control a search through the ancestors exactly as for ancestor. The difference is the action taken upon success. If an appropriate ancestor is found, the proof is backed up to the point where the subproof for the ancestor predicate began and the proof is restarted from that point. retry restores the proof to the state it had at a particular point in the past. Consequently, retry is only useful when some change has been made to the clauses. skel The skel predicate has one argument and tests whether a term is a structure (or skeleton). statistics The statistics predicate is used to acquire PROLOG system statistics. Statistics available include the number of used and free bytes in the heap, control stack and the global stack. Also, the number of relocations of the control stack may be checked (See Section 11 - Execution Stack Description for a description of the heap, control stack and global stack). statistics may be called with no arguments or - 18 - two arguments. If called with no arguments, all information is printed to the terminal. For statistics(Arg1,Arg2), Arg1 must be instantiated to one of the following atoms: relocations heap control_stack global_stack Arg2 must be an uninstantiated variable. If Arg1 is relocations, Arg2 is unified with the number of relocations of the control stack. If Arg1 is either heap, global_stack or control_stack, Arg2 is unified heap global_stack control_stack Arg2 with a two element list. The first element is the number of bytes being used and the second element is the number of bytes that are free. stop The stop predicate is used to leave the PROLOG system. The execution of stop terminates the PROLOG session and returns to the operating system. All clauses and operator declarations in the current workspace are lost. string The string predicate is similar to the name predicate. It is used to create an atom from a list of characters or to create a list from the characters of an atom. The difference between string and name is that the characters in name's list are their EBCDIC representation whereas string's characters are 'real'. For example, name(hi,X). (unifies X with [136,137]) string(hi,X). (unifies X with [h,i]) upshift The upshift predicate has two arguments, of which at least one must be a constant. upshift is used to change lowercase letters into uppercase letters and vice versa. If the first argument is a single lowercase letter then the second argument is unified - 19 - with the uppercase constant for the same letter. If the first argument is a variable and the second argument is a single uppercase letter, then the first argument is unified with the lowercase constant for the same letter. varlist The varlist predicate is used mainly in conjunction with the named-variable predicates (See section 7 - Named-variable Predicates). It creates a list of the uninstantiated variables (excluding anonymous variables) from the term specified by the first argument. The list is unified with the second argument. - 20 - 5 I/O Predicates Two sets of I/O predicates are provided, the core PROLOG I/O predicates as described in PIP and an additional set of I/O predicates. Filenames The default input and output stream is the user's terminal. A filename of 'user' is given to the user's terminal for the 'core' predicates. The additional I/O predicates use the user's terminal by specifying no filename. There are three type of files available : 1. Internal files 2. External files 3. Library files Internal files are CMS files with the format 'NNNNNNNN PROLOG'. Internal files are referenced by using the 'NNNNNNNN' filename and the filetype defaults to PROLOG. For example, the following goal reads the first term from the file TERMS PROLOG: see(terms), read(X). External files are described through a FILEDEF command. External files are recognized by a preceding slash. For example, the following goal reads the first term from the file TERMS FILE A: system([filedef,terms,disk,terms,file,a]), see('/terms'), read(X). Library files are CMS maclib members with the format 'NNNNNNNN' MACLIB A1 (MMMMMMMM)'. Library files are referenced with the format 'NNNNNNNN(MMMMMMMM)' where NNNNNNNN is the library name and MMMMMMMM is the library member. For example, the following goal reads the first term from 'PROLIB MACLIB A1 (TERMS)': see('prolib(terms)'), read(X). - 21 - In conclusion, several files may be open at the same time. However, no file may be open for input and output simultaneously. Core I/O Predicates These predicates are compatible with the predicates described in PIP. read, get, and get0 return the atom eof when they reach the end of a file. put allows the use of expressions including single element lists such as put("a"). An additional predicate, writeq, is provided to write terms in a readable form. That is, it writes single quotes around all atoms that need them. Additional I/O Predicates The additional I/O predicates provide an easy way of reading and writing several files simultaneously. It also allows relative record reading and writing. All of these predicates begin with the letter 'w'. (Note: library files and variable length files do not support relative record I/O). The control declaration write_suffix controls the printing of a '.' and newline write_suffix at the end of a term for the predicates wwrite, wwriteq and wdisplay. If write_suffix is on, the suffix is wdisplay write_suffix on printed. A result of off omits the printing of the suffix. wread The wread predicate has one, two or three arguments. The second argument is the optional file identifier. The third argument is an optional record number. It must be a positive integer, indicating where in the file the read is to start. The first record in the file has a record number of 1. A term is read from the indicated file and unified with the first argument. The term must be delimited with the end of term character. If the end of the input file has been reached the predicate fails. If backtracking returns to the read then a read of the next term will be attempted. If the term read cannot be unified with the first argument or the format of the term is invalid then backtracking will cause a read of the next term to be attempted. - 22 - wwrite The wwrite predicate has one, two or three arguments. The second argument is the optional file identifier. The third argument is an optional record number. The term specified by the first argument is written on the indicated file. The term is delimited by the end of term character. The term is written using prefix, infix and suffix notation where appropriate, as indicated by the operator declarations at the time of writing. wwriteq The wwriteq predicate has one, two or three arguments. It functions in a manner very similar to wwrite. The only difference occurs in the format of the written output. wwriteq encloses identifiers in quotes as required, to ensure that the written term can be read back in by the wread predicate. Thus any identifiers containing blanks, punctuation symbols, etc. will be written enclosed in apostrophes. wdisplay The wdisplay predicate has one, two or three arguments. The arguments are as in wwrite. wdisplay writes terms in canonical form. That is, all operator declarations are ignored. wreadch The wreadch predicate has one, two or three arguments. The first argument must be a variable or an atom, the second argument is an optional file identifier, and the third argument is an optional record number. A single character is read from the given file. The constant whose value is the single character is unified with the first argument. If the end of an input line (or record) has been reached then the first character of the next line (or record) is read. If the end of the input file has been reached then the predicate fails. wreadch may be resatisfied. - 23 - wreadempty The wreadempty predicate is provided for use in conjunction with the wreadch predicate. It allows record boundaries to be detected when reading a character at a time. wreadempty has one optional argument - the file identifier. It succeeds if the input buffer is empty (i.e. the next readch will cause a new physical record to be read). wwritech The wwritech predicate has one, two or three arguments. The second argument is the optional file identifier. The third argument is an optional record number. The first argument specifies a term which is formatted using the operator declarations and placed in the output buffer for the given file. If the buffer is filled then it is written to the given file (and emptied). If the buffer is partially filled then it is not written out. Note that the wreadch and wwritech predicates are not symmetrical. The writech predicate can be used to write a single character but it is considerably more general than wreadch. wnl The wnl predicate has one optional argument - the file identifier. It writes the current output buffer to the given file and empties the buffer. wnl is used in conjunction with wwritech. For example, the goal statement: wwritech('on '), wwritech(one), wwritech(' line.'), wnl. causes the following to be written on the terminal: on one line. Note that this output is identical to that produced by the call wwrite('on one line'). or by the call - 24 - wwritech('on o'), wwrite('ne line'). wfileclose The wfileclose predicate has one argument - a file identifier. It may be used to logically close a file so that it may be reread from the beginning. Note that when a file is used for input after output, the file is automatically closed so that the first input will be from the beginning of the file. In a similar manner, output after input will cause an automatic close. Output to an existing file will be appended to the end of the file. wtab The wtab predicate has one or two arguments. The second argument is the optional file identifier. The first argument must be a non-negative integer. It specifies the number of blanks to be written on the output file. - 25 - 6 Debugging Predicates The debugging facility provided by WCP is different from the one described in PIP. Exhaustive tracing is not supported. Spy points are provided with a four port trace as described in PIP. The four ports are call, exit, redo, and fail. At each port, the user definable systrace_ predicate is called. systrace_ systrace_ systrace_ must succeed for ports call and exit and must fail for ports redo and fail. The normal clauses for systrace_ redo fail systrace_ (which are included in the initial set of clauses when PROLOG is invoked) are : systrace_(Port,Goal) :- systrace(Port,Goal), fail. systrace_(call,Goal). systrace_(exit,Goal). The user may then define the systrace predicate as desired. The initial set of clauses contains the clause : systrace(Port,Goal) :- wwritech(Port), wtab(1), wwrite(Goal). This clause may be deleted or preceded by another user clause to modify the output format. For example, if the following clauses are added prior to the clause above, the call and redo ports are leashed. systrace(Port,Goal) :- (Port==call; Port==redo), call_trace(Port,Goal), !. call_trace(Port,Goal) :- wwritech(Port), tab(1), wwritech(Goal), tab(1), wwritech(?), wread(X), react(X). react(go). react(X) :- X, !, fail. The user can then enter commands at the call and redo ports. Typical commands include ancestor displaying - 26 - and spy point altering. Entering go. causes the proof to continue. In general, the user may find it useful to provide different clauses for systrace, but those for systrace_ systrace systrace_ should be left unchanged. In order to manipulate spy points, four predicates are provided: spy - add spy points nospy - remove spy points debugging - display all spy points nodebug - remove all spy points They work as described in PIP. - 27 - 7 Named-variable Predicates When writing a term containing uninstantiated variables such as write(a(X,Y,X)). the term is written as: a(_1,_2,_1) The variables are written in the format _N where N is the variable number (left-right order of variables). PROLOG does not keep track of the original variable names X and Y. There are certain cases where the original variable names would be beneficial. A clause editor displaying the original variable names would make the presentation of a clause easier to read. Several predicates are provided to give PROLOG named-variable capability. They allow the reading and writing of terms. Clauses may also be asserted to the database with named-variables. A clause type predicate is provided to look at clauses. The arguments to these predicates make use of list notation to store the term and a list of the variable name in the format [Term,[Variable_name_list]] For example, the corresponding named variable predicate for write is writev and the goal writev([a(X,Y,X),['X','Y']]),nl. prints a(X,Y,X) The elements in the variable name list ['X','Y'] need to be quoted or they will be treated as variables. The following are the named-variable predicates. readv, wreadv readv and wreadv correspond to the read and wread predicates. The first argument must be an - 28 - uninstantiated variable and the input term with its variable-name-list is unified with the variable. writev, writeqv, displayv, wwritev, wwriteqv, wdisplayv These predicates correspond to write,writeq,display,wwrite,wwriteq and wdisplay respectively. The first argument must be in the format [Term,[Variable_name_list]] where the Variable_name_list's elements correspond to the left-right order of variables. If the term contains more variables than are in the Variable_name_list, the _N format is used for the remainding variables. assertv The assertv predicate corresponds to assert. The number of variables in the term must equal the number of elements in the variable-name-list or an error occurs. axv, axnv The axv and axnv predicates are the named- variable predicates corresponding to ax and axn. The second argument of axv and the third argument of axnv are unified with a [Term,[Variable_name_list]] list instead of a clause as in ax and axn. If a clause is added using assert rather than assertv, axv and axnv succeed, but the Variable_name_list will be a nil list ([]). - 29 - 8 System Dependent Predicates edit The edit predicate invokes the CMS editor from the PROLOG environment. For example, the goal edit(blocks). will edit the file blocks and then return to PROLOG. rexx The rexx predicate is used to call REXX and EXEC2 execs that have untokenized, mixed case arguments. This is unlike the system predicate which tokenizes the arguments into 8 character uppercase arguments. rexx may be invoked with one or two arguments. The first argument specifies the EXEC to be executed and its arguments in list notation. The second argument is optional. If present, it is unified with the integer return code from the EXEC. If the second argument is not specified, the return code is ignored. Only REXX and EXEC2 execs may be called with rexx. The following call to rexx calls an exec date with arguments 'Wednesday', 'February', 13 and 1985. rexx([date,'Wednesday','February',13,1985]). system The system predicate allows CMS commands to be executed from the PROLOG environment. It may be invoked with one or two arguments. The first argument specifies the command to be executed. The second argument is optional. If present, it is unified with the integer return code from the CMS command. If the second argument is not present then the return code is ignored. The command to be executed is specified as a list of one or more constants. Each constant corresponds to one token in the CMS command. Tokens may be no more than eight characters long. In order to invoke CP commands, simply use an initial token of cp. Note that all lower case letters in tokens are automatically shifted to uppercase. The left - 30 - parenthesis preceding the options is a separate token. The following are valid calls to system: system([print,prolog,exec]). system([l,'*',prolog],Returncode). system([cp,q,users]). system([t,prolog,maclib,'(',member,xxxxxx]). xedit The xedit predicate invokes the xedit full screen editor from the PROLOG environment. For example, the goal xedit(blocks). will edit the file blocks and then return to PROLOG. ced The clause editor ced is provided to add, inspect and delete clauses. It is a PROLOG program which makes use of the XEDIT editor. ced can be used with zero or one arguments. ced with no arguments adds new clauses to the database. ced with one argument can have an atom or an uninstantiated variable as its argument. If the argument is an uninstantiated variable, ced edits all clauses in the database. If the argument is an atom P, ced edits all clauses with predicate P. Variable name information may be preserved by setting the control declaration for var_names to on. var_names on - 31 - 9 Workspace Predicates Prolog has three types of clauses: compiled system clauses non-compiled system clauses user-defined clauses Compiled system clauses are those defined in the PROLOG source and the user-defined subroutines. The non-compiled system clauses are those defined in the INIT PROLOG file. Compiled and non-compiled system clauses are more commonly referred to as built-in predicates. They are added to the database on initialization and are hidden from the database manipulation predicates such as clause and retract. The user-defined clauses are all clauses defined after initialization. The workspace predicates act exclusively on user-defined clauses. When PROLOG is invoked, there are no user-defined clauses in the database. This is called the clear workspace. While within PROLOG, the current set of clauses is the active workspace. An active workspace can have a workspace identifier associated with it (WSID). It is specified by the wsid predicate and is initially set to 'clear'. A WSID can be any valid filename. An active workspace can be saved by using the save predicate. The workspace is saved in a file as specified by save or the WSID. The workspace predicates are modelled after the APL workspace commands. The following is a description of each. clear clear is a predicate with no arguments. It has the effect of clearing the active workspace of all clauses and setting it to the initial state. This includes the resetting of all operator and control declarations. wsid wsid may have zero, one or two arguments. When used with no arguments it causes the WSID to be displayed. When used with one argument, it sets the WSID to the filename specified by this argument. wsid - 32 - with two arguments unifies the current WSID with the second argument. If this succeeds, the WSID is set to the filename specified by the first argument. save save has zero or one arguments. When used with no arguments, the active workspace is saved in the file specified by WSID. save with one argument saves the active workspace in the file specified by the argument. save fails if an attempt is made to save the active workspace in the file 'clear'. load load has one argument, the filename of the workspace being loaded. The active workspace is first cleared and consequently, the specified workspace is loaded into the active workspace. load sets the WSID to the filename specified by its argument. copy copy has one argument, the filename of the workspace being copied into the active workspace. All clauses in the workspace are added to the active workspace. When an attempt is made to copy the clauses for a given predicate and arity, a check is made to see if any clauses exist with the same predicate and arity in the active workspace. If any such clauses exist, they are deleted before the new clauses are copied in. This mimics the actions of reconsult. pcopy pcopy is a protected version of copy. Clauses in the active workspace are never deleted. When the situation arises which causes copy to perform a deletion, pcopy leaves the active workspace clauses intact and adds the clauses. This mimics the actions of consult. bootws The bootws predicate works in conjunction with the load predicate. When a workspace is loaded into PROLOG, - 33 - the user-definable bootws is automatically executed. If bootws is not defined in the workspace, no action is taken (I.E. the no_clause predicate is not executed) no_clause and hence, bootws is optional. - 34 - 10 User Defined Subroutines The facility to call user defined subroutines is provided in PROLOG. The USERDEF ASSEMBLE file must be used to describe all user subroutines. Each subroutine is described using the $UDEF macro. The $UDEF macro is located in UDEF MACLIB and hence, GLOBAL UDEF is needed to access it. The resulting USERDEF TEXT file and the user defined subroutine TEXT files are linked with the PROLOG TEXT file to create the PROLOG MODULE. All user subroutines must adhere to the IBM standard linkage conventions. The address of the parameter list is stored in register 1. If no parameters are defined, it will equal 0. Each entry in the parameter list is an address to the desired parameter. $UDEF The USERDEF ASSEMBLE file has the format USERDEF CSECT $UDEF factorial,FACT,PARMS=(II,OI) $UDEF paste,PASTE,PARMS=(ISV20,OSV20) . . . DC H'0' * FLAGS END OF $UDEFs END where each $UDEF entry describes a user defined subroutine. The first argument to $UDEF is the subroutine's name within PROLOG. Any valid atom may be used. The second argument is the control section name of the user subroutine. For example, in FORTRAN VS, it would be the subroutine name and in IBM Assembler, the CSECT name. The third argument defines the parameter list passed to the user subroutine. Parameter List The parameter list is a list of parameter addresses passed between PROLOG and the user subroutine. The parameters may include input and output integers and strings, a work area, a return code, a global communications area and a local communications - 35 - area. The parameter list is described by the PARMS key in $UDEF. An input integer or string requires a PROLOG integer or atom to be passed to the user subroutine. Whereas, an output integer or string is unified with the corresponding PROLOG term. The remaining parameter types allocate work or communications area but do not require corresponding PROLOG terms. The following is a list of the parameter types: R The R parameter specifies the user subroutine's return code. It is initialized to zero. The valid return codes are: 0 - succeed 4 - succeed but backtracking point 8 - fail If no return code is requested in the PARMS key of $UDEF, the user subroutine always succeeds. G The G parameter specifies a 256 byte global communications area. This area is initialized to binary zeros when PROLOG is invoked. All user subroutines requesting a global communications area receive the same area and is used for communications purposes between subroutines. Lnnn The Lnnn parameter specifies an nnn byte local communications area. If nnn is omitted, the size of the area defaults to 4 bytes. The local area is used by user subroutines that are backtracking points. For instance, if the user subroutine returns a code of 4 and the proof backtracks to the user subroutine, the contents of the local area may be used to resatisfy the user subroutine. A local area is unnecessary if no backtracking is to be done by the user subroutine. The local area is initialized to zeros on the invocation of the user subroutine. - 36 - II The II parameter specifies a 4 byte input integer. The corresponding PROLOG integer is passed to the user subroutine. OI The OI parameter specifies a 4 byte output integer. A 4 byte area is supplied on invocation of the user subroutine and is initialized to zero. The user subroutine may manipulate this area as desired and on return to PROLOG, the location is unified with the corresponding PROLOG term. IR The IR parameter specifies an 8 byte input real number. The corresponding PROLOG real number is passed to the user subroutine. OR The OR parameter specifies an 8 byte output real number. An 8 byte area is supplied on invocation of the user subroutine and is initialized to zero. The user subroutine may manipulate this area as desired. The location is unified with the corresponding PROLOG term on return to PROLOG. ISnnn The ISnnn parameter specifies an nnn character input string. The corresponding PROLOG atom is passed (padded with blanks if necessary) to the user subroutine. OSnnn The OSnnn parameter specifies an nnn character output string. An nnn byte area is supplied on invocation of the user subroutine and is initialized to blanks. The user subroutine may manipulate this area as desired. On return to PROLOG, an atom is created from the characters in the area with trailing blanks removed. This atom is unified with the corresponding - 37 - PROLOG term. ISVnnn The ISVnnn parameter specifies a variable length input string with maximum length nnn. The input string is preceded by a 2 byte string length. The address in the parameter list points to the string length. The corresponding PROLOG atom and its length are copied in the supplied area on invocation. Atoms less than nnn in length are indicated by the 2 byte length. However, atoms longer than nnn in length are truncated to length nnn and the 2 byte string length becomes nnn. OSVnnn The OSVnnn parameter specifies a variable length output string with maximum length nnn. An nnn+2 byte area is supplied on invocation of the user subroutine with the first 2 bytes equal to 0 (the string length) and the remaining area initialized to blanks. The user subroutine may manipulate this area as desired. On return to PROLOG, an atom is created from the characters in the area using the first 2 bytes as the atom length. This atom is unified with the corresponding PROLOG term. Example In order to allow PROLOG to use the following subroutine: - 38 - SUBROUTINE FACT(N,FACTN,R) C C SUBROUTINE TO CALCULATE FACTORIALS C INTEGER N,FACTN,R IF(N.LT.0) GOTO 20 FACTN=1 IF(N.EQ.0) RETURN DO 10 I=1,N FACTN=FACTN*I 10 CONTINUE RETURN 20 CONTINUE R=8 RETURN END the following steps are necessary. 1. Add the following $UDEF to the USERDEF ASSEMBLE file $UDEF factorial,FACT,PARMS=(II,OI,R) 2. GLOBAL MACLIB UDEF 3. ASSEMBLE USERDEF 4. LOAD PROLOG USERDEF FACT (CLEAR ORIGIN 20000 5. GENMOD PROLOG The factorial subroutine can now be used within PROLOG. For example, factorial(3,X). ( unifies 6 with X ) factorial(0,X). ( unifies 1 with X ) factorial(abc,X). ( fails ) factorial(Y,X). ( fails ) factorial('-3',X). ( fails ) - 39 - 11 Execution Stack Description When invoking PROLOG, a large contiguous area is needed to store clauses and save execution information. This area is partitioned into three pieces: heap - store clauses, debugging information, file control blocks and operator declarations control stack - save success and failure information during the proof of a goal global stack - save input terms, variable instantiation lists and trail entries during the proof of a goal The control stack is located between the heap and the global stack. Relocation occurs if the heap or global stack grows into the control stack. The statistics predicate is provided to display information about the execution stack. PROLOG makes use of tail recursion optimization (TRO) to reduce the size of the control stack. This is a modified TRO which doesn't distinguish between local and global variables and hence, the local variable space savings is not applied. TRO affects the ancestors of a goal. Ancestors which are deterministic (non-backtracking) are eventually popped from the control stack. This may affect the ancestor sensitive predicates ancestor, retry and !(with arguments). If this presents a problem, TRO may be turned off by changing the control declaration for tro from on to off. - 40 - 12 Getting Started PROLOG is invoked from the VM/CMS environment by typing the command PROLOG. This command causes the PROLOG MODULE to be invoked. The PROLOG MODULE has one optional parameter with the following format: The valid flags are: B - invoke PROLOG for a 3278 terminal (use '{' and '}' instead of '[' and ']') C - invoke PROLOG for a 3277 terminal (use '<<' and '>>' instead of '[' and ']') F - invoke PROLOG with VSCOM#. VSCOM# establishes FORTRAN linkages for interrupt exceptions including FORTRAN IO U - invoke PROLOG in uppercase only mode The B and C flags are necessary because some common terminals such as the IBM 3277 and 3278 have a limited character set. The characters '\', '[' and ']' are not provided on the IBM 3277 keyboard. In their place, PROLOG allows the characters '^', '<<' and '>>' respectively. The 'C' flag must be set in order to use '<<' and '>>' whereas, the use of '^' instead of '\' is defined in INIT PROLOG. The IBM 3278 keyboard is without the '[' and ']' characters. The characters '{' and '}' are allowed in their place. The 'B' flag must be set for 3278 mode. The controls the size of the data area acquired for execution. This area is used to contain all clauses as well as the execution stack. The size of the area is specified as the number of 1024 byte blocks of memory used. If no size is specified, the default is 100. When the PROLOG MODULE is invoked, the INIT PROLOG file is consulted. INIT PROLOG contains several clauses which provide a useful initial database. INIT PROLOG may, however, be modified as desired. Also, if uppercase-only mode is invoked, INITU PROLOG is - 41 - consulted. All clauses in INIT PROLOG become builtin predicates and are treated like the compiled builtin predicates. That is, they are protected from the assert and retract predicates and cannot be displayed using clause or listing. A special user-defineable clause with predicate boot is available. If must be defined in INIT PROLOG and is executed once the initialization is completed. If boot is not defined, no action is taken (I.E. the no_clause predicate is not executed) and hence, boot is no_clause boot optional. On some systems, the double quote character (") defines the logical escape character. This poses a problem when using the "" format for strings. To correct this problem, the logical escape character can be turned off using the cp command: cp terminal escape off - 42 - 13 Sample Terminal Session prolog Welcome To Waterloo Prolog 1.7 Copyright 1985 Intralogic Inc Core Syntax [user]. /* consult terminal */ a:-write(hello),nl. eof. [user]:- a. hello a:- tell(test). tell(test):- write(a(1)),write('.'),nl. write(a(1)),write('.'),nl:- write(a(2)),write('.'),nl. write(a(2)),write('.'),nl:- told. told:- [test]. /* consult test */ [test]:- listing(a). a :- write(hello), nl. - 43 - a(1). a(2). listing(a):- retract(a:-Body). retract(a :- write(hello),nl):- retract(a(X)). retract(a(1)):- a(X). a(2):- [-test]. /* reconsult test */ [- test]:- listing(a). a(1). a(2). listing(a):- see(test). see(test):- read(X). read(a(1)):- read(X). read(a(2)):- read(X). read(eof):- - 44 - seen. seen:- seeing(X). seeing(user):- read(X). anything. read(anything):- put("a"),put("h"),nl. ah put([129]),put([136]),nl:- [user]. a:-b,c. a. b. c:-fail. eof. [user]:- spy [a,b,c]. spy [a,b,c]:- a. call a. call b. exit b. call c. fail c. redo b. fail b. exit a. a:- X is ok. INVALID ARITHMETIC EXPRESSION - 45 - _1 is ok. _1 is ok. goal. Error Recovery - type a goal or 'quit.' to exit ancestor(A),write(A),nl,fail. error_exec_. error_exec_. error_cmd_. error_cmd_. error. _1 is ok. _1 is ok. goal. ? Error Recovery - type a goal or 'quit.' to exit quit. ? readv(Term),writev(Term),nl. a(X,Y,X). a(X,Y,X). readv([a(_1,_2,_1),[X,Y]]),writev([a(_1,_2,_1),[X,Y]]),nl:- readv([a(_1,_2,_1),[X,Y]]),writev([a(_1,_2,_1),[X,Y]]),nl:- stop. /* exit from PROLOG */ - 46 -