The SB-Prolog System, Version 3.1 A User Manual edited by Saumya K. Debray from material by David Scott Warren Suzanne Dietrich SUNY at Stony Brook Fernando Pereira SRI International December 1989 Department of Computer Science University of Arizona Tucson, AZ 85721 Modified for the 386 MS-DOS version by Diomidis Spinellis Department of Computing Imperial College The SB-Prolog System, Version 3.1 A User Manual Abstract: SB-Prolog is a Prolog system for Unix and 386 MS-DOS- based systems. The core of the system is an emulator, written in C for portability, of a Prolog virtual machine that is an exten- sion of the Warren Abstract Machine. The remainder of the sys- tem, including the translator from Prolog to the virtual machine instructions, is written in Prolog. Parts of this manual, specifically the sections on Prolog syntax and descriptions of some of the builtins, are based on the C-Prolog User Manual by Fernando Pereira. ____________________ - Unix is a trademark of AT&T. MS-DOS is a trademark of Micro- soft Corporation. 1. Introduction SB-Prolog is a Prolog system based on an extension of the Warren Abstract Machine[1]. The WAM simulator is written in C to enhance portability. Prolog source programs can be compiled into byte code files, which contain encodings of WAM instructions and are interpreted by the simulator. Programs can also be inter- preted via consult. SB-Prolog offers several features that are not found on most Prolog systems currently available. These include: compilation to object files; dynamic loading of predicates; provision for generating executable code on the global stack, which can be later be reclaimed; an extension table facility that permits memoization of relations. Other features include full integra- tion between compiled and interpreted code, and a facility for the definition and expansion of macros that is fully compatible with the runtime system. The system incorporates tail recursion optimization, and performs clause indexing in both compiled and interpreted code. However, there is no garbage collector for the global stack. This may be incorporated into a later version. One of the few luxuries afforded to a person giving software away for free is the ability to take philosophical stances without hurting his wallet. Based on our faith in the ``declara- tive ideal'', viz. that pure programs with declarative readings are Good, we have attempted to encourage, where possible, a more declarative style of programming. To this end, we have deli- berately chosen to not reward programs containing cuts in some situations where more declarative code is possible (see Appendix 2, 3). We have also resisted the temptation to make assert less expensive. We hope this will help promote a better programming style. 2. Getting Started This section is intended to give a broad overview of the SB- Prolog system, so as to enable the new user to begin using the system with a minimum of delay. Many of the topics touched on here are covered in greater depth in later sections. 2.1. The Dynamic Loader Search Path ____________________ [1] D. H. D. Warren, ``An Abstract Prolog Instruction Set'', Tech. Note 309, SRI International, 1983. 2 In SB-Prolog, it is not necessary for the user to load all the predicates necessary to execute a program. Instead, if an unde- fined predicate foo is encountered during execution, the system searches the user's directories in the order specified by the environment variable SIMPATH until it finds a directory contain- ing a file foo whose name is that of the undefined predicate. It then dynamically loads and links the file foo (which is expected to be a byte code file defining the predicate foo), and continues with execution; if no such file can be found, an error message is given and execution fails. This feature makes it unnecessary for the user to have to explicitly link in all the predicates that might be necessary in a program: instead, only those files are loaded which are necessary to have the program execute. This can significantly reduce the memory requirements of programs. The key to this dynamic search-and-load behaviour is the SIMPATH environment variable, which specifies the order in which directories are to be searched. It may be set by adding the fol- lowing line to the user's .cshrc file: setenv SIMPATH path where path is a sequence of directory names separated by colons: dir<1>:dir<2>: ... :dir and dir are full path names to the respective directories. For example, executing the command setenv SIMPATH .:$HOME/prolog/modlib:$HOME/prolog/lib sets the search order for undefined predicates to the following: first, the directory in which the program is executing is searched; if the appropriate file is not found in this directory, the directories searched are, in order, ~/prolog/modlib and ~/prolog/lib. If the appropriate file is not found in any of these directories, the system gives an error message and execu- tion fails. On an MS-DOS system the equivalent operation is performed by setting the environment variable SIMPATH to the appropriate string using the SET command. This can be done in the AUTOEXEC.BAT file or in the batch file that is used for SB-Prolog startup. An example of the command is: set SIMPATH=/usr/sbp/lib:/usr/sbp/modlib:/usr/sbp/cmplib:. In this case SB-Prolog is installed in the /usr/sbp directory. 3 Notice that the directory separator is a colon and not a semi- colon as it is used for the PATH variable. Also notice that the current directory is explicitly mentioned. An example batch file is included in the distribution. The beginning user is advised to include the system direc- tories (listed in the next section) in his SIMPATH, in order to be able to access the system libraries (see below). 2.2. System Directories There are four basic system directories: cmplib, lib, modlib and sim -. cmplib contains the Prolog to byte code translator; lib and modlib contain library routines. The src subdirectory in each of these contains the corresponding Prolog source programs. The directory sim contains the simulator, the subdirectory buil- tin contains code for the builtin predicates of the system. It is recommended that the beginning user include the system directories in his SIMPATH, by setting SIMPATH to .:SBP/modlib:SBP/lib:SBP/cmplib where SBP denotes the path to the root of the SB-Prolog system directories. 2.3. Invoking the Simulator The simulator is invoked by the command sbprolog bc_file where bc_file is a byte code file resulting from the compilation of a Prolog program. In almost all cases, the user will wish to interact with the SB-Prolog query evaluator, in which case bc_file will be $readloop, and the command will be sbprolog Path/$readloop where Path is the path to the directory containing the command interpreter $readloop. This directory, typically, is modlib (see Section 2.2 above). The command interpreter reads in a query typed in by the user, evaluates it and prints the answer(s), repeating this until it encounters an end-of-file (the standard end-of-file character ____________________ The binary MS-DOS distribution does not include the sim and src directories. They are included in the full SB-Prolog distri- bution. 4 on the system, e.g. ctrl-D), or the user types in end_of_file or halt. The user should ensure that the the directory containing the executable file sim (typically, the system directory sim: see Section 2.2 above) is included in the shell variable path; if not, the full path to the simulator will have to be specified. In general, the simulator may be invoked with a variety of options, as follows: sbprolog -options bc_file or sbprolog -option<1> -option<2> ... -option bc_file The options recognized by the simulator are described in Section 4.2. When called with a byte code file bc_file, the simulator begins execution with the first clause in that file. The first clause in such a file, therefore, should be a clause without any arguments in the head (otherwise, the simulator will attempt to dereference argument pointers in the head that are really point- ing into deep space, and usually come to a sad end). If the user is executing a file in this manner rather than using the command interpreter, he should also be careful to include the undefined predicate handler, consisting of the predicates `_$interrupt/2 and `_$undefined_pred'/1, which is normally defined in the files modlib/src/$init_sys.P and modlib/src/$readloop. 2.4. Executing Programs There are two ways of executing a program: a source file may be compiled into a byte-code file, which can then be loaded and exe- cuted; or, the source file may be interpreted via consult. The system supports full integration of compiled and interpreted code, so that some predicates of a program may be compiled, while others may be interpreted. However, the unit of compilation or consulting remains the file. The remainder of this section describes each of these procedures in more detail. 2.4.1. Compiling Programs The compiler is invoked through the Prolog predicate compile. It translates Prolog source programs into byte code that can then be executed on the simulator. The compiler may be invoked as fol- lows: | ?- compile(InFile [, OutFile ] [, OptionsList ]). or | ?- compile(InFile, OutFile, OptionsList, PredList). 5 where optional parameters are enclosed in brackets. InFile is the name of the input (i.e. source) file; OutFile is the name of the output file (i.e. byte code) file; OptionsList is a list of compiler options, and PredList is a list of terms P/N denoting the predicates defined in InFile, where P is a predicate name and N its arity. The input and output file names must be Prolog atoms, i.e. either begin with a lower case letter and consist only of letters, digits, dollar signs and underscores; or, be enclosed within single quotes. If the output file name is not specified, it defaults to InFile.out. The list of options, if specified, is a Prolog list, i.e. a term of the form [ option<1>, option<2>, ..., option ]. If left unspecified, it defaults to the empty list []. PredList, if specified, is usually given as an uninstantiated variable; its principal use is for setting trace points on the predicates in the file (see Sections 6 and 8). Notice that PredList can only appear in compile/4. A list of compiler options appears in Section 8.3. 2.4.2. Loading Byte Code Files Byte code files may be loaded into the simulator using the predi- cate load: | ?- load(ByteCode_File). where ByteCode_File is a Prolog atom (see Section 3.1) that is the name of a byte code file. The load predicate invokes the dynamic loader, which carries out a search according to the sequence specified by the environ- ment variable SIMPATH (see Section 2.1). It is therefore not necessary to always specify the full path name to the file to be loaded. Byte code files may be concatenated together to produce other byte code files. Thus, for example, if foo1 and foo2 are byte code files resulting from the compilation of two Prolog source programs, then the file foo, obtained by executing the shell command cat foo1 foo2 > foo is a byte code file as well, and may be loaded and executed. In 6 this case, loading and executing the file foo would give the same result as loading foo1 and foo2 separately, which in turn would be the same as concatenating the original source files and com- piling this larger file. This makes it easier to compile large programs: one need only break them into smaller pieces, compile the individual pieces, and concatenate the resulting byte code files together. 2.4.3. Consulting Programs Instead of compiling a file to generate a byte code file which then has to be loaded, a program may be executed interpretively by ``consulting'' the corresponding source file: | ?- consult(SourceFile [, OptionList ] ). or | ?- consult(SourceFile, OptionList, PredList). where SourceFile is a Prolog atom which is the name of a file containing a Prolog source program; OptionList is a list of options to consult; and PredList is a list of terms P/N, where P is a predicate name and N its arity, specifying which predicates have been consulted from SourceFile; its principal use is for setting trace points on the predicates in the file (see Section 6). Notice that PredList can only appear in consult/3. At this point, the options recognized for consult are the following: t ``trace''. Causes a trace point to be set on any predicate in the current file that does not already have a trace point set. v ``verbose''. Causes information regarding which predicates have been consulted to be printed out. Default: off. In addition to the above, options for the macro expander are also recognized (see Section 10)). consult will create an index on the printipal functor of the first argument of the predicates being consulted, unless this is changed using the index/3 directive. In particular, note that if no index is desired on a predicate foo/n, then the directive :- index(foo, n, 0). should be given. It is important to note that SB-Prolog's consult predicate is similar to that of Quintus Prolog, and behaves like C-Prolog's reconsult. This means that if a predicate is defined across two or more files, consulting them will result in only the clauses in the file consulted last being used. 7 2.5. Execution Directives Execution directives may be specified to compile and consult through :-/1. If, in the read phase of compile or consult, a term with principal functor :-/1 is read in, this term is exe- cuted directly via call/1. This enables the user to dynamically modify the environment, e.g. via op declarations (see Section 3.2), asserts etc. A point to note is that if the environment is modified as a result of an execution directive, the modifications are visible only in that environment. This means that consulted code, which runs in the environment in which the source program is read in (and which is modified by such execution directives) feel the effects of such execution directives. However, byte code result- ing from compilation, which, in general, executes in an environ- ment different from that in which the source was compiled, does not inherit the effects of such directives. Thus, an op declara- tion can be used in a source file to change the syntax and allow the remainder of the program to be parsed according to the modi- fied syntax; however, these modifications will not, in general, manifest themselves if the byte code is executed in another environment. Of course, if the byte code is loaded into the same environment as that in which the source program was compiled, e.g. through | ?- compile(foo, bar), load(bar). the effects of execution directives will continue to be felt. 3. Syntax 3.1. Terms The syntax of SB-Prolog is by and large compatible with that of C-Prolog. The data objects of the language are called terms. A term is either a constant, a variable or a compound term. Con- stants can be integers or atoms. The symbol for an atom must begin with a lower case letter or the dollar sign $, and consist of any number of letters, digits, underscores and dollar signs; if it contains any character other than these, it must be enclosed within single quotes.[2] As in other programming languages, constants are definite elementary objects. ____________________ [2] Users are advised against using symbols beginning with `$' or `_$', however, in order to minimize the possibility of con- flicts with symbols internal to the system. 8 Variables are distinguished by an initial capital letter or by the initial character "_", for example X Value A A1 _3 _RESULT _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 the underline character "_". A variable should be thought of as standing for some definite but unidentified object. A variable is not simply a writeable storage location as in most programming languages; rather it is a local name for some data object, cf. the variable of pure LISP and constant declarations in Pascal. 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 com- pound term whose functor is named `point' of arity 3, with argu- ments X, Y and Z, is written point(X,Y,Z) An atom is considered to be a functor of arity 0. A functor or predicate symbol is uniquely identified by its name and arity (in other words, it is possible for different sym- bols having different arities to share the same name). A functor or predicate symbol p with arity n is usually written p/n. 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 9 X+Y (P;Q) X ]). :- op( 1200, fx, [ :- ]). :- op( 1198, xfx, [ ::- ]). :- op( 1150, fy, [ mode, public, dynamic ]). :- op( 1100, xfy, [ ; ]). :- op( 1050, xfy, [ -> ]). :- op( 1000, xfy, [ ',' ]). /* See note below */ :- op( 900, fy, [ not, \+, spy, nospy ]). :- op( 700, xfx, [ =, is, =.., ==, \==, @<, @>, @=<, @>=, 12 =:=, =\=, <, >, =<, >=, ?=, \= ]). :- op( 661, xfy, [ `.' ]). :- op( 500, yfx, [ +, -, /\, \/ ]). :- op( 500, fx, [ +, - ]). :- op( 400, yfx, [ *, /, //, <<, >> ]). :- op( 300, xfx, [ mod ]). :- op( 200, xfy, [ ^ ]). Operator declarations are most usefully placed in directives at the top of your program files. In this case the directive should be a command as shown above. Another common method of organisation is to have one file just containing commands to declare all the necessary operators. This file is then always consulted first. Note that a comma written literally as a punctuation character can be used as though it were an infix operator of pre- cedence 1000 and type `xfy': X,Y ','(X,Y) represent the same compound term. But note that a comma written as a quoted atom is not a standard operator. Note also that the arguments of a compound term writ- ten in standard syntax must be expressions of precedence below 1000. Thus it is necessary to parenthesize the expression "P :- Q" in assert((P :- Q)) The following syntax restrictions serve to remove potential ambiguity associated with prefix operators. o In a term written in standard syntax, the principal func- tor and its following "(" must not be separated by any whitespace. Thus point (X,Y,Z) is invalid syntax (unless point were declared as a prefix operator). o 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. (where `:-' is the prefix operator) is invalid syntax, and must be written as 13 :- (p;q),r. o 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 = (?-) 4. SB-Prolog: Operational Semantics 4.1. Standard Execution Behaviour The normal execution behaviour of SB-Prolog follows the usual left to right order of literals within a clause, and the textual top to bottom order of clauses for a predicate. This corresponds to a depth first search of the leftmost SLD-tree for the program and the given query. Unification without occurs check is used, and execution backtracks to the most recent choice point when unification fails. 4.2. Cuts and If-Then-Else This standard execution behaviour of SB-Prolog can be changed using constructs like cut (`!') and if-then-else (`->'). In SB- Prolog, cuts are usually treated as hard, i.e. discard choice points of all the literals to the left of the cut in the clause containing the cut being executed, and also the choice point for the parent predicate, i.e. any remaining clauses for the predi- cate containing the cut being executed. There are some situa- tions, however, where the scope of a cut is restricted to be smaller than this. Restrictions apply under the following condi- tions: (1) The cut occurs in a term which has been constructed at run- time and called through call/1, e.g. in ..., X = (p(Y), !, q(Y)), ..., call(X), ... In this case, the scope of the cut is restricted to be within the call, unless one of the following cases also apply and serve to restrict its scope further. (2) The cut occurs in a negated goal, or within the scope of the test of an if-then-else (in an if-then-else of the form ``Test -> TruePart ; FalsePart'', the test is the goal Test). In these cases, the scope of the cut is restricted to be within the negation or the test of the if-then-else, 14 respectively. In cases involving nested occurrences of these situations, the scope of the cut is restricted to that for the deepest such nested construct, i.e. most restricted. For example, in the con- struct ..., not( (p(X) -> not( (q(X), (r(X) -> s(X) ; (t(X), !, u(X)))) )) ), ... the scope of the cut is restricted to the inner negation, and does not affect any choice point that may have been set up for p(X). 4.3. Unification of Floating Point Numbers As far as unification is concerned, no type distinction is made between integers and floating point numbers, and no explicit type conversion is necessary when unifying an integer with a float. However, due to the finite precision representation of floating point numbers and cumulative round-off errors in floating point arithmetic, comparisons involving floating point numbers may not always give the expected results. For the same reason, users are warned that indexing on predicate arguments (see Section 15) may not give the expected results if floating point numbers are involved. 5. Evaluable Predicates This section describes (most of) the evaluable predicates pro- vided by SB-Prolog. These can be divided into three classes: inline predicates, builtin predicates and library predicates. Inline predicates represent ``primitive'' operations in the WAM. Calls to inline predicates are compiled into a sequence of WAM instructions in-line, i.e. without actually making a call to the predicate. Thus, for example, relational predicates (>/2, >=/2, etc.) compile to, essentially, a subtraction and a condi- tional branch. Inline predicates cannot be redefined by the user. Table 1 lists the SB-Prolog inline predicates. Unlike inline predicates, builtin predicates are implemented by C functions in the simulator, and accessed via the inline predicate `_$builtin'/1. Thus, if a builtin predicate foo/3 was defined as builtin number 38, there would be a definition in the system of the form foo(X,Y,Z) :- '_$builtin'(38). 15 arg/3 =/2 =/2 >/2 /\/2 `\/'/2 <>/2 =:=/2 =\=/2 is/2 ?=/2 \= \/1 `_$builtin'/1 `_$call'/1 nonvar/1 var/1 integer/1 real/1 halt/0 true/0 fail/0 Table 1: Inline Predicates of SB-Prolog In effect, a builtin is simply a segment of code in a large case (i.e. switch) statement. Each builtin is identified inter- nally by an integer, referred to as its ``builtin number'', asso- ciated with it. The code for a builtin with builtin number k corresponds to the k[th.] case in the switch statement. SB- Prolog limits the total number of builtins to 256. Builtins, unlike inline predicates, can be redefined by the user. For example, the predicate foo/3 above can be redefined simply by compiling the new definition into a directory such that during dynamic loading, the new definition would be encountered first and loaded. A list of the builtins currently provided is listed in Appendix 1. Section 7.4 describes the procedure to be followed in order to define new builtin predicates. Like builtin predicates, library predicates may also be redefined by the user. The essential difference between builtin and library predicates is that whereas the former are coded into the simulator in C, the latter are written in Prolog. 5.1. Input and Output Input and output are done with respect to the current input and output streams. These can be set, reset or checked using the file handling predicates described below. The default input and output streams are denoted by user, and refer to the user's ter- minal. 5.1.1. File Handling see(F) F becomes the current input stream. F must be instantiated to an atom at the time of the call. 16 seeing(F) F is unified with the name of the current input file. seen Closes the current input stream. tell(F) F becomes the current output stream. F must be instantiated to an atom at the time of the call. telling(F) F is unified with the name of the current output file. told Closes the current output stream. $exists(F) Succeeds if file F exists. 5.1.2. Term I/O read(X) The next term, delimited by a full stop (i.e. a "." fol- lowed by a carriage-return 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 atom `end_of_file'. Further calls to read for the same stream will then cause an error failure. write(X) The term X is written to the current output stream accord- ing to operator declarations in force. display(X) The term X is displayed on the terminal. writeq(Term) Similar to write(Term), but the names of atoms and functors are quoted where necessary to make the result acceptable as input to read. print(Term) Prints out the term Term onto the current output stream. This predicate provides a handle for user-defined pretty- printing. If Term is a variable then it is written using write/1; otherwise,, if a user-defined predicate portray/1 is defined, then a call is made to portray(Term); otherwise, print/1 is equivalent to write/1. writename(Term) If Term is an uninstantiated variable, its name, which looks 17 a lot like an address in memory, is written out; otherwise, the principal functor of Term is written out. writeqname(Term) As for writename, but the names are quoted where necessary. print_al(N, A) Prints A (which must be an atom or a number) left-aligned in a field of width N, with blanks padded to the right. If A's print name is longer than the field width N, then A is printed but with no right padding. print_ar(N, A) Prints A (which must be an atom or a number) right-aligned in a field of width N, with blanks padded to the left. If A's print name is longer than the field width N, then A is printed but with no left padding. portray_term(Term) Writes out the term Term on the current output stream. Variables are treated specially: an uninstantiated variable is printed out as Vn, where n is a number. portray_clause(Term) Writes out the term Term, interpreted as a clause, on the current output stream. Variables are treated as in portray_term/1. 5.1.3. Character I/O nl A new line is started on the current output stream. get0(N) N is the ASCII code of the next character from the current input stream. If the current input stream reaches its end of file, a -1 is returned (however, unlike in C-Prolog, the input stream is not closed on encountering end-of-file). get(N) N is the ASCII code of the next non-blank printable character from the current input stream. It has the same behaviour as get0 on end of file. put(N) ASCII character code N is output to the current output stream. N must be an integer. 18 tab(N) N spaces are output to the current output stream. N must be an integer. 5.2. Arithmetic Arithmetic is performed by evaluable predicates which take as arguments arithmetic expressions and evaluate them. An arithmetic expression is a term built from evaluable functors, numbers and variables. At the time of evaluation, each vari- able in an arithmetic expression must be bound to a number or to an arithmetic expression. Each evaluable functor stands for an arithmetic operation. The evaluable functors are as follows, where X and Y are arithmetic expressions. X + Y addition. X - Y subtraction. X * Y multiplication. X / Y division. X // Y integer division. X mod Y X (integer) modulo Y. -X unary minus. X /\ Y integer bitwise conjunction. X \/ Y integer bitwise disjunction. 19 X << Y integer bitwise left shift of X by Y places. X >> Y integer bitwise right shift of X by Y places. \X integer bitwise negation. integer(X) If X is bound to a floating point number, this evaluates to the smallest integer not greater than X. (Syntactic sugar for floor/2 below.) float(X) If X is bound to an integer, evaluates to the smallest float not greater than X. (Syntactic sugar for floor/2 below.) exp(X) If X is instantiated to a number, evaluates to e[X], where e = 2.71828 ... (Syntactic sugar for exp/2 below.) ln(X) If X is instantiated to a positive number, this evaluates to the natural logarithm of X. (Syntactic sugar for exp/2 below.) sin(X) If X is instantiated to a number (representing an angle in radians), evaluates to sin(X). (Syntactic sugar for sin/2 below.) arcsin(X) If X is instantiated to a number between -J/2 and J/2, evaluates to sin[-1](X). (Syntactic sugar for sin/2 below.) As far as unification is concerned, no type distinction is made between integers and floating point numbers, and no explicit type conversion is necessary when unifying an integer with a float. However, due to the finite precision representation of floating point numbers and cumulative round-off errors in float- ing point arithmetic, comparisons involving floating point numbers may not always give the expected results. 20 The arithmetic evaluable predicates are as follows, where X and Y stand for arithmetic expressions, and Z for some term. Note that this means that is only evaluates one of its arguments as an arithmetic expression (the right-hand side one), whereas all the comparison predicates evaluate both their arguments. Z is X Arithmetic expression X is evaluated and the result, is uni- fied with Z. Fails if X is not an arithmetic expression. Unlike many other Prolog systems, variables in the expres- sion X may be bound to other arithmetic expressions as well as to numbers. eval(E, X) Evaluates the arithmetic expression E and unifies the result with the term X. Fails if E is not an arithmetic expres- sion. (Thus, eval/2 is, except for the switched argument order, the same as is/2. It's around mainly for historical reasons.) X =:= Y The values of X and Y are equal. If either X or Y involve compound subexpressions that are created at runtime, they should first be evaluated using eval/2. X =\= Y The values of X and Y are not equal. If either X or Y involve compound subexpressions that are created at runtime, they should first be evaluated using eval/2. X < Y The value of X is less than the value of Y. If either X or Y involve compound subexpressions that are created at run- time, they should first be evaluated using eval/2. X > Y The value of X is greater than the value of Y. If either X or Y involve compound subexpressions that are created at runtime, they should first be evaluated using eval/2. X =< Y The value of X is less than or equal to the value of Y. If either X or Y involve compound subexpressions that are created at runtime, they should first be evaluated using eval/2. X >= Y 21 The value of X is greater than or equal to the value of Y. If either X or Y involve compound subexpressions that are created at runtime, they should first be evaluated using eval/2. floor(X, Y) If X is a floating point number in the call and Y is free, then Y is instantiated to the largest integer whose absolute value is not greater than the absolute value of X; if X is uninstantiated in the call and Y is an integer, then X is instantiated to the smallest float not less than Y. floatc(F, M, E) If F is a number while M and E are uninstantiated in the call, then M is instantiated to a float m (of magnitude less than 1), and E to an integer n, such that F = m * 2[n]. If F is uninstantiated in the call while M is a float and E an integer, then F becomes instantiated to M * 2[E]. exp(X, Y) If X is instantiated to a number and Y is uninstantiated in the call, then Y is instantiated to e[X] (where e = 2.71828...); if X is uninstantiated in the call while Y is instantiated to a positive number, then X is instantiated to log(Y). square(X, Y) If X is instantiated to a number while Y is uninstantiated in the call, then Y becomes instantiated to X[2]; if X is uninstantiated in the call while Y is instantiated to a positive number, then X becomes instantiated to the positive square root of Y (if Y is negative in the call, X becomes instantiated to 0.0). sin(X, Y) If X is instantiated to a number (representing an angle in radians) and Y is uninstantiated in the call, then Y becomes instantiated to sin(X) (the user should check the magnitude of X to make sure that the result is meaningful). If Y is instantiated to a number between -J/2 and J/2 and X is unin- stantiated in the call, then X becomes instantiated to sin[-1](Y). 22 5.3. Convenience P , Q P and then Q. P ; Q P or Q. true Always succeeds. X = Y Defined as if by the clause " Z=Z ", i.e. X and Y are uni- fied. X \= Y Succeeds if X and Y are not unifiable, fails if X and Y are unifiable. It is thus equivalent to not(X = Y), but is sig- nificantly more efficient. X ?= Y Succeeds if X and Y are unifiable and fails if they are not, but does not instantiate any variables. Thus, it tests whether X and Y are unifiable. Equivalent to not(not(X = Y)), but is significantly more efficient. 5.4. Extra Control ! Cut (discard) all choice points made since the parent goal started execution. (The scope of cut in different contexts is discussed in Section 4.2). not P If the goal P has a solution, fail, otherwise succeed. It is defined as if by not(P) :- P, !, fail. not(_). P -> Q ; R Analogous to "if P then Q else R" i.e. defined as if by 23 P -> Q ; R :- P, !, Q. P -> Q ; R :- R. P -> Q When occurring other than as one of the alternatives of a disjunction, is equivalent to P -> Q ; fail. repeat Generates an infinite sequence of backtracking choices. It is defined by the clauses: repeat. repeat :- repeat. fail Always fails. 5.5. Meta-Logical 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 (i.e. a non-variable term of arity 0, other than a number). integer(X) Checks that X is currently instantiated to an integer. real(X) Checks that X is currently instantiated to a floating point number. float(X) Same as real/1, checks that X is currently instantiated to a floating point number. number(X) Checks that X is currently instantiated to a number, i.e. that it is either an integer or a real. 24 atomic(X) Checks that X is currently instantiated to an atom or number. structure(X) Checks that X is currently instantiated to a compound term, i.e. to a nonvariable term that is not atomic. is_buffer(X) Succeeds if X is instantiated to a buffer. 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, a number. 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 mes- sage is given. In the case where T is initially instan- tiated to a variable, the result of the call is to instantiate T to the most general term having the princi- pal functor 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. E.g. product(0,N,N-1) =.. [product,0,N,N-1] N-1 =.. [-,N,1] product =.. [product] If X is instantiated to a variable, then Y must be instan- tiated either to a list of determinate length whose head is an atom, or to a list of length 1 whose head is a number. name(X,L) If X is an atom or a number then L is a list of the ASCII codes of the characters comprising the name of X. E.g. name(product,[112,114,111,100,117,99,116]) 25 i.e. name(product,"product"). If X is instantiated to a variable, L must be instantiated to a list of ASCII character codes. E.g. | ?- name(X,[104,101,108,108,111])). X = hello | ?- name(X,"hello"). X = hello call(X) If X is a nonvariable term in the program text, then it is executed exactly as if X appeared in the program text instead of call(X), e.g. ..., p(a), call( (q(X), r(Y)) ), s(X), ... is equivalent to ..., p(a), q(X), r(Y), s(X), ... However, if X is a variable in the program text, then if at runtime X is instantiated to a term which would be accept- able as the body of a clause, the goal call(X) is executed as if that term appeared textually in place of the call(X), except that any cut (`!') occurring in X will remove only those choice points in X. If X is not instantiated as described above, an error message is printed and call fails. X (where X is a variable) Exactly the same as call(X). How- ever, we prefer the explicit usage of call/1 as good pro- gramming practice, and the use of a top level variable subgoal elicits a warning from the compiler. conlength(C, L) Succeeds if the length of the print name of the constant C (which can be an atom, buffer or integer), in bytes, is L. If C is a buffer (see Section 5.8), it is the length of the buffer; if C is an integer, it is the length of the decimal representation of that integer, i.e., the number of bytes that a $writename will use. 5.6. Sets When there are many solutions to a problem, and when all those solutions are required to be collected together, this can be achieved by repeatedly backtracking and gradually building up a list of the solutions. The following evaluable predicates are provided to automate this process. 26 setof(X, P, S) Read this as "S is the set of all instances of X such that P is provable''. If P is not provable, setof(X,P,S) succeeds with S instantiated to the empty list []. The term P specifies a goal or goals as in call(P). S is a set of terms represented as a list of those terms, without dupli- cates, in the standard order for terms (see Section 5.7). If there are uninstantiated variables in P which do not also appear in X, then a call to this evaluable predicate may backtrack, generating alternative values for S corresponding to different instantiations of the free vari- ables of P. Variables occurring in P will not be treated as free if they are explicitly bound within P by an existential quantifier. An existential quantification is written: Y^Q meaning "there exists a Y such that Q is true", where Y is some Prolog term (usually, a variable, or tuple or list of variables). bagof(X, P, Bag) This is the same as setof except that the list (or alterna- tive lists) returned will not be ordered, and may contain duplicates. If P is unsatisfiable, bagof succeeds binding Bag to the empty list. The effect of this relaxation is to save considerable time and space in execution. findall(X, P, L) Similar to bagof/3, except that variables in P that do not occur in X are treated as local, and alternative lists are not returned for different bindings of such variables. The list L is, in general, unordered, and may contain dupli- cates. If P is unsatisfiable, findall succeeds binding S to the empty list. X^P The system recognises this as meaning "there exists an X such that P is true", and treats it as equivalent to call(P). The use of this explicit existential quantifier outside the setof and bagof constructs is superfluous. 5.7. Comparison of Terms These evaluable predicates are meta-logical. They treat uninstantiated variables as objects with values which may be compared, and they never instantiate those variables. They should not be used when what you really want is arithmetic com- parison (Section 5.2) or unification. The predicates make 27 reference to a standard total ordering of terms, which is as fol- lows: o variables, in a standard order (roughly, oldest first - the order is not related to the names of variables); o numbers, from -"infinity" to +"infinity"; o atoms, in alphabetical (i.e. ASCII) order; o complex terms, ordered first by arity, then by the name of principal functor, then by the arguments (in left-to-right order). For example, here is a list of terms in the standard order: [ X, -9, 1, fie, foe, fum, X = Y, fie(0,2), fie(1,1) ] The basic predicates for comparison of arbitrary terms are: X == Y Tests if the terms currently instantiating X and Y are literally identical (in particular, variables in equivalent positions in the two terms must be identical). For example, the question | ?- X == Y. fails (answers "no") because X and Y are distinct unin- stantiated variables. However, the question | ?- X = Y, X == Y. succeeds because the first goal unifies the two variables (see page 42). X \== Y Tests if the terms currently instantiating X and Y are not literally identical. T1 @< T2 Term T1 is before term T2 in the standard order. T1 @> T2 Term T1 is after term T2 in the standard order. T1 @=< T2 Term T1 is not after term T2 in the standard order. 28 T1 @>= T2 Term T1 is not before term T2 in the standard order. Some further predicates involving comparison of terms are: compare(Op, T1, T2) The result of comparing terms T1 and T2 is Op, where the possible values for Op are: `=' if T1 is identical to T2, `<' if T1 is before T2 in the standard order, `>' if T1 is after T2 in the standard order. Thus compare(=,T1,T2) is equivalent to T1 == T2. sort(L1, L2) The elements of the list L1 are sorted into the standard order, and any identical (i.e. `==') elements are merged, yielding the list L2. keysort(L1, L2) The list L1 must consist of items of the form Key-Value. These items are sorted into order according to the value of Key, yielding the list L2. No merging takes place. 5.8. Buffers SB-Prolog supports the concept of buffers. A buffer is actually a constant and the characters that make up the buffer is the name of the constant. However, the symbol table entry for a buffer is not hashed and thus is not added to the obj-list, so two dif- ferent buffers will never unify. Buffers can be allocated either in permanent space or on the heap. Buffers in permanent space stay there forever; buffers on the heap are deallocated when the ``allocate buffer'' goal is backtracked over. A buffer allocated on the heap can either be a simple buffer, or it can be allocated as a subbuffer of another buffer already on the heap. A subbuffer will be deallocated when its superbuffer is deallocated. There are occasions when it is not known, in advance, exactly how much space will be required and so how big a buffer 29 should be allocated. Sometimes this problem can be overcome by allocating a large buffer and then, after using as much as is needed, returning the rest of the buffer to the system. This can be done, but only under very limited circumstances: a buffer is allocated from the end of the permanent space, the top of the heap, or from the next available space in the superbuffer; if no more space has been used beyond the end of the buffer, a tail portion of the buffer can be returned to the system. This opera- tion is called ``trimming'' the buffer. The following is a list of library predicates for buffer management: alloc_perm(Size, Buff) Allocates a buffer with a length Size in the permanent (i.e. program) area. Size must be bound to a number. On successful return, Buff will be bound to the allocated buffer. The buffer, being in the permanent area, is never de-allocated. alloc_heap(Size, Buff) Allocates a buffer of size Size on the heap and binds Buff to it. Since it is on the heap, it will be deallocated on backtracking. trimbuff(Type, Buff, Newlen) This allows (in some very restricted circumstances) the changing of the size of a buffer. Type is 0 if the buffer is permanent, 1 if the buffer is on the heap. Buff is the buffer. Newlen is an integer: the size (which should be smaller than the original length of the buffer) to make the buffer. If the buffer is at the top of the heap (if heap buffer) or the end of the program area (if permanent) then the heap-top (or program area top) will be readjusted down. The length of the buffer will be modified to Newlen. This is (obviously) a very low-level primitive and is for hackers only to implement grungy stuff. conlength(Constant,Length) Succeeds if the length of the print name of the constant Constant (which can be an atom, buffer or integer), in bytes, is Length. If Constant is a buffer, it is the length of the buffer; if Constant is an integer, it is the length of the decimal representation of that integer, i.e., the number of bytes that a $writename will use. 5.9. Modification of the Program The predicates defined in this section allow modification of the program as it is actually running. Clauses can be added to the program (asserted) or removed from the program (retracted). At the lowest level, the system supports the asserting of clauses 30 with upto one literal in the body. It does this by allocating a buffer and compiling code for the clause into that buffer. Such a buffer is called a ``clause reference'' (clref). The clref is then added to a chain of clrefs. The chain of clrefs has a header, which is a small buffer called a ``predicate reference'' (prref), which contains pointers to the beginning and end of its chain of clrefs. Clause references are quite similar to ``data- base references'' of C-Prolog, and can be called. When clauses are added to the program through assert, an index is normally created on the principal functor of the first argument in the head of the clause. The argument on which the index is being created may be changed via the index/3 directive. In particular, if no index is desired on a predicate, this should be specified using the index/3 directive with the argument number set to zero, e.g. if no index is desired on a predicate foo/3, then the directive :- index(foo, 3, 0). should be specified. The predicates that can be used to modify the program are the following: assert(C) The current instance of C is interpreted as a clause and is added to the program (with new private variables replacing any uninstantiated variables), at the end of the list of clauses for that predicate. C must be instantiated to a non-variable. assert(C, Ref) As for assert/1, but also unifies Ref with the clause refer- ence of the clause asserted. asserti(C,N) The current instance of C, interpreted as a clause, is asserted to the program with an index on its N[th] argument. If N is zero, no index is created. asserta(C) Similar to assert(C), except that the new clause becomes the first clause of the procedure concerned. asserta(C, Ref) Similar to asserta(C), but also unifies Ref with the clause reference of the clause asserted. 31 assertz(C) Similar to assert(C), except that the new clause becomes the last clause of the procedure concerned. assertz(C, Ref) Similar to assertz(C), but also unifies Ref with the clause reference of the clause asserted. assert_union(P, Q) The clauses for Q are added to the clauses for P. For exam- ple, the call | ?- assert_union(p(X,Y),q(X,Y)). has the effect of adding the rule p(X,Y) :- q(X,Y). as the last rule defining p/2. If P is not defined, it results in the call to Q being the only clause for P. The variables in the arguments to assert_union/2 are not significant, e.g. the above would have been equivalent to | ?- assert_union(p(Y,X),q(X,Y)). or | ?- assert_union(p(_,_),q(_,_)). However, the arities of the two predicates involved must match, e.g. even though the goal | ?- assert_union(p(X,Y), r(X,Y,Z)). will succeed, the predicate p/2 will not in any way depend on the clauses for r/3. assert(Clause,AZ,Index,Clref) Asserts a clause to a predicate. Clause is the clause to assert. AZ is 0 for insertion as the first clause, 1 for insertion as the last clause. Index is the number of the argument on which to index (0 for no indexing). Clref is returned as the clause reference of the fact newly asserted. If the main functor symbol of Clause has been declared (by $assertf_alloc_t/2, see below) to have its clauses on the heap, the clref will be allocated there. If the predicate symbol of Clause is undefined, it will be ini- tialized and Clause added. If the predicate symbol has com- piled clauses, it is first converted to be dynamic (see sym- type/2, Section 5.10) by adding a special clref that calls the compiled clauses. Fact, AZ and Index are input 32 arguments, and should be instantiated at the time of call; Clref is an output argument, and should be uninstantiated at the time of call. clause(P,Q) P must be bound to a non-variable term, and the pro- gram is searched for a clause Cl whose head matches P. The head and body of the clause Cl is unified with P and Q respectively. If Cl is a unit clause, Q will be unified with `true'. Only interpreted clauses, i.e. those created through assert, can be accesed via clause/2. clause(Head,Body,Ref) Similar to clause(Head,Body) but also unifies Ref with the database reference of the clause concerned. clause/3 can be executed in one of two modes: either Head must be instan- tiated to a non-variable term at the time of the call, or Ref must be instantiated to a database reference. As in the case of clause/2, only interpreted clauses, i.e. those created through assert, can be accesed via clause/3. retract(Clause) The first clause in the program that unifies with Clause is deleted from the program. This predicate may be used in a non-deterministic fashion, i.e. it will successively back- track to retract clauses whose heads match Head. Head must be initially instantiated to a non-variable. In the current implementation, retract works only for asserted (e.g. con- sulted) clauses. abolish(P) Completely remove all clauses for the procedure with head P (which should be a term). For example, the goal | ?- abolish( p(_, _, _) ). removes all clauses for the predicate p/3. abolish(P, N) Completely remove all clauses for the predicate P (which should be an atom) with arity N (which should be an integer). 5.10. Internal Database recorded(Key, Term, Ref) 33 The internal database is searched for terms recorded under the key Key. These terms are successively unified with Term in the order they occur in the database; at the same time, Ref is unified with the database reference of the recorded item. The key must be given, and may be an atom or complex term. If it is a complex term, only the principal functor is significant. recorda(Key, Term, Ref) The term Term is recorded in the internal database as the first item for the key Key, where Ref is its database refer- ence. The key must be given, and only its principal functor is significant. recordz(Key, Term, Ref) The term Term is recorded in the internal database as the last item for the key Key, where Ref is its database refer- ence. The key must be given, and only its principal functor is significant. erase(Clref) The recorded item or clause whose database reference is Clref is deleted from the internal database or program. Clref should be instantiated at the time of call. instance(Ref, Term) A (most general) instance of the recorded term whose data- base reference is Ref is unified with Term. Ref must be instantiated to a database reference. Note that instance/2 will not be able to access terms that have been erased. 5.11. Information about the State of the Program listing Lists in the current output stream the clauses for all the interpreted predicates in the program, except predicates that are ``internal'', i.e. whose names begin with `$' or `_$', or which are provided as predefined (builtin or library) predicates. A bug in the current system is that even though the user is allowed to redefine such predicates, listing/0 does not know about such redefinitions, and will not list such predicates (they may, however, be accessed through listing/1 if they are interpreted). listing(A) The argument A may be a predicate specification of the form Name/Arity in which case only the clauses for the specified 34 predicate are listed. Alternatively, it is possible for A to be a list of predicate specifications, e.g. | ?- listing([concatenate/3, reverse/2, go/0]). Only interpreted clauses, i.e. clauses created via assert, can be accessed through listing/1. current_atom(Atom) Generates (through backtracking) all currently known atoms, and unifies each in turn with Atom. However, atoms con- sidered ``internal'' symbols, i.e. those whose names begin with $ or _$ are not returned. The intrepid user who wishes to access such internal atoms as well can use the goal ?- $current_atom(Atom, 1). current_functor(Name, Term) Generates (through backtracking) all currently known func- tors (which includes function and predicate symbols), and for each one returns its name and most general term as Name and Term respectively. However, functors considered ``internal'' symbols, i.e. those whose names begin with $ or _$, or which are provided as predefined predicates, are not returned if both arguments to current_functor/2 are vari- ables. Internal symbols (of which there are a great many) as well as external ones may be accessed via ?- $current_functor(Name, Term, 1). A bug in the current implementation is that even though the user is allowed to redefine ``internal'' (builtin or library) predicates, current_functor/2 does not know whether they have been redefined, and hence will not return such predicates if both arguments to current_functor/2 are vari- ables. current_predicate(Name, Term) Generates (through backtracking) all currently known predi- cates, and for each one returns its name and most general term as Name and Term respectively. However, predicates considered ``internal'', i.e. those whose names begin with $ or _$, or which are provided as predefined predicates, are not returned if both arguments to current_predicate/2 are variables. Internal symbols (of which there are a great many) as well as external ones may be accessed via ?- $current_predicate(Name, Term, 1). 35 A bug in the current implementation is that even though the user is allowed to redefine ``internal'' (builtin or library) predicates, current_predicate/2 does not know whether they have been redefined, and hence will not return such predicates if both arguments to current_predicate/2 are variables. predicate_property(Term, Property) If Term is a term whose principal functor is a predicate, Property is unified with the currently known properties of the corresponding predicate. If Term is a variable, then it is unified (successively, through backtracking) with the most general term for a predicate whose known properties are unified with Property. For example, all the interpreted predicates in the program may be enumerated using ?- predicate_property(X, interpreted). If the first argument to predicate_property/2 is uninstan- tiated at the time of the call, ``internal'' predicates will not be returned. A bug in the current implementation is that even though the user is allowed to redefine such ``internal'' predicates, predicate_property/2 does not know about such redefinitions, and will not return such predi- cates if its first argument is uninstantiated. Currently, the only properties that are considered are interpreted and compiled. 5.12. Environmental op(priority, type, name) Treat name as an operator of the stated type and priority (see Section 3.2). name may also be a list of names, in which all are to be treated as operators of the stated type and priority. break Causes the current execution to be suspended at the next procedure call. Then the message ``[ Break (level 1) ]'' is displayed. The interpreter is then ready to accept input as though it was at the top level (except that at break level n > 0, the prompt is ``n: ?- ''). If another call of break is encountered, it moves up to level 2, and so on. To close the break and resume the execution which was suspended, type the END-OF-INPUT character. Execution will be resumed at the procedure call where it had been suspended. Alterna- tively, the suspended execution can be aborted by calling the evaluable predicate abort, which causes a return to the top level. 36 abort Aborts the current execution, taking you back to top level. save(F) The system saves the current state of the system into file F. restore(F) The system restores the saved state in file F to be the current state. One restriction imposed by the current sys- tem is that various system parameters (e.g. stack sizes, permanent space, heap space, etc.) of the saved state have to be the same as that of the current invocation. Thus, it is not possible to save a state from an invocation where 50000 words of permanent space had been allocated, and then restore the same state in an invocation with 100000 words of permanent space. cputime(X) Unifies X with the time elapsed, in milliseconds, since the system was started up. $getenv(Var,Val) Val is unified with the value of the Unix environment vari- able Var. Fails is Var is undefined. statistics Prints out the current allocations and amounts of space used for each of the four main areas: the permanent area, the local stack, the global stack and the trail stack. Does not work well unless the simulator has been called with the -s option (see Section 7.2). statistics(Keyword, List) Usually used with Keyword instantiated to a keyword, e.g. `runtime', and List unbound. It unifies List with a list of statistics determined by Keyword. The keys and values are summarized below. Times are given in milliseconds and sizes are given in bytes. Keyword List runtime [cpu time used by Prolog, cpu time since last call to statistics/2] memory [total virtual memory, 0] core ( same as for the keyword memory ) program [program space in use, program space free] 37 heap ( same as for the keyword program ) global_stack [global stack in use, global stack free] local_stack [local stack in use, local stack free] trail [trail stack in use, trail stack free] garbage_collection [0, 0] stack_shifts [0, 0] Note: (1) For the keyword `memory' the second element of the returned list is always 0. (2) For the keyword `trail', the second element of the returned list is the amount of trail stack free. This is similar to Sicstus Prolog (version 0.5), but different from Quintus Prolog (version 1.6). (3) Currently, SB-Prolog does not have garbage collection or stack shifting, hence the list values returned for these are [0, 0]. nodynload(P, N) Flags the predicate P with arity N as one that should not be attempted to be dynamically loaded if it is undefined. If a predicate so flagged is undefined when a call to it is encountered, the call fails quietly without trying to invoke the dynamic loader or giving an error message. P and N should be instantiated to an atom and an integer, respec- tively, at the time of call to nodynload/2. symtype(T, N) Unifies N with the ``internal type'' of the principal func- tor of the term T, which must be instantiated at the time of the call. N is bound to 0 if T does not have an entry point defined (i.e. cannot be executed); to 1 if the principal functor of T is ``dynamic'', i.e. has asserted code; to 2 if the principal functor for T is a compiled predicate; and 3 if T denotes a buffer. Thus, for example, if the predicate p/2 is a compiled predicate which has been loaded into the system, the goal | ?- symtype(p(_,_), X). will succeed binding X to 2; on the other hand, the goal | ?- assert(q(a,b,c)), symtype(q(_,_,_), X). will succeed binding X to 1. system(Call) 38 Calls the operating system with the atom Call as argument. For example, the call | ?- system('ls'). will produce a directory listing. Since system/1 is exe- cuted by forking off a shell process, it cannot be used, for example, to change the working directory of the simulator. The equivalent MS-DOS operation to get a directory listing is | ?- system('dir'). In MS-DOS it is possible to change the current directory. syscall(N, Args, Res) Executes the Unix system call number N with arguments Args, and returns the result in Res. N is an integer, and Args a Prolog list of the arguments to the system call. For exam- ple, to execute the system call ``creat(File,Mode)'', know- ing that the syscall number for the Unix command creat(2) is 8, we execute the goal | ?- syscall(8, [File, Mode], Des). where Des is the file descriptor returned by creat. The syscall numbers for some Unix system calls are given in Table 2 (Only the access call works for MS-DOS). 5.13. Global Values SB-Prolog has some primitives that permit the programmer to mani- pulate global values. These are provided primarily as an effi- ciency hack, and needless to say, should be used with a great deal of care. globalset(Term) Allows the user to save a global value. Term must be bound to a compound term, say p(V). V must be a number or a con- stant or a variable. If V is a number or a constant, the effect of globalset(p(V)) can be described as: retract(p(_)), assert(p(V)). I.e., p is a predicate that when called will, from now on (until some other change by globalset/1), deterministically return V. If V is a variable, the effect is to make V a global variable whose value is accessible by calling p. For example, executing ``globalset(p(X))'' makes X a global variable. X can be set by unification with some other term. 39 _______________________________________ | exit 1| fork 2| | read 3| write 4| | open 5| close 6| | creat 8| link 9| | unlink 10| chdir 12| | chmod 15| lseek 19| | access 33| kill 37| | wait 84| socket 97| | connect 98| accept 99| | send 101| recv 102| | bind 104| setsockopt 105| | listen 106| recvmsg 113| | sendmsg 114| getsockopt 118| | recvfrom 125| sendto 133| | socketpair 135| mkdir 136| |__________________|___________________| Table 2: Some Syscall Numbers for Unix Systems Calls On backtracking, X will be restored to its earlier value. gennum(Newnum) gennum/1 sets its argument to a new integer every time it is invoked. gensym(C, Newsym) gensym/2 sets its second argument to an atom whose name is made by concatenating the name of the atom C to the current gennum number. This new constant is bound to Newsym. For example, if the current gennum number is 37, then the call | ?- gensym(aaa,X) will succeed binding X to the atom `aaa37'. 5.14. Exotica This section describes some low level routines that are sometimes useful in mucking around with buffers. These are for serious hackers only. $alloc_buff(Size,Buff,Type,Supbuff,Retcode) Allocates a buffer. Size is the length (in bytes) of the buffer to allocate; Buff is the buffer allocated, and should 40 be unbound at the time of the call; Type indicates where to allocate the buffer: a value of 0 indicates that the buffer is to be allocated in permanent space, 1 that it should be on the heap, and 2 indicates that it should be allocated from a larger heap buffer; Supbuff is the larger buffer to allocate a subbuffer out of, and is only looked at if the value of Type is 2; Retcode is the return code: a value of 0 indicates that the buffer has been allocated, while a value of 1 indicates that the buffer could not be allocated due to lack of space. The arguments Size, Type, and Supbuff (if Type = 2) are input arguments, and should be bound at the time of the call; Buff and Retcode are output arguments, and should be unbound at the time of the call. call_ref(Call, Ref) Calls the predicate whose database reference (prref) is Ref, using the literal Call as the call. This is similar to call_ref(Call, Ref, 0). call_ref(Call, Ref, Tr) Calls the predicate whose database reference (prref) is Ref, using the literal Call as the call. Tr must be either 0 or 1: if Tr is 0 then the call Call is made assuming the ``trust'' optimization will be made; if Tr is 1 then the ``trust'' optimization is not used, so that any new fact added before final failure will be seen by Call. (Also, this currently does not take advantage of any indexing that might have been constructed.) Call, Ref and Tr are all input arguments, and should be instantiated at the time of call. $assertf_alloc_t(Palist,Size) Declares that each predicate in the list Palist of predicate/arity pairs (terms of the form `/'(P,N) where P is a predicate symbol and N the arity of P) is to have any facts asserted to them stored in a buffer on the heap, to be allocated here. This allocates a superbuffer of size Size on the heap. Future assertions to these predicates will have their clauses put in this buffer. When this call is backtracked over, any clauses asserted to these predicates are deallocated, and a subsequent call to any of those predicates will cause the simulator to report an error and fail. Both Palist and Size are input arguments, and should be instantiated at the time of call. $db_new_prref(Prref,Where,Supbuff) Creates an empty Prref, i.e. one with no facts in it. If called, it will simply fail. Where indicates where the prref should be allocated: a value of 0 indicates the per- manent area, while a value of 2 indicates that it is to be allocated as a subbuffer. Supbuff is the superbuffer from 41 which to allocate Prref if Where is 2. Where should be instantiated at the time of call, while Prref should be uninstantiated; in addition, if Where is 2, Supbuff should be instantiated at the time of call. $db_assert_fact(Fact,Prref,AZ,Index,Clref,Where,Supbuff) Fact is a fact to be asserted; Prref is a predicate refer- ence to which to add the asserted fact; AZ is either 0, indicating the fact should be inserted as the first clause in Prref, or 1, indicating it should be inserted as the last; Index is 0 if no index is to be built, or n if an index on the n[th] argument of the fact is to be used. (Asserting at the beginning of the chain with indexing is not yet supported.) Where indicates where the clref is to be allocated: a value of 0 indicates that it should be in the permanent area, while a value of 2 indicates that it should be allocated as a subbuffer of Supbuff. Clref is returned and it is the clause reference of the asserted fact. Fact, Prref, AZ, Index and Where are input argu- ments, and should be instantiated at the time of call; in addition, if Where is 2, then Supbuff should also be instan- tiated. Clref is an output argument, and should be unin- stantiated at the time of call. $db_add_clref(Fact,Prref,AZ,Index,Clref,Where,Supbuff) Adds the clref Clref to the prref Prref. Fact is the fact that has been compiled into Clref (used only to get the arity and for indexing). The other parameters are as for $db_assert_fact/7. $db_call_prref(Call,Prref) Calls the prref Prref using the literal Call as the call. The call is done by simply branching to the first clause. New facts added to Prref after the last fact has been retrieved by Call, but before Call is failed through, will not be used. Both Call and Prref are input arguments, and should be instantiated at the time of call. $db_call_prref_s(Call,Prref) This also calls the prref Prref using Call as the call. The difference from $db_call_prref is that this does not use the ``trust'' optimization, so that any new fact added before final failure will be seen by Call. (Also, this currently does not take advantage of any indexing that might have been constructed, while $db_call_prref does.) Both Call and Prref are input arguments, and should be instan- tiated at the time of call. $db_get_clauses(Prref,Clref,Dir) 42 This returns, nondeterministically, all the clause refer- ences Clref for clauses asserted to prref Prref. If Dir is 0, then the first clref on the list is returned first; if Dir is 1, then they are returned in reverse order. Prref and Dir are input arguments, and should be instantiated at the time of call; Clref is an output argument, and should be uninstantiated at the time of call. 6. Debugging 6.1. High Level Tracing The preferred method of tracing execution is through the predicate trace/1. This predicate takes as argument a term P/N, where P is a predicate name and N its arity, and sets a ``trace point'' on the corresponding predicate; it can also be given a list of such terms, in which case a trace point is set on each member of the list. For example, executing | ?- trace(pred1/2), trace([pred2/3, pred3/2]). sets trace points on predicates pred1/2, pred2/3 and pred3/2. Only those predicates are traced that have trace points set on them. If all the predicates in a file are to be traced, it is usu- ally convenient to use the PredList parameter of compile/4 or consult/3, e.g.: | ?- compile(foo, 'foo.out', [t,v], Preds), load('foo.out'), trace(Preds). or | ?- consult(foo, [v], Preds), trace(Preds). Notice that in the first case, the t compiler option (see Section 8.3) should be specified in order to turn off certain assembler optimizations and facilitate tracing. In the second case, the same effect may be achieved by specifying the t option to con- sult. The trace points set on predicates may be overwritten by loading byte code files via load/1, and in this case it may be necessary to explicitly set trace points again on the loaded predicates. This does not happen with consult: predicates that were being traced continue to have trace points set after con- sulting. The tracing facilities of SB-Prolog are in many ways very similar to those of C-Prolog. However, leashing is not sup- ported, and only those predicates can be traced which have had trace points set on them through trace/1. This makes trace/1 and spy/1 very similar: essentially, trace amounts to two levels of spy points. In SB-Prolog, tracing occurs at Call (i.e. entry to 43 a predicate), successful Exit from a clause, and Failure of the entire call. The tracing options available during debugging are the following: c, NEWLINE: Creep Causes the system to single-step to the next port (i.e. either the entry to a traced predicate called by the exe- cuted clause, or the success or failure exit from that clause). a: Abort Causes execution to abort and control to return to the top level interpreter. b: Break Calls the evaluable predicate break, thus invoking recur- sively a new incarnation of the system interpreter. The command prompt at break level n is n: ?- The user may return to the previous break level by entering the system end-of-file character (e.g. ctrl-D), or typing in the atom end_of_file; or to the top level interpreter by typing in abort. f: Fail Causes execution to fail, thus transferring control to the Fail port of the current execution. h: Help Displays the table of debugging options. l: Leap Causes the system to resume running the program, only stop- ping when a spy-point is reached or the program terminates. This allows the user to follow the execution at a higher level than exhaustive tracing. n: Nodebug Turns off debug mode. q: Quasi-skip This is like Skip except that it does not mask out spy points. r: Retry (fail) Transfers to the Call port of the current goal. Note, how- ever, that side effects, such as database modifications etc., are not undone. s: Skip Causes tracing to be turned off for the entire execution of 44 the procedure. Thus, nothing is seen until control comes back to that procedure, either at the Success or the Failure port. Other predicates that are useful in debugging are: untrace(Preds) where Preds is a term P/N, where P is a predicate name and N its arity, or a list of such terms. Turns off tracing on the specified predicates. Preds must be instantiated at the time of the call. spy(Preds) where Preds is a term P/N, where P is a predicate name and N its arity, or a list of such terms. Sets spy points on the specified predicates. Preds must be instantiated at the time of the call. nospy(Preds) where Preds is a term P/N, where P is a predicate name and N its arity, or a list of such terms. Removes spy points on the specified predicates. Preds must be instantiated at the time of the call. debug Turns on debugging mode. This causes subsequent execution of predicates with trace or spy points to be traced, and is a no-op if there are no such predicates. The predicates trace/1 and spy/1 cause debugging mode to be turned on automatically. nodebug Turns off debugging mode. This causes trace and spy points to be ignored. debugging Displays information about whether debug mode is on or not, and lists predicates that have trace points or spy points set on them. tracepreds(L) Binds L to a list of terms P/N where the predicate P of arity N has a trace point set on it. spypreds(L) Binds L to a list of terms P/N where the predicate P of arity N has a spy point set on it. There is one known bug in the package: attempts to set trace points, via trace/1, on system and library predicates that are used by the trace package can cause bizarre behaviour. 45 6.2. Low Level Tracing SB-Prolog also provides a facility for low level tracing of exe- cution. This can be activated by invoking the simulator with the -T option, or through the predicate $trace/0. It causes trace information to be printed out at every call (including those to system trap handlers). The volume of such trace information can very become large very quickly, so this method of tracing is not recommended in general. Low level tracing may be turned off using the predicate untrace/0. 7. The Simulator The simulator resides in the SB-Prolog system directory sim. The following sections describe various aspects of the simulator. 7.1. Invoking the Simulator The simulator is invoked by the command sbprolog bc_file where bc_file is a byte code file resulting from the compilation of a Prolog program. In almost all cases, the user will wish to interact with the SB-Prolog query evaluator, in which case bc_file will be $readloop, and the command will be sbprolog Path/$readloop where Path is the path to the directory containing the command interpreter $readloop. This directory, typically, is the system directory modlib. The command interpreter reads in a query typed in by the user, evaluates it and prints the answer(s), repeating this until it encounters an end-of-file (the standard end-of-file character on the system, e.g. ctrl-D (ctrl-Z for MS-DOS)), or the user types in end_of_file or halt. The user should ensure that the the directory containing the executable file sim (typically, the system directory sim) is included in the shell variable path; if not, the full path to the simulator will have to be specified. In general, the simulator may be invoked with a variety of options, as follows: 46 sbprolog -options bc_file or sbprolog -option<1> -option<2> ... -option bc_file The options recognized by the simulator are described below. When called with a byte code file bc_file, the simulator begins execution with the first clause in that file. The first clause in such a file, therefore, should be a clause without any arguments in the head (otherwise, the simulator will attempt to dereference argument pointers in the head that are really point- ing into deep space, and usually come to a sad end). If the user is executing a file in this manner rather than using the command interpreter, he should also be careful to include the undefined predicate handler `_$undefined_pred'/1, which is normally defined in the file modlib/$init_sys.P. Under MS-DOS the simulator can either be invoked using the batch file sbp or by running the sim executable with the appropriate parameters. 7.2. Simulator Options The following is a list of options recognized by the simulator. T Generates a trace at entry to each called routine. d Produces a disassembled dump of bc_file into a file named `dump.pil' and exits. n Adds machine addresses when producing trace and dump. s Maintains information for the builtin statistics/0. Default: off. m size Allocates size words (4 bytes) of space to the local stack and heap together. Default: 100000. p size Allocates size words of space to the program area. Default: 100000. b size Allocates size words of space to the trail stack. Default: m/5, where m is the amount of space allocated to the local stack and heap together. This parameter, if specified, must follow the -m parameter. As an example, the command 47 sbprolog -s -p 60000 -m 150000 \$readloop starts the simulator executing the command interpreter with 60000 bytes of program space, 150000 bytes of local and global stack space and (by default) 30000 bytes of trail stack space; the s option also results in statistics information being maintained. 7.3. Interrupts SB-Prolog provides a facility for exception handling using user- definable interrupt handlers. This can be used both for external interrupts, e.g. those generated from the keyboard by the user or from signals other processes; or internal traps, e.g. those caused by stack overflows, encountering undefined predicates, etc. For example, the ``undefined predicate'' interrupt is han- dled, by default, by the predicate `_$undefined_pred'/1, which is defined in the files modlib/src/$init_sys.P and modlib/src/$readloop.P. The default action on encountering an undefined predicate is to attempt to dynamically load a file whose name matches that of the undefined predicate. However, the user may easily alter this behaviour by redefining the undefined predicate handler. In general, interrupts are handled by the predicate `_$interrupt'/2: a call to this predicate is of the form `_$interrupt'(Call, Code), where Call is the call that generated the interrupt, and Code is an integer indicating the nature of the interrupt. For each interrupt code, the interrupt handler then calls a handler that is designed to handle that particular kind of interrupt. At this point, the following interrupt codes have predefined meanings: 0 undefined predicate; 1 keyboard interrupt ( ^C ); 2 stack overflow. Other interrupt codes may be incorporated by modifying the defin- ition of the predicate `_$interrupt'/2 in the file modlib/src/$readloop.P. Interrupts during execution are signalled from within the WAM simulator. The general method for raising an interrupt is using the function set_intercode in the file sim/sub_inst.c: to raise an interrupt whose code is n, the line lpcreg = set_intercode(n); is added to the appropriate place in the main loop of the inter- preter, defined in sim/main.c. 48 8. The Compiler The compiler translates Prolog source files into byte-code object files. It is written entirely in Prolog. The byte code for the compiler can be found in the SB-Prolog system directory cmplib, with the source code resident in cmplib/src. Byte code files may be concatenated together to produce other byte code files. Thus, for example, if foo1 and foo2 are byte code files resulting from the compilation of two Prolog source programs, then the file foo, obtained by executing the shell command cat foo1 foo2 > foo is a byte code file as well, and may be loaded and executed. In this case, loading and executing the file foo would give the same result as loading foo1 and foo2 separately, which in turn would be the same as concatenating the original source files and com- piling this larger file. This makes it easier to compile large programs: one need only break them into smaller pieces, compile the individual pieces, and concatenate the byte files together. On MS-DOS systems the same operation can be achieved by using the command: copy /b foo1 + foo2 foo The following sections describe the various aspects of the compiler in more detail. 8.1. Invoking the Compiler The compiler is invoked through the Prolog predicate compile: | ?- compile(InFile [, OutFile ] [, OptionsList ]). where optional parameters are enclosed in brackets. InFile is the name of the input (i.e. source) file; OutFile is the name of the output file (i.e. byte code) file; OptionsList is a list of compiler options (see below). The input and output file names must be Prolog atoms, i.e. either begin with a lower case letter or dollar sign `$', and consist only of letters, digits, and underscores; or, be enclosed within single quotes. If the output file name is not specified, it defaults to InFile.out. The list of options, if specified, is a Prolog list, i.e. a term of the form [ option<1>, option<2>, ..., option ]. 49 If left unspecified, it defaults to the empty list []. In fact, the output file name and the options list may be specified in any order. Thus, for example, the queries | ?- compile('/usr/debray/foo', foo_out, [v]). and | ?- compile('/usr/debray/foo', [v], foo_out). are equivalent, and specify that the Prolog source file `/usr/debray/foo' is to be compiled in verbose mode (see ``Com- piler Options'' below), and that the byte code is to be generated into the file foo_out. The compile predicate may also be called with a fourth parameter: | ?- compile(InFile, OutFile, OptionsList, PredList). where InFile, OutFile and OptionsList are as before; compile/4 unifies PredList with a list of terms P/N denoting the predicates defined in InFile, where P is a predicate name and N its arity. PredList, if specified, is usually given as an uninstantiated variable; its principal use is for setting trace points on the predicates in the file (see Section 6), e.g. by executing | ?- compile('/usr/debray/foo', foo_out, [v], L), load(foo_out), trace(L). Notice that PredList can only appear in compile/4. 8.2. Compiler Options The following options are currently recognized by the compiler: a Specifies that an ``assembler'' file is to be created. The name of the assembler file is obtained by appending ``.asl'' to the source file name. While the writing out of assembly code slows down the compilation process to some extent, it allows the assembler to do a better job of optimizing away indirect subroutine linkages (since in this case the assem- bler has assembly code for the entire program to work with at once, not just a single predicate). This results in code that is faster and more compact. d Dumps expanded macros to the user (see Section 10). e Expand macros (see Section 10). t If specified, turns off assembler optimizations that elim- inate indirect branches through the symbol table in favour of direct branches. This is useful in debugging compiled code. It is necessary if the extension table feature is to be used. 50 v If specified, compiles in ``verbose'' mode, which causes messages regarding progress of compilation to be printed out. 8.3. Assembly The SB-Prolog assembler can be invoked by loading the compiler and using the predicate $asm/3: | ?- $asm(InFile, OutFile, OptionsList). where InFile is a Prolog atom which is the name of a WAM assembly source file (e.g. the ``.asl'' file generated when a Prolog pro- gram is compiled with the ``a'' option), OutFile is an atom which is the name of the intended byte code file, and OptionsList is a list of options. The options recognized by the assembler are: v ``Verbose'' mode. Prints out information regarding progress of assembly. t ``Trace''. If specified, the assembler generates code to force procedure calls to branch indirectly via the symbol table, instead of using a direct branch. This is useful for tracing compiled code. It is necessary if the extension table feature is to be used. The assembler is intended primarily to support the compiler, so the assembly language syntax is quirky in places. The interested reader is advised to look at the assembly files resulting from compilation with the ``a'' option for more on SB-Prolog assembler syntax. 8.4. Compiler Directives 8.4.1. Mode Declarations The user may declare input and output arguments of predi- cates using mode declarations. These declarations, for an n-ary predicate p, are of the form :- mode p( Mode ). where Mode consists of n mode values; or :- mode(p, n, ModeList) where ModeList is a list of mode values of length n. Mode values may be the following: c, ++ 51 Indicates that the corresponding argument position is always a ground term in any call to the predicate. The argument is therefore an input argument. nv, + Indicates that the corresponding argument position is always a nonvariable term (i.e. is instantiated) in any call in any call to the predicate. The argument is therefore an input argument. f, - Indicates that the corresponding argument position is always an uninstantiated variable in any call to the predicate. The argument is therefore an output argument. d, ? Indicates that the corresponding argument may be any term in calls to the predicate. For example, a 3-ary predicate p whose first argument is always a ground term in a call, whose second argument is always uninstan- tiated, and whose third argument can be any term, may have its mode declared as :- mode p(++, -, d) or as :- mode(p, 3, [c, f, d]). Currently, mode information is used by the compiler in two ways. First, it often allows more compact code to be generated. The second use is in guiding program transformations that allow fas- ter code to be generated. For example, the predicate part([], _, [], []). part([E|L], M, [E|U1], U2) :- E =< M, part(L, M, U1, U2). part([E|L], M, U1, [E|U2]) :- E > M, part(L, M, U1, U2). executes about 30% faster with the mode declaration :- mode part(++, ++, -, -). than without. 8.4.2. Indexing Directives The compiler usually generates an index on the principal functor of the first argument of a predicate. The user may direct the 52 compiler to generate an index on any other argument by means of an indexing directive. This is of the form :- index(Pred, Arity, IndexArg) indicating that an index should be created on the IndexArg[th.] argument of the predicate Pred/Arity. All of the values Pred, Arity and IndexArg should be bound in the directive: Pred should be an atom, Arity a nonnegative integer, and IndexArg an integer between 0 and Arity. If IndexArg is 0, then no index is created for that predicate. As an example, if we wished to create an index on the third argument of a 5-ary predicate foo, the com- piler directive would be :- index(foo, 5, 3). An index directive may be placed anywhere in the file containing the predicate it refers to. 9. Libraries To describe how libraries are currently supported in our system, we must describe the _$undefined_pred/1 interrupt handler. The system keeps a table of libraries and routines that are needed from each. When a predicate is found to be undefined, the table is searched to see if it is defined by some library file. If so, that file is loaded (if it hasn't been previously loaded) and the association is made between the routine name as defined in the library file, and the routine name as used by the invoker. The table of libraries and needed routines is: defined_mods(Modname, [pred<1>/arity<1>, ..., pred/arity]). where Modname is the name of the library. It exports n predicate definitions. The first exported pred is of arity arity<>1, and needs to be invoked by the name of pred<1>. The table of libraries that have already been loaded is given by loaded_mods(Modname). A library file is a file of predicate definitions, together with a fact defining a list of predicates exported by it; and a set of facts, each of which specifies, for some other library file, the predicates imported from that library file. For example, con- sider a library name `p'. It contains a single fact, named p_export, that is true of the list of predicate/arity's that are exported. E.g. p_export([p1/2, p2/4]) 53 indicates that the module p exports the predicates p1/2 and p2/4. For each library m which contains predicates needed by the library p, there is a fact for p_use, describing what library is needed and the names of the predicates defined there that are needed. For example, if library p needs to import predicates ip1/2 and ip2/3 from library q, there would be a fact p_use(q,[ip1/2, ip2/3]) where q is a module that exports two predicates: one 2-ary and one 3-ary. This list corresponds to the export list of library q. The correspondence between the predicates in the export list of an exporting library, and those in the import or use list of a library which imports one or more of them, is by position, i.e. the predicate names at the exporting and importing names may be different, and the association between names in the two lists is by the position in the list. If the importing library does not wish to import one or more of the predicates exported by the exporting module, it may put an anonymous variable in the corresponding position in its use list. Thus, for example, if library p above had wished to import only the predicate ip2/3 from library q, the corresponding use fact would be p_use(q, [_, ip2/3]). The initial set of predicates and the libraries from which they are to be loaded is set up by an initial call to $prorc/0 (see the SB-Prolog system file modlib/src/$prorc.P). This predi- cate makes initial calls to the predicate $define_mod which set up the tables described above so that the use of standard predi- cates will cause the correct libraries to be loaded in the _$undefined_pred routine, and the correct names to be used. 10. Macros SB-Prolog features a facility for the definition and expansion of macros that is fully compatible with the runtime system. Its basic mechanism is a simple partial evaluator. It is called by both consult and compile, so that macro expansion occurs indepen- dently of whether the code is interpreted or compiled (but not when asserted). Moreover, the macro definitions are retained as clauses at runtime, so that invocation of macros via call/1 at runtime (or from asserted clauses) does not pose a problem. This means, however, that if the same macro is used in many different files, it will be loaded more than once, thus leading to wasetd space. This ought to be thought about and fixed. The source for the macro expander is in the SB-Prolog system file modlib/src/$mac.P. 54 10.1. Defining Macros `Macros', or predicates to be evaluated at compile-time, are defined by clauses of the form Head ::- Body where facts have `true' as their body. The partial evaluator will expand any call to a predicate defined by ::-/2 that unifies with the head of only one clause in ::-/2. If a call unifies with the head of more than one clause in ::-/2, it will not be expanded Notice that this is not a fundamental restriction, since `;' is permitted in the body of a clause. The partial evaluator also converts each definition of the form Head ::- Body. to a clause of the form Head :- Body. and adds this second clause to the other ``normal'' clauses that were read from the file. This ensures that calls to the macro at runtime, e.g. through call/1 or from unexpanded calls in the pro- gram do not cause any problems. The partial evaluator is actually a Prolog interpreter writ- ten `purely' in Prolog, i.e., variable assignments are expli- citly handled. This is necessary to be able to handle impure constructs such as `var(X), X=a'. As a result this is a very slow Prolog evaluator. Since naive partial evaluation can go into an infinite loop, SB-Prolog's partial evaluator maintains a depth-bound and will not expand recursive calls deeper than that. The depth is deter- mined by the globalset predicate $mac_depth. The default value for $mac_depth is 50 This can be changed to some other value n by executing | ?- globalset($mac_depth(n)). 10.2. Macro Expander Options The following options are recognized by the macro expander: d Dumps all clauses to the user after expansion. Useful for debugging. e Expand macros. If omitted, the expander simply converts each ::-/2 clause to a normal :-/2 clause. 55 v ``Verbose'' mode. Prints macros that are/are not being expanded. 11. Extension Tables: Memo Relations Extension tables store the calls and answers for a predicate. If a call has been made before, answers are retrieved from the extension table instead of being recomputed. Extension tables provide a caching mechanism for Prolog. In addition, extension tables affect the termination characteristics of recursive pro- grams. Some Prolog programs, which are logically correct, enter an infinite loop due to recursive predicates. An extension table saved on recursive predicates can find all answers (provided the set of such answers is finite) and terminate for some logic pro- grams for which Prolog's evaluation strategy enters an infinite loop. Iterations over the extension table execution strategy pro- vides complete evaluation of queries over function-free Horn clause programs. To be able to use the simple extension table evaluation on a set of predicates, the source file should either be consulted, or compiled with the t option (the t option keeps the assembler from optimizing subroutine linkage and allows the extension table facility to intercept calls to predicates). To use extension table execution, all predicates that are to be saved in the extension table must be passed to et/1. For exam- ple, | ?- et([pred1/1, pred2/2]), et(pred3/2) will set up ``ET-points'' for the for predicates pred1/1, pred2/2 and pred3/2, which will cause extension tables for these predi- cates to be maintained during execution. At the time of the call to et/1, these predicates must be defined, either by having been loaded, or through consult. The predicate noet/1 takes a list of predicate/arity pairs for which ET-points should be deleted. Notice that once an ET- point has been set up for a predicate, it will be maintained unless explicitly deleted via noet/1. If the definition of a predicate which has an ET-point defined is to be updated, the ET-point must first be deleted via noet/1. The predicate can then be reloaded and a new ET-point established. This is enforced by the failure of the goal ``et(P/N)'' if an ET-point already exists for the argument predicate. In this case, the following error message will be displayed: *et* already defined for: P/N There are, in fact, two extension table algorithms: a simple one, which simply caches calls to predicates which have ET-points 56 defined; and a complete ET algorithm, which iterates the simple extension table algorithm until no more answers can be found. The simple algorithm is more efficient than the complete one; however, the simple algorithm is not complete for certain espe- cially nasty forms of mutual recursion, while the complete algo- rithm is. To use the simple extension table algorithm, predi- cates can simply be called as usual. The complete extension table algorithm may be used via the query | ?- et_star(Query). The extension table algorithm is intended for predicates that are ``essentially pure'', and results are not guaranteed for code using impure code. The extension table algorithm saves only those answers which are not instances of what is already in the table, and uses these answers if the current call is an instance of a call already made. For example, if a call p(X, Y), with X and Y uninstantiated, is encountered and inserted into the exten- sion table, then a subsequent call p(X, b) will be computed using the answers for p(X, Y) already in the extension table. Notice that this might not work if var/nonvar tests are used on the second argument in the evaluation of p. Another problem with using impure code is that if an ET predicate is cut over, then the saved call implies that all answers for that predicate were computed, but there are only par- tial results in the ET because of the cut. So on a subsequent call the incomplete extension table answers are used when all answers are expected. Example: r(X,Y) :- p(X,Y),q(Y,Z),!,fail. | ?- r(X,Y) ; p(X,Y). Let p be an ET predicate whose evaluation yields many tuples. In the evaluation of the query, r(X,Y) makes a call to p(X,Y). Assuming that there is a tuple such that q(Y,Z) succeeds with the first p tuple then the evaluation of p is cut over. The call to p(X,Y) in the query uses the extension table because of the pre- vious call in the evaluation of r(X,Y). Only one answer is found, whereas the relation p contains many tuples, so the computation is not complete. Note that "cuts" used within the evaluation of an ET predicate are ok, as long as they don't cut over the evaluation of another ET predicate. The evaluation of the predi- cate that uses cuts does not cut over any et processing (such as storing or retrieving answers) so that the tuples that are com- puted are saved. In the following example, the ET is used to gen- erate prime numbers where an ET point is put on prime/1. Example: prime(I) :- globalset(globalgenint(2)),fail. /* Generating Primes */ 57 prime(I) :- genint(I), not(div(I)). div(I) :- prime(X), 0 is I mod X. genint(N) :- repeat, globalgenint(N), N1 is N+1, globalset(globalgenint(N1)). The following summarizes the library predicates supporting the extension table facility: et(L) Sets up an ET-point on the predicates L, which causes calls and anwers to these predicates to be saved in an ``extension table''. L is either a term Pred/Arity, where Pred is a predicate symbol and Arity its arity, or a set of such terms represented as a list. L must be instantiated, and the predicates specified in it defined, at the time of the call to et/1. Gives error messages and fails if any of the predicates in L is undefined, or if an ET-point already exists on any of them; in this case, no ET-point is set up on any of the predicates in L. et_star(Goal) Invokes the complete extension table algorithm on the goal Goal. et_points(L) Unifies L with a list of predicates for which an ET-point is defined. L is the empty list [] if there are no ET-points defined. noet(L) Deletes ET-points on the predicates specified in L. L is either a term P/N, where P is the name of a predicate and N its arity, or a set of such terms represented as a list. Gives error messages and fails if there is no ET-point on any of the predicates specified in L. Deleting an ET-point for a predicate also removes the calls and answers stored in the extension table for that predicate. The extension tables for all predicates for which ET-points are defined may be deleted using et_points/1 in cojnunction with noet/1. L must be instantiated at the time of the call to noet/1. et_remove(L) 58 Removes both calls and answers for the predicates specified in L. In effect, this results in the extension table for these predicates to be set to empty. L must be instantiated at the time of the call to either a term P/N, where P is a predicate with arity N, or a list of such terms. An error occurs if any of the predicates in L does not have an ET- point set. All extension tables can be emptied by using et_points/1 in conjunction with et_remove/1. et_answers(P/N, Term) Retrieves the answers stored in the extension table for the predicate P/N in Term one at a time. Term is of the form P(t<1>, ..., t). An error results and et_answers/2 fails if P/N is not fully specified (ground), or if P/N does not have an ET-point set. et_calls(P/N, Term) Retrieves the calls stored in the extension table for the predicate P/N in Term one at a time. Term is of the form P(t<1>, ..., t). An error results and et_calls/2 fails if P/N is not fully specified (ground), or if P/N does not have an ET-point set. 12. Definite Clause Grammars Definite clause grammars are an extension of context free gram- mars, and may be conveniently expressed in Prolog. A grammar rule in Prolog has the form Head --> Body. with the interpretation ``a possible form for Head is Body''. Extra conditions, in the form of explicit Prolog literals or con- trol constructs such as if-then-else (`->') or cut (`!'), may be included in Body. The syntax of DCGs supported by SB-Prolog is as follows: (1) A non-terminal symbol may be any Prolog term other than a variable. (2) A terminal symbol may be any Prolog term. To distinguish terminals from nonterminals, a sequence of terminal symbols a, b, c, d, . . . is written as a Prolog list [a, b, c, d, . . . ], with the empty sequence written as the empty list []. If the termi- nal symbols are ASCII character codes, they can be written 59 (as elsewhere) as strings. (3) Extra consitions, in the form of Prolog literals, can be included in the right-hand side of a rule by enclosing such conditions in curly braces, { and }. E.g., one can write natnum(X) --> {integer(X), X >= 0}. (4) The left hand side of a rule consists of a single nontermi- nal. Notice that ``push-back lists'' are thus not sup- ported. (5) The right hand side of a rule may contain alternatives (written using the disjunction operator `;' or `|'), and control primitives such as if-then-else (`->'), not/1 and cut (`!'). The use of not/1 on the right hand side of gram- mar rules is not recommended, however, because their seman- tics in this context is murky at best. All other control primitives, e.g. repeat/0, must explicitly be enclosed within curly braces if they are not to be interpreted as nonterminals. Except for the restriction of lists of terminals in the left hand sides of rules, the translation of DCGs in SB-Prolog is very similar to that in Quintus Prolog. Library predicates supporting DCGs are the following: dcg(Rule, Clause) Succeeds if the DCG rule Rule corresponds to the Prolog clause Clause. At the time of call, Rule must be bound to a term whose principal functor is `-->'/2. phrase(Phrase, List) The usual way to commence execution of grammar rules. The list List is a phrase (i.e., sequence of terminals) gen- erated by Phrase according to the current grammar rules. Phrase is a nonterminal (in general, the right hand side of a grammar rule), and must be instantiated to a nonvariable term in the call. If List is bound to a list of terminals in the call, then the goal corresponds to parsing List; if List is unbound in the call, then the grammar is being used for generation. expand_term(T1, T2) This predicate is used to transform terms that are read in, when a file is consulted or compiled. The usual use is to transform grammar rules into Prolog clauses: if T1 is a grammar rule, then T2 is the corresponding Prolog clause. 60 Users may define their own transformations by defining the predicate term_expansion/2. When a term T1 is read in when a file is being compiled or consulted, expand_term/2 first calls term_expansion/2: if the expansion succeeds, the transformed term so obtained is used; otherwise, if T1 is a grammar rule, then it is expanded using dcg/2; otherwise, T1 is used as is. `C'(S1, Terminal, S2) Used to handle terminal symbols in the expansion of grammar rules. Not usually of direct use to the user. This is defined as `C'([X|S], X, S). 13. Profiling Programs There is an experimental utility for profiling programs interac- tively. Two kinds of profiling are supported: one may count the number of calls to a predicate, or compute the time spent in a predicate. It is important that the predicates being profiled are either consulted, or compiled with the t option, so that calls to the relevant predicates can be intercepted by the pro- filer. To use the profiler, predicates whose calls are to be counted must be passed to count/1, e.g. | ?- count([p/1, q/2]), count(r/3). will set up ``count-points'' on the predicates p/1, q/2 and r/3. Predicates whose calls are to be timed have to be passed to time/1, e.g. | ?- time([s/1, t/2]), time(u/3). will set up ``time-points'' on the predicates s/1, t/2 and u/3. It is possible to set both count-points and time-points on the same predicate. After count-points and time-points have been set, the program may be executed as many times as desired: the profiling system will accumulate call counts and execution times for the appropriate predicates. Execution profiles may be obtained using the predicates prof_stats/0 or prof_stats/1. Using prof_stats/0 to display the execution profile will cause the call counts and execution times of predicates being profiled to be reset to 0 (this may be avoided by using prof_stats/1). It should be noted that in this context, the ``execution time'' for a predicate is an estimate of the total time spent in the subtrees below calls to that predicate (including failed sub- trees): thus, the execution time figures may be dilated slightly 61 if the subtree below a timed predicate contains predicates that are being profiled, because of the time taken for updating the call counts and execution times. For each predicate, the execu- tion time is displayed as the fraction of time spent, in computa- tion in subtrees under calls to that predicate, relative to the time elapsed from the last time profiling was timed on or the last time profiling statistics were taken, whichever was more recent. Bugs: May behave bizarrely if a predicate being profiled contains cuts. The following summarizes the library predicates supporting profiling: count(L) Sets up a count-point on the predicates L, which causes calls to these predicates to be counted, and turns profiling on. L is either a term Pred/Arity, where Pred is a predi- cate symbol and Arity its arity, or a set of such terms represented as a list. L must be instantiated, and the predicates specified in it defined, at the time of the call to count/1. time(L) Sets up a time-point on the predicates L, which causes exe- cution times for calls to these predicates to be accumu- lated, and turns profiling on. L is either a term Pred/Arity, where Pred is a predicate symbol and Arity its arity, or a set of such terms represented as a list. L must be instantiated, and the predicates specified in it defined, at the time of the call to time/1. nocount(L) Deletes the count-point on the predicates L. L is either a term Pred/Arity, where Pred is a predicate symbol and Arity its arity, or a set of such terms represented as a list. L must be instantiated, and the predicates specified in it defined, at the time of the call to nocount/1. notime(L) Deletes the time-point on the predicates L. L is either a term Pred/Arity, where Pred is a predicate symbol and Arity its arity, or a set of such terms represented as a list. L must be instantiated, and the predicates specified in it defined, at the time of the call to time/1. profiling 62 Displays information about whether profile mode is on or not, and lists predicates that have count- and time-points set on them. prof_reset(L) Resets call counts and/or execution times for the predicates L. L is either a term Pred/Arity, where Pred is a predicate symbol and Arity its arity, or a set of such terms represented as a list. L must be instantiated, and the predicates specified in it defined, at the time of the call to prof_reset/1. resetcount(L) Resets call counts for the predicates L. L is either a term Pred/Arity, where Pred is a predicate symbol and Arity its arity, or a set of such terms represented as a list. L must be instantiated, and the predicates specified in it defined, at the time of the call to resetcount/1. resettime(L) Resets execution times for the predicates L. L is either a term Pred/Arity, where Pred is a predicate symbol and Arity its arity, or a set of such terms represented as a list. L must be instantiated, and the predicates specified in it defined, at the time of the call to resettime/1. profile Turns profiling on. This causes subsequent execution of predicates with count- or time-points to be profiled, and is a no-op if there are no such predicates. The predicates count/1 and time/1 cause profiling to be turned on automati- cally. noprofile Turns profiling off. This causes count- and time-points to be ignored. timepreds(L) Unifies L to a list of terms P/N where the predicate P of arity N has a time point set on it. countpreds(L) Unifies L to a list of terms P/N where the predicate P of arity N has a count point set on it. prof_stats 63 Causes the call counts and/or execution times accumulated since the last call to prof_stats/0 to be printed out for predicates that are being profiled. The execution times are given as fractions of the total time elapsed since the last time profiling was turned on, or the last time prof_stats was called, whichever was most recent. This also results in the call counts and relative execution times of these predi- cates being reset to 0. Equivalent to prof_stats(1). prof_stats(N) Causes the call counts and/or execution times accumulated since the last call to prof_stats/0 to be printed out for predicates that are being profiled. The execution times are given as fractions of the total time elapsed since the last time profiling was turned on, or the last time prof_stats was called, whichever was most recent. If N is 1, then this also results in the call counts and execution times of these predicates being reset to 0; otherwise, the call counts and execution times are not reset. 14. Other Library Utilities The SB-Prolog library contains various other utilities, some of which are listed below. append(X, Y, Z) Succeeds if list Z is the concatenation of lists X and Y. member(X, L) Checks whether X unifies with any element of list L, succeeding more than once if there are multiple such ele- ments. $memberchk(X, L) Similar to member/2, except that $memberchk/2 is determinis- tic, i.e. does not succeed more than once for any call. $reverse(L, R) Succeeds if R is the reverse of list L. If L is not a fully determined list, i.e. if the tail of L is a variable, this predicate can succeed arbitrarily many times. $merge(X, Y, Z) Succeeds if Z is the list resulting from ``merging'' lists X and Y, i.e. the elements of X together with any element of Y not occurring in X. If X or Y contain duplicates, Z may also contain duplicates. 64 $absmember(X, L) Similar to $member/2, except that it checks for identity (through ==/2) rather than unifiability (through =/2) of X with elements of L. $nthmember(X, L, N) Succeeds if the N[th.] element of the list L unifies with X. Fails if N is greater than the length of L. Either X and L, or L and N, should be instantiated at the time of the call. $member2(X, L) Checks whether X unifies with any of the actual elements of L. The only difference between this and $member/2 is on lists with a variable tail, e.g. [a, b, c | _ ]: while $member/2 would insert X at the end of such a list if it did not find it, $member2/2 only checks for membership but does not insert it into the list if it is not there. length(L, N) Succeeds if the length of the list L is N. This predicate is deterministic if L is instantiated to a list of definite length, but is nondeterministic if L is a variable or has a variable tail. subsumes(X, Y) Succeeds if the term X subsumes the term Y (i.e. if Y is an instance of X). 65 CREDITS The initial development of SB-Prolog, from 1984 to August 1986, was at SUNY at Stony Brook, where Versions 1.0 and 2.0 were developed. Since August 1986, its development has continued at the University of Arizona, Tucson. A large number of people were involved, at some time or another, with the Logic Programming group at SUNY, Stony Brook, and deserve credit for helping to bring SB-Prolog to its present form. David Scott Warren led the project at Stony Brook. Most of the simulator and builtins were written by Jiyang Xu and David S. Warren (I added the later stuff, Versions 2.1 onwards). Much of the library was also by David, with some contributions from me. Weidong Chen did the work on clause indexing. Suzanne Dietrich wrote the Extension Table package. I wrote most of the compiler. Several people helped debug previous versions, including Leslie Rohde; Bob Beck of Sequent Computers; and Mark Gooley of the University of Illinois at Urbana-Champaign. Special thanks are due to Richard O'Keefe, who contributed the Prolog code for the parser (in the form of the predicates read/1 and read/2), the C code for the tokenizer, and the code for setof/3 and bagof/3. I am grateful to Fernando Pereira for permission to use material from the C-Prolog manual for the descriptions of Prolog syntax and many of the builtins in this User Manual. - S.K.D. The MS-DOS version of the system was compiled using version 1.05 of the MS-DOS 386 GNU C compiler port by D. J. Delorie . The portability of the whole system made the MS-DOS port an easy task. - dds 66 Appendix 1: Evaluable Predicates of SB-Prolog An entry of ``B'' indicates a builtin predicate, ``I'' an inline predicate, and ``L'' a library predicate. A ``P'' indi- cates that the predicate is handled by the preprocessor during compilation and/or consulting. A ``D'' denotes a compiler direc- tive. (L) $db_assert_fact/5 _$undefined_pred/1 .................... 42 .................... 5 (L) (P) :-/1 ........... 8 $db_add_clref/7 (B) $exists/1 ...... 17 .................... 42 (I) =:=/2 .......... 21 (L) (I) =\=/2 .......... 21 $db_call_prref/2 (I) /2 ............ 21 (L) (I) ==/2 ........... 21 .................... 42 (I) `,'/2 .......... 23 (L) (I) `;'/2 .......... 23 $db_get_clauses/3 (I) =/2 ............ 23 .................... 43 (I) \=/2 ........ 23 (L) $trace/0 ....... 46 (I) ?=/2 ........ 23 (L) $untrace/0 ..... 46 (P) !/0 ............ 23 (L) `_$inter- (P) ->/2 ........... 24 rupt'/2 ............ 48 (L) =../2 .......... 25 (P) ::-/2 .......... 55 (B) ==/2 ........... 28 (L) $reverse/2 ..... 64 (B) \==/2 .......... 28 (L) $merge/3 ....... 64 (B) @/2 ........... 28 .................... 65 (B) @==/2 .......... 29 .................... 65 (L) $current_atom/2 (B) atom/1 ......... 24 .................... 35 (B) atomic/1 ....... 25 (L) (I) arg/3 .......... 25 $current_functor/3 (L) alloc_perm/2 .................... 35 .................... 30 (L) (L) alloc_heap/2 $current_predicate/3 .................... 30 .................... 35 (L) assert/1 ....... 31 (L) $getenv/2 ...... 37 (L) assert/2 ....... 31 (L) $alloc_buff/5 (L) asserti/2 ...... 31 .................... 40 (L) asserta/1 ...... 31 (L) (L) asserta/2 ...... 31 $assertf_alloc_t (L) assertz/1 ...... 32 .................... 41 (L) assertz/2 ...... 32 (L) (L) $db_new_prref/3 assert_union/2 ..... 32 .................... 41 (L) assert/4 ....... 32 (L) (L) abolish/1 ...... 33 67 (L) abolish/2 ...... 33 (B) floor/2 ......... 22 (B) abort/0 ........ 37 (B) floatc/3 ........ 22 (L) append/3 ....... 64 (I) fail/0 ......... 24 (I) float/1 ......... 24 (L) bagof/3 ........ 27 (L) functor/3 ...... 25 (L) break/0 ........ 36 (B) get0/1 ......... 18 (L) compile/1 ...... 5 (B) get/1 .......... 18 (L) compile/2 ...... 5 (L) globalset/1 (L) compile/3 ...... 5 .................... 39 (L) compile/4 ...... 5 (L) gennum/1 ....... 40 (L) consult/1 ...... 7 (L) gensym/2 ....... 40 (L) consult/2 ...... 7 (P) call/1 ......... 26 (I) is/2 ........... 21 (B) conlength/2 (I) integer/1 ...... 24 .................... 26 (B) is_buffer/1 (B) compare/3 ...... 29 .................... 25 (L) clause/2 ....... 33 (L) instance/2 ..... 34 (L) clause/3 ....... 33 (D) index/3 ........ 53 (L) current_atom/1 ..... 35 (L) keysort/2 ...... 29 (L) current_functor/2 (B) load/1 ......... 6 .................... 35 (L) listing/0 ...... 34 (L) (L) listing/1 ...... 34 current_predicate/2 (L) length/2 ....... 65 .................... 35 (B) cputime/1 ...... 37 (D) mode/3 ......... 51 (L) call_ref/2 ..... 41 (L) member/2 ....... 64 (L) call_ref/3 ..... 41 (L) `C'/3 .......... 61 (B) nl/0 ........... 18 (L) count/1 ........ 62 (P) not/1 .......... 23 (L) countpreds/1 (I) nonvar/1 ....... 24 .................... 63 (B) number/1 ....... 24 (B) name/2 ......... 25 (L) display/1 ...... 17 (L) nodynload/2 (L) debug/0 ........ 45 .................... 38 (L) debugging/0 (L) nospy/1 ........ 45 .................... 45 (L) nodebug/0 ...... 45 (L) dcg/2 .......... 60 (L) noet/1 ......... 58 (L) nocount/1 ...... 62 (L) eval/2 ......... 21 (L) notime/1 ....... 62 (B) exp/2 ........... 22 (L) noprofile/0 (L) erase/1 ........ 34 .................... 63 (L) et/1 ........... 58 (L) et_star/1 ...... 58 (L) op/3 ........... 12 (L) et_points/1 .................... 58 (L) print/1 ........ 17 (L) et_remove/1 (L) print_al/2 ..... 18 .................... 59 (L) print_ar/2 ..... 18 (L) et_calls/2 ..... 59 (L) (L) expand_term/2 portray_term/2 ..... 18 .................... 60 (L) portray_clause/2 68 .................... 18 (L) trace/1 ........ 43 (B) put/1 .......... 18 (L) tracepreds/1 (L) .................... 45 predicate_property/2 (U) .................... 36 term_expansion/2 (L) phrase/2 ....... 60 .................... 61 (L) profiling/0 (L) time/1 ......... 62 .................... 62 (L) timepreds/1 (L) prof_reset/1 .................... 63 .................... 63 (L) profile/0 ...... 63 (L) untrace/1 ...... 45 (L) prof_stats/0 .................... 63 (I) var/1 .......... 24 (L) prof_stats/1 .................... 64 (L) write/1 ........ 17 (L) writeq/1 ....... 17 (L) read/1 ......... 17 (B) writename/1 (L) repeat/0 ....... 24 .................... 17 (I) real/1 .......... 24 (B) writeqname/1 (L) retract/1 ...... 33 .................... 18 (L) recorded/3 ..... 33 (L) recorda/3 ...... 34 (L) recordz/3 ...... 34 (B) restore/1 ....... 37 (L) resetcount/1 .................... 63 (L) resettime/1 .................... 63 (B) see/1 .......... 16 (B) seen ........... 17 (B) square/2 ........ 22 (B) sin/2 ........... 22 (B) structure/1 ..... 25 (L) setof/3 ........ 27 (L) sort/2 .......... 29 (B) save/1 .......... 37 (B) statistics/0 .................... 37 (L) statistics/2 .................... 37 (B) symtype/2 ...... 38 (B) system/1 ....... 39 (B) syscall/3 ...... 39 (L) spy/1 .......... 45 (L) spypreds/1 ..... 45 (L) subsumes/2 ..... 65 (B) tell/1 ......... 17 (B) telling/1 ...... 17 (B) told/0 ......... 17 (B) tab/1 .......... 19 (I) true/0 ......... 23 (L) trimbuff/3 ..... 30 69 Appendix 2: A Note on Coding for Efficiency The SB-Prolog system tends to favour programs that are rela- tively pure. Thus, for example, asserts tend to be quite expen- sive, encouraging the user to avoid them if possible. This sec- tion points out some syntactic constructs that lead to the gen- eration of efficient code. These involve (i) avoiding the crea- tion of backtrack points; and (ii) minimizing data movement between registers. Optimization of logic programs is an area of ongoing research, and we expect to enhance the capabilities of the system further in future versions. 1. Avoiding Creation of Backtrack Points Since the creation of backtrack points is relatively expensive, program efficiency may be improved substantially by using con- structs that avoid the creation of backtrack points where possi- ble. The SB-Prolog compiler recognizes conditionals involving certain complementary inline tests, and generates code that does not create choice points for such cases. Two inline tests p(t<1>, . . ., t) and q(t<1>, . . ., t) are complementary if and only if p(t<1>, . . ., t) _ not(q(t<1>, . . ., t)). For example, the literals `X > Y' and `X =< Y' are complementary. At this point, complementary tests are recognized as such only if their argument tuples are identical. The inline predicates that are treated in this manner, with their corresponding complemen- tary literals, are shown in Table 3. The syntactic constructs recognized are: (i) Disjuncts of the form head( . . . ) :- (( test(t<1>, . . ., t), . . . ) ; (( not(test(t<1>. . . ., t), . . .)). or head( . . . ) :- (( test(t<1>, . . ., t), . . . ) ; (( comp_test(t<1>. . . ., t), . . .)). where test is one of the inline tests in the table above, and comp_test the corresponding complementary test (note that the arguments to test and comp_test have to be identi- cal). (ii) Conditionals of the form head :- (test<1>, ... , test) -> True_Case ; False_Case. or head :- (test<1>; ... ; test) -> True_Case ; False_Case. where each test is an inline test, as mentioned in the table above. 70 _________________________________________ |____Inline_Test____|__Complementary_Test| | >/2 | ==/2 | (U1 = [E|U1a], U2 = U2a) ; (U1 = U1a, U2 = [E|U2a])), part(M,L,U1a,U2a). or part(M,[E|L],U1,U2) :- ((E =< M, U1 = [E|U1a], U2 = U2a) ; (E > M, U1 = U1a, U2 = [E|U2a])), part(M,L,U1a,U2a). Thus, either of the two later versions will be more efficient 71 than the version with the explicit cut (this is a design decision we have consciously made, in the hope of discouraging blatantly non-declarative code where efficient declarative code can be written). 2. Minimizing Data Movement Between Registers Data movement between registers for parameter passing may be minimized by leaving variables in the same argument position wherever possible. Thus, the clause p(X,Y) :- p1(X,Y,0). is preferable to p(X,Y) :- p1(0,X,Y). because the first definition leaves the variables X and Y in the same argument positions (first and second, respectively), while the second definition does not. 3. Processing All Arguments of a Term It is often the case that we wish to process each of the argu- ments of a term in turn. For example, to decide whether a com- pound term is ground, we have to check that each of its arguments is ground. One possibility is to create a list of those argu- ments, and traverse the list processing each element. Using this approach, a predicate to check for groundness would be ground(T) :- atomic(T). ground(T) :- structure(T), T =.. [_ | Args], groundargs(Args). groundargs([]). groundargs([A | ARest]) :- ground(A), groundargs(ARest). This is not the most efficient way to process all the arguments of a term, because it involves the creation of intermediate lists, which is expensive both in space and time. A much better alternative is to use arg/3 to index into the term and retrieve arguments. Using this approach, the ground/1 predicate above would be written as ground(T) :- atomic(T). ground(T) :- structure(T), functor(T, P, N), groundargs(1, N, T). groundargs(M, N, T) :- M =< N -> (arg(M, T, A), ground(A), M1 is M + 1, groundargs(M1, N, T)) ; true. The second approach is likely to be more efficient than the first in SB-Prolog. 72 If the arguments of the term do not need to be processed in ascending order, then it is more efficient to process them in descending order using arg/3 to access them. For example, the predicate for groundness checking could be written as ground(T) :- atomic(T). ground(T) :- structure(T), functor(T, P, N), groundargs(N, T). groundargs(M, T) :- M =:= 0 -> true ; (arg(M, T, A), ground(A), M1 is M - 1, groundargs(M1, T)). This is even more efficient than the earlier version, because (i) groundargs needs to have one less parameter to be passed to it at each iteration; and (ii) testing ``M =:= 0'' is simpler and more efficient than checking ``M =< N'', and takes fewer machine instructions. 4. Testing Unifiability Often, it is necessary to check whether or not a term has a par- ticular value. If we know that the term will be bound to a number, we can use the evaluable predicates =:=/2 or =\=/2, as explained earlier. For other values, it may often be cheaper, in the appropriate circumstances, to use the predicates ?=/2 or \=/2. For example, consider a predicate p/2 that calls q/1 with its second argument if its first argument unifies with a, and r/1 otherwise. A naive definition might be p(a, X) :- !, q(X). p(Y, X) :- r(X). However, the call to p/2 results in the (temporary) creation of a backtrack point. A solution that avoids this backtrack point creation is p(Y, X) :- Y ?= a -> q(X) ; r(X). Of course, if the argument order in p/2 could be reversed in this case, then data movement would be reduced even further (see above), and the code would be even more efficient: p(X, Y) :- Y ?= a -> q(X) ; r(X). 73 Appendix 3: Adding Builtins to SB-Prolog - Adding a builtin involves writing the C code for the desired case and installing it into the simulator. The files in the irectory sim/builtin contain the C code for the builtin predicates sup- ported by the system. The following procedure is to be followed when adding a builtin to the system: I. Installing C Code: (1) Go to the directory sim/builtin. (2) Look at the #defines in the file builtin.h, and choose a number N1 (between 0 and 255) which is not in use to be the builtin number for the new builtin. (3) Add to the file builtin.h the line #define NEWBUILTIN N1 (4) The convention is that the code for builtin will be in a parameterless procedure named b_NEWBUILTIN. Modify the file init_branch.c in the directory sim/builtin by adding these lines: extern int b_NEWBUILTIN(); and set_b_inst ( NEWBUILTIN, b_NEWBUILTIN ); in the appropriate places. (5) The builtins are compiled together into one object file, builtin. Update the file Makefile by appending the name of your object code file at the end of the line ``OBJS = ... '' and insert the appropriate commands to compile your C source file, e.g.: OBJS = [ ... other file names ... ] newbuiltin.o ... newbuiltin.o: $(HS) cc $(CFLAGS) newbuiltin.c (6) Execute the updated make file to create an updated object file builtin. ____________________ - You will need the full source of SB-Prolog to do this. 74 (7) Go to the directory sim and execute make to install the new file builtin. II. Installing Prolog Code: Assume that the builtin predicate to be added is newbuil- tin/4. The procedure for installing the Prolog code for this is as follows: (1) Go to the SB-Prolog system directory lib/src, where the Pro- log source for the library routines is kept. (2) Each builtin definition is of the form pred( ... ) :- '_$builtin'(N). where N is an integer, the builtin number of pred. (3) Create a Prolog source file newbuiltin.P (notice correspon- dence with the name of the predicate being defined) contain- ing the definition newbuiltin(A,B,C,D) :- '_$builtin'(N1). where N1 is the builtin number of the predicate newbuiltin, obtained when installing the C code for the builtin (see above). (4) Compile this Prolog predicate, using the simulator and the compile predicate, into a file newbuiltin (notice correspon- dence with the name of the predicate being defined) in the SB-Prolog directory lib. Appendix 4: Compiling SB-Prolog under MS-DOS In order to compile SB-Prolog under MS-DOS you will need DJGPP (the GNU C compiler for 386 MS-DOS available by anonymous ftp from grape.ecs.clarkson.edu:pub/msdos/djgpp) and the full source for the SB-Prolog system (available from the same place you got this version or cs.arizona.edu:sbprolog/v3). The file diffs contains unofficial patches for compiling SB Prolog ver- sion 3.1 on a 386 MS-DOS system. In order to use them you must first unpack all the sources from the compressed tar files. The tar I am using can uncompress on the fly with the -z flag. It was distributed in the Usenet comp.binaries.ibm.pc newsgroup dur- ing August 1991. If you haven't got such a beast do "compress -dc file | tar" or do it in two steps. Tar will probably have a 75 problem writing the two aux.[ch] files. This can be solved in four different ways: o Use a clever tar that will rename those files before unpack- ing. o Patch the tar file and the checksums. o Respond by "fail" on the "abort retry fail prompt" and copy, the files separately from a Unix system. o Patch the MS-DOS kernel changing the aux device name to something different. (It works!) After copying them rename them to _aux.c and _aux.h. When you have the source tree in one place run patch to install the changes needed to run the system on MS-DOS. The file diffs must be fed into patch using input redirection. If you have not got patch you will have to do them by hand. Then copy the frexp.c file into the sim directory as the frexp routine in the C library has a small bug in it and causes the prolog com- piler to crash. Now you are ready to compile. Go into the directory sim/builtin and type make (using a un*x compatible make such as pdmake) and then do the same in the sim directory. You will need the latest version (at least patchlevel 5) of DJ Delorie's port of gcc for MS-DOS and the associated tools. After you have com- piled set the environment variables needed (see the file sbp.bat for an exanple) and run the system using the "go32 sbprolog" com- mand. The makefile contains an example for creating a single executable file under the "install" rule. 76 TABLE OF CONTENTS 1 : Introduction ........................................ 2 2 : Getting Started ..................................... 2 2.1 : The Dynamic Loader Search Path .................. 2 2.2 : System Directories .............................. 4 2.3 : Invoking the Simulator .......................... 4 2.4 : Executing Programs .............................. 5 2.4.1 : Compiling Programs ......................... 5 2.4.2 : Loading Byte Code Files .................... 6 2.4.3 : Consulting Programs ........................ 7 2.5 : Execution Directives ............................ 8 3 : Syntax .............................................. 8 3.1 : Terms ........................................... 8 3.2 : Operators ....................................... 11 4 : SB-Prolog: Operational Semantics .................... 14 4.1 : Standard Execution Behaviour .................... 14 4.2 : Cuts and If-Then-Else ........................... 14 4.3 : Unification of Floating Point Numbers ........... 15 5 : Evaluable Predicates ................................ 15 5.1 : Input and Output ................................ 16 5.1.1 : File Handling .............................. 16 5.1.2 : Term I/O ................................... 17 5.1.3 : Character I/O .............................. 18 5.2 : Arithmetic ...................................... 19 5.3 : Convenience ..................................... 23 5.4 : Extra Control ................................... 23 5.5 : Meta-Logical .................................... 24 5.6 : Sets ............................................ 26 5.7 : Comparison of Terms ............................. 27 5.8 : Buffers ......................................... 29 5.9 : Modification of the Program ..................... 30 5.10 : Internal Database .............................. 33 5.11 : Information about the State of the Program ..... 34 5.12 : Environmental .................................. 36 5.13 : Global Values .................................. 39 5.14 : Exotica ........................................ 40 6 : Debugging ........................................... 43 6.1 : High Level Tracing .............................. 43 6.2 : Low Level Tracing ............................... 46 7 : The Simulator ....................................... 46 7.1 : Invoking the Simulator .......................... 46 7.2 : Simulator Options ............................... 47 7.3 : Interrupts ...................................... 48 8 : The Compiler ........................................ 49 8.1 : Invoking the Compiler ........................... 49 8.2 : Compiler Options ................................ 50 8.3 : Assembly ........................................ 51 8.4 : Compiler Directives ............................. 51 8.4.1 : Mode Declarations .......................... 51 8.4.2 : Indexing Directives ........................ 52 9 : Libraries ........................................... 53 10 : Macros ............................................. 54 10.1 : Defining Macros ................................ 55 10.2 : Macro Expander Options ......................... 55 11 : Extension Tables: Memo Relations ................... 56 12 : Definite Clause Grammars ........................... 59 13 : Profiling Programs ................................. 61 14 : Other Library Utilities ............................ 64 Credits .................................................... 66 Appendix 1: Evaluable Predicates of SB-Prolog .............. 67 Appendix 2: A Note on Coding for Efficiency ................ 70 1 : Avoiding Creation of Backtrack Points ............... 70 2 : Minimizing Data Movement Between Registers .......... 72 3 : Processing All Arguments of a Term .................. 72 4 : Testing Unifiability ................................ 73 Appendix 3: Adding Builtins to SB-Prolog - ................. 74 Appendix 4: Compiling SB-Prolog under MS-DOS ............... 75