UNSW PROLOG USER MANUAL This document describes a Prolog interpreter for VAX-11 and PDP-11 computers running under the Unix operating system. The original version was written by L.M. Pereira, F.C.N. Pereira and D.H.D. Warren for DECsystem-10 Prolog. It has been adapted for UNSW Prolog by Claude Sam- mut. The interpreter was written in C by Claude Sammut while he was at the Department of Computer Science, University of New South Wales. He was assisted by Shaun Arundell (UNSW) and later modifications were sug- gested by Alan Feuer, Bell Labs, Murray Hill. Wherever possible we have tried to make this system compatible with Prolog-10, however, there are significant differences and it should not be taken for granted that pro- grams can be ported without change. This section provides an introduction to the syntax and semantics of a certain subset of logic (`definite clauses', also known as `Horn clauses'), and indicates how this subset forms the basis of Prolog. The data objects of the language are called `terms'. A term is either a `constant', a `variable' or a `compound term'. The constants include `integers' such as: 0 1 999 -512 In UNSW Prolog, integers are restricted to the range -2^31 to 2^31-1 on the VAX and -2^15 to 2^15-1 on the PDP-11. Constants also include atoms such as: a void = := `Algol-68' [] The symbol for an atom can be any sequence of characters, which should be written in quotes if there is possibility of confusion with other symbols (such as variables, integers). As in conventional pro- gramming languages, constants are definite elementary objects, and correspond to proper nouns in natural language. Variables are distinguished by an initial capital letter or by the initial character "_", eg. X Value A A1 _3 _RESULT If a variable is only referred to once, it does not need to be named and may be written as an `anonymous' variable indicated by a single underline character: _ A variable should be thought of as standing for some definite but unidentified object. This is analogous to the use of a pronoun in natural language. Note that a variable is not simply a writeable storage location as in most programming languages; rather it is a local name for some data object. - 1 - The structured data objects of the language are the compound terms. A compound term comprises a `functor' (called the `principal' functor of the term) and a sequence of one or more terms called `arguments'. A functor is characterised by its `name', which is an atom, and its `arity' or number of arguments. For example the compound term whose functor is named `point' of arity 3, with arguments X, Y and Z, is written: point(X,Y,Z) Note that an atom is considered to be a functor of arity 0. Functors are generally analogous to common nouns in natural language. One may think of a functor as a record type and the arguments of a compound term as the fields of a record. Compound terms are usefully pictured as trees. For example, the term: s(np(john),vp(v(likes),np(mary))) would be pictured as the structure: s / \ np vp | / \ john v np | | likes mary Sometimes it is convenient to write certain functors as `operators'. 2-ary functors may be declared as `infix' operators and 1-ary functors as `prefix' or `postfix' operators. Thus it is possible to write, eg. X+Y (P;Q) X)! op(700, xfx, [=, ==, /=, is, <, >, =<, >=])! op(700, fx, [load, unload, trace, untrace, ed, ef, em])! op(500, yfx, [+, -])! op(500, fx, [+, -])! op(400, yfx, [*, /])! op(300, xfx, mod)! Note that the arguments of a compound term written in standard syntax must be expressions of precedence BELOW 1000. Thus it is neces- sary to bracket the expression `P:-Q' in: assert((P:-Q)) Note carefully the following syntax restrictions, which serve to remove potential ambiguity associated with prefix operators. (1) In a term written in standard syntax, the principal functor and its following `(' must NOT be separated by any intervening spaces, newlines etc. Thus: point (X,Y,Z) is invalid syntax. (2) If the argument of a prefix operator starts with a `(', this `(' must be separated from the operator by at least one space or other non-printable character. Thus: :-(p | q) , r. is invalid syntax, and must be written as eg. :- (p | q) , r. (3) If a prefix operator is written without an argument, as an ordinary atom, the atom is treated as an expression of the same precedence as the prefix operator, and must therefore be bracketed where necessary. Thus the brackets are necessary in: X = (?-) A further source of ambiguity in Prolog is arises from the ability to define infix and prefix operators with the same name. In some Prolog systems, these operators would be considered identical. In UNSW Prolog, this is not the case. For example, in C, there is a prefix operator `++' and also a postfix operator `++' which have slighlty different functions. If we declare: op(300, fx, ++)! op(300, xf, ++)! - 9 - Then we are creating two completely distinct atoms (which are also dis- tinct from the atom `++' which is not an operator). If an operator is to be referred to outside of its normal context as a principal functor of a compound term then, the operator must be preceded by a type name. For example: X =.. [infix +, 1, 2]? X = 1 + 2 This uses the infix operator `+' to create a new compound term using the `univ' predicate. Similarly: X =.. [prefix ++, x]? X = ++ x X =.. [postfix ++, x]? X = x ++ To run Prolog under the Unix operating system type: $ prolog FILE1 FILE2 ... FILE1, FILE2, ... contain Prolog programs which are to be loaded before the user is asked to type a command. If a syntax error occurs in a file, the error message will include the name of the file and the number of the line in which it occurred. When the files are loaded the system responds with a prompt `:'. The user may then enter programs or type in commands. The -s option may also be specified in the command line. For exam- ple, $ prolog -s3000 file_name This indicates that the variable stack is to increased to 3000 units. The default size is 1000. This option should only be necessary when running large programs. A program is made up of a sequence of clauses, possibly interspersed with directives to the interpreter. The clauses of a procedure do not have to be immediately consecutive, but remember that their relative order may be important. The text of a program is normally created separately in a file (or files), using the text editor. To input a program from a file FILE inside Prolog, give the com- mand: load FILE! which will instruct the interpreter to read-in the program. If the file name contains characters such as `.', its name is `prog.pl' say, it is necessary to give the complete filename between quotes. - 10 - Any characters following the `%' character on any line are treated as part of a comment and are ignored. When a file is loaded, a predicate of the form file(FileName, ProcedureList) is asserted to be true. `FileName' is the name of the loaded file and `ProcedureList' is a list of the names of the procedures defined in the file. When the same file is loaded a second time, the definitions of the procedures in ProcedureList are first removed so that clauses in the procedures are not duplicated. Clauses may also be typed in directly at the terminal, (although this is only recommended if the clauses will not be needed permanently, and are few in number). Directives are either commands or questions. Both are ways of directing the system to execute some goal or goals. Suppose list membership has been defined by: member(X, [X, .._]). member(X, [_, ..L]) :- member(X, L). Note the use of anonymous variables written "_". The command: member(3,[1,2,3]), print(yes)! directs the system to check whether 3 belongs to the list [1, 2, 3], and to output `yes' if so. Execution of a command terminates when all the goals in the command have been successfully executed. Other alternative solutions are not sought; one may imagine an implicit `cut' at the end of the command. If no solution can be found, the system simply returns with a prompt. The syntax of a question is the same as a command, except that it is ended by `?' instead of `!'. If the specified goal(s) can be satis- fied, the final value of each distinct variable is displayed (except for anonymous variables). The outcome of some questions is shown below. member(X, [tom, dick, harry])? X = tom X = dick X = harry member(X, [a, b, f(Y, c)]), member(X, [f(b, Z), d])? Y = b X = f(b,c) Z = c member(X, [f(_), g])? X = f(_) X = g - 11 - It is also possible to ask a question which involves no variables, for example, member(3,[1,2,3])? If the goal is satisfied, as would be the case in this example, then the system responds ** yes If the goal can be satisfied in more than one way, this response is repeated. For example, the question member(1, [1,2,3,1,1])? would result in the response ** yes ** yes ** yes If the goal is not satisfied, the system responds with `** no'. Note: For compatibility with Prolog-10 the prefix operators `:-' and `?-' may also be used for commands and questions. It is possible to edit procedures and files within Prolog. To edit the definition of a procedure, use the command: ed ProcedureName! The procedure will be printed onto a temporary file and the standard text editor will automatically be called by Prolog. When the editor is exited, Prolog will replace the old definition of the procedure with the edited form. If the procedure had been loaded from a file, that file is `not' changed by ed. Files of procedures (also called `modules') may be edited using the command: em FileName! The standard text editor will be invoked and the file may be changed. On exit from the editor, the file will be reloaded. Any changed made to the file will be permanent. Any file, whether it contains Prolog code or not may be edited by using the command: ef FileName! As with `em', the text editor will be invoked automatically. However the file is `not' read by Prolog on exit from the editor. If your Unix system has more than one text editor, you can specify which one Prolog is to use by assigning the name of the editor to the shell environment variable EDITOR. This should be done before running Prolog. - 12 - Once a program has been read, the interpreter will have available all the information necessary for its execution. A program may be saved on disk for future execution. To save a program into a file FILE, perform the command: save FILE! The result is a text file which can be edited normally. Procedures are listed in an arbitrary order. The program can be restored later by executing, load FILE! Execution of a program is started by giving the interpreter a directive which contains a call to one of the program's procedures. Only when execution of one directive is complete does the interpreter become ready for another directive. However, one may interrupt the normal execution of a directive by typing This `interruption' has the effect of terminating the execution of the command. The system will then respond with a prompt. Execution may also be terminated if the program runs out of stack space. When this occurs, the user may save the current program and reexecute Prolog with more stack space by using the -s option in the shell command. Warning: make sure that the reason the program ran out of stack space was not due to an error in your program before increasing the size. The Prolog interpreter provides a tracing facility. Procedures may be individually traced enabling each call the procedure to be displayed on the terminal with the current values of its arguments. Tracing of procedure PROCEDURE is enabled by the command: trace PROCEDURE! A number of procedures may be traced by the command: trace [proc1, proc2, ..]! Tracing is disabled by the command: untrace procedure! and more than one procedure may be untraced by: untrace [proc1, proc2, ..]! At the beginning of a line output by the trace procedure, Prolog prints either of the following: C> E< F< R> - 13 - `C' indicates that a procedure is being called for the first time. A line beginning with `E' shows the interpreter exiting a clause after all the goals have been satisfied. `F' indicates an exit from a clause due to a failure in satisfying a goal. After a failure, Prolog will attempt to redo a procedure call if there are alternative clauses left in the procedure definition. This is shown by an `R'. An example of tracing output is shown below: f(A, b) :- g(A). g(A) :- A = a. g(c). trace [f, g]! f(c, B)? C|>f(c, b) C||>g(c) F||g(c) E||' in the shell command. When the terminal is waiting for input on a new line, the prompt `:' is displayed. [If the standard input file is not a terminal, no prompt is displayed] When the current output stream is closed, standard output becomes the current output stream. When the current input stream is closed, the program file becomes the new input stream. The program file may be the user's terminal or a file loaded by the `load' directive or a file in the shell command. - 14 - A file is referred to by its name, `written as an atom', eg. myfile '123' 'fred.data' '/lib/prolog.lib' All I/O errors normally cause a failure of the procedure and warn- ings are issued on the standard error file. End of file is signalled when the predicate `eof' is true. Any more input requests for a file whose end has been reached return the atom `end_of_file'. consult(F) Instructs the interpreter to read-in the program which is in file F. When a directive is read it is immediately executed. When a clause is read it is put after any clauses already read by the interpreter for that procedure. When a file has been consulted, a predicate file(F, PProc_List) is asserted. PProc_List is a list of the procedures defined in F. unload F Retracts all procedures defined in F (according to the contents of file(F,.PProc_list). Also retracts file(F,.PProc_list). load F same as consult, except unloads F first, i.e. prevents redefinition of procedures. It shouldn't matter if F had not been consulted pre- viously. ed PP A listing of procedure PP is output to a temporary file. The text editor is the called to allow the user to change the definition. When the editor is terminated, the file is read in causing the pro- cedure to be redefined if no syntax errors were found. If the interpreter discovered syntax errors, the user is asked if the pro- cedure is to be re-edited. If the response is `n', the old defini- tion is restored. Any other response causes the editor to be invoked again. ef F edit file F. The file is not read by Prolog. em F Equivalent to : unload F, ef F, consult(F). - 15 - file(F, PProc_list) Both F and PProc_list must be uninstantiated. F is successively bound to the names of consulted files. PProc_list is bound to the list of procedure names defined in the respective files. see(F) File F becomes the current input stream. seeing(F) F is unified with the name of the current input file. seen Closes current input stream. read_from_this_file Unless `see' has changed the current input stream, all requests for input use the standard input file. Thus, when a read command is executed from a file being loaded by Prolog, the input will nor- mally come from the standard input file, not the program file. `read_from_this_file' will cause the input to be taken from the program file instead of the standard input. tell(F) File F becomes the current output stream. telling(F) F is unified with the name of the current output file. told Closes the current output stream. close(F) File F, currently open for input or output, is closed. eof Becomes true when the end of the current input stream has been reached. read(X) The next term, delimited by a `fullstop' (ie. a `.' followed by or a space), is read from the current input stream and unified with X. The syntax of the term must accord with current operator declarations. If a call `read(X)' causes the end of the current input stream to be reached, X is unified with the term `end_of-file'. If the program requests input from the terminal, the user is prompted by the current prompt string. The default prompt is `> '. - 16 - ratom(X) The next lexical token on the current input stream is bound to X. eg, ratom(X)? > fred X = fred. ratom(X)? > 123 X = 123 % X is an integer ratom(X)? > <= X = <= ratom(X)? > , X = ',' prompt(Ol.PPrompt, Ne.PPrompt) The current prompt string is bound to Ol.PPrompt, and Ne.PPrompt becomes the new prompt string. The goal `prompt(X, X)' binds the current prompt to X without changing it. Note: only the `read' prompt is changed. The system prompt `: ' cannot be changed. ask(Question, Answer) The user is prompted by the string Question. The next token on the input stream is bound to Answer. print(X, ...) `print' will accept a variable number of arguments, printing each term on the same line. If an argument is a string (enclosed in `"') then the string is printed without quotes. This `print' may be used to output messages. A newline character is appended to the output. The special character sequences `\n' and `\t' are recog- nized as newline and tab respectively. prin(X, ...) Same as `print' except no newline is appended. write(X) The term X is written to the current output stream according to current operator declarations. nl A new line is started on the current output stream. - 17 - getc(X) Unifies X with the next character in the current input stream. skip(C) Skips to just after the next character C. putc(X) The character X is output the the current output steam. ascii(C, I) If C is a single character atom, I is instantiated to the ascii code representing C. If C is a variable and I is an integer, C is instantiated to the appropriate single character atom tab(N) N spaces are output to the current output stream. N may be an integer expression. rename(F,N) If file F is currently open, closes it and renames it to N. If N is `[]', deletes the file. Arithmetic is performed by built-in procedures which take as arguments `integer expression' and `evaluate' them. An integer expression is a term built from `evaluable functors', integers and variables. At the time of evaluation, each variable in an integer expression must be bound to an integer, or, for the interpreter ONLY, to an integer expression. Each evaluable functor stands for an arithmetic operation. The evaluable functors are as follows, where X and Y are integer expressions: X+Y integer addition X-Y integer subtraction X*Y integer multiplication X/Y integer division X^Y exponentiation (Y >_ 0). X mod Y X modulo Y -X unary minus - 18 - The arithmetic built-in procedures are as follows, where X and Y stand for arithmetic expressions, and Z for some term: Z is X Integer expression X is evaluated and the result is unified with Z. Fails if X is not an integer expression. X == Y The values of X and Y are equal. X /= Y The values of X and Y are not equal. X < Y The value of X is less than the value of Y. X > Y The value of X is greater than the value of Y. X <= Y The value of X is less than or equal to the value of Y. X >= Y The value of X is greater than or equal to the value of Y. The comparison operations described in the previous section also apply to X and Y if the are instantiated to atoms. (X, Y) X and Y. X | Y also X; Y X or Y. X = Y Defined as if by clause " Z = Z. ". length(L,N) L must be instantiated to a list of determinate length. This length is unified with N. findall(V, PP, L) Find all all terms, V, such that PP is true. Append V to the list, L. Findall uses backtracking to find all the solutions to PP. - 19 - ! See earlier description of cut. not(PP) If the goal PP has a solution, fail, otherwise succeed. It is defined as if by: not(PP) :- PP, !, fail. not(_). PP -> Q | R also PP -> Q; R Analogous to "if PP then Q else R" i.e. defined as if by: PP -> Q | R :- PP, !, Q. PP -> Q | R :- R. PP -> Q When occurring other than as one of the alternatives of a disjunc- tion, is equivalent to: PP -> Q | fail. repeat Generates an infinite sequence of bactracking choices. It behaves (but doesn't use store!) as if defined by the clauses: repeat. repeat :- repeat. fail Always fails. true Always succeeds. var(X) Tests whether X is currently instantiated to a variable. nonvar(X) Tests whether X is currently instantiated to a non-variable term. atom(X) Checks that X is currently instantiated to an atom (ie. a non-variable term of arity 0, other than an integer). - 20 - integer(X) Checks that X is currently instantiated to an integer. atomic(X) Checks that X is currently instantiated to an atom or integer. functor(T,F,N) The principal functor of term T has name F and arity N, where F is either an atom or, provided N is 0, an integer. Initially, either T must be instantiated to a non-variable, or F and N must be instantiated to, respectively, either an atom and a non-negative integer or an integer and 0. If these conditions are not satisfied, an error message is given. In the case where T is initially instantiated to a variable, the result of the call is to instantiate T to the most general term having the principal func- tor indicated. arg(I,T,X) Initially, I must be instantiated to a positive integer and T to a compound term. The result of the call is to unify X with the Ith argument of term T. (The arguments are numbered from 1 upwards.) If the initial conditions are not satisfied or I is out of range, the call merely fails. X =.. Y Y is a list whose head is the atom corresponding to the principal functor of X and whose tail is the argument list of that functor in X. eg.: product(0, N, N - 1) =.. [product, 0, N, N - 1] If X is instantiated to a variable, then Y must be instantiated to a list of determinate length whose head is atomic (ie. an atom or integer). name(X,L) If X is an atom or integer then L is a list of the characters of the name of X. eg.: name(product, [p, r, o, d, u, c t]) If X is instantiated to a variable, L must be instantiated to a list of characters. eg.: name(X, [f, r, e, d])? `name' is defined in terms of `concat' and `char' - 21 - concat(L, A) The members of list L are concatenated to form the atom A. For example, concat([abc, def], X)? X = abcdef concat([f, r, e, d, 12], X)? X = fred12 The members of L must be atoms or integers. If X is instantiated then its value is compared with the result of the concatenation. char(I, A, C) The Tth character of atom A is C. I and A must be instantiated. X If X is instantiated to a term which would be acceptable as body of a clause, the goal X is executed exactly as if that term appeared textually in place of the X. In particular, any cut (`!') occurring in X is interpreted as if it occurred in the body of the clause containing X. If X is not instantiated as described the clause fails. assert(C) The current instance of C is interpreted as a clause and is added to the current interpreted program (with new private variables replacing any uninstantiated variables). The position of the new clause within the procedure concerned is implementation-defined. C must be instantiated to a non-variable. asserta(C) Like `assert(C)', except that the new clause becomes the first clause for the procedure concerned. assertz(C) Like `assert(C)', except that the new clause becomes the last clause for the procedure concerned. clause(PP,Q) PP must be bound to a non-variable term, and the current interpreted program is searched for clauses whose head matches PP. The head and body of those clauses are unified with PP and Q respectively. If one of the clauses is a unit clause, Q will be unified with `true'. - 22 - retract(C) The first clause in the current interpreted program that matches C is erased. C must be initially instantiated to a non-variable, and becomes unified with the value of the erased clause. The space occupied by the erased clause will be recovered when instances of the clause are no longer in use. retractall(PP) All clauses in the current interpreted program whose head matches PP are `retract'ed. PP must be bound to a non-variable term. pp A Lists in the current output stream all the interpreted clauses for predicates with name A, where A is bound to an atom. listing Lists in the current output stream all the clauses in the current interpreted program. numbervars(X,N,M) Unifies each of the variables in term X with a special term, so that `write(X)' prints each of those variables as `_I', where the Is are consecutive integers from N to M-1. N must be instantiated to an integer. ancestors(L) Unifies L with a list of ancestor goals for the current clause. The list starts with the parent goal and ends with the most recent ancestor coming from a `call' in a compiled clause. subgoal_of(S) The goal `subgoal_of(S)' is equivalent to the sequence of goals:- ancestors(L), in(S,L) where the predicate `in' successively matches its first argument with each of the elements of its second argument. trace PP also trace [PPList] Enable tracing of single procedure PP or a list of procedures. For example, to trace all the procedures defined in file `fred', type: file(fred, L), trace L! untrace PP also untrace .PPlist] Disable tracing of a single procedure PP or of a list of pro- cedures, PPlist. - 23 - op(PPriority, Type, Name) Treat name Name as an operator of the stated Type and PPriority. Name may also be a list of names in which case all are to be treated as operators of the stated Type and PPriority. If Name is already an operator or list of operators, PPriority and Type are successively bound to the priority and type of Name. save F The system saves the current procedure definitions in the system into file F. statistics Display on the terminal statistics relating to stack usage sh The Prolog session is suspended and a new shell is forked. When the shell terminates (by typing ^d), Prolog resumes. system(ShellCommand) ShellCommand is an atom or string which is passed to the shell to executes as a normal Unix command. If UNSW Prolog was compiled with PRINC_VARS defined then the fol- lowing is allowed: f(1). g(X, Y) :- X(Y). g(f, X)? X = 1 The principal functor of a compound term may be a variable. Prolog will perform unification on the principal functor in the same way is it uni- fies the arguments of a term. Note that `X' must be bound before a goal such as `X(Y)' can be satisfied. - 24 -