GProlog, the GNU ISO-Compliant Implementation of Prolog
On a Ubuntu Linux distro, the documentation for GNU Prolog is found in the following subdirectories and files:
- /usr/share/doc/gprolog-doc/gprolog.html
- /usr/share/doc/gprolog-doc/gprolog.pdf
- /usr/share/doc/gprolog-doc/
- /usr/share/doc/gprolog-doc/examples/
The GNU Prolog Interactive Interpreter
Starting/exiting the interactive interpreter
GNU Prolog offers a classical Prolog interactive interpreter also called top-level. It allows the user to execute queries, to consult Prolog programs, to list them, to execute them and to debug them. The top-level can be invoked using the following command:
% gprolog [OPTION] . . .
where the %
symbol is the operating system shell prompt.
Options:
--init-goal GOAL | execute GOAL before entering the top-level |
--consult-file FILE | consult FILE inside the top-level |
--entry-goal GOAL | execute GOAL inside the top-level |
--query-goal GOAL | execute GOAL as a query for the top-level |
--help | print a help and exit |
--version | print version number and exit |
-- | do not parse the rest of the command-line |
The main role of the gprolog command is to execute the top-level itself, i.e. to execute the built-in predicate top_level/0
, which will produce something like:
GNU Prolog 1.4.0 By Daniel Diaz Copyright (C) 1999-2018 Daniel Diaz | ?-
To quit the top-level type the end-of-file key sequence (Ctl-D) or its term representation:
end_of_file.
It is also possible to use the built-in predicate halt/0
Here is an example of using execution goal options (bash). Note that no space character follows the comma character:
gprolog --init-goal write\(before\),nl --entry-goal write\(inside\),nl --query-goal append\([a,b],[c,d],X\)
Which produces output:
beforeGNU Prolog 1.4.5 (64 bits)
Compiled Feb 23 2020, 20:14:50 with gcc
By Daniel Diaz
Copyright (C) 1999-2020 Daniel Diaz
inside
| ?- append([a,b],[c,d],X).
X = [a,b,c,d]
yes
| ?-
The interactive interpreter read-execute-write loop
The GNU Prolog top-level is built on a classical read-execute-write loop that also allows for re-executions (when the query is not deterministic) as follows:
- display the prompt, i.e. ’| ?-’.
- read a query (i.e. a goal).
- execute the query.
- in case of success display the values of the variables of the query.
- if there are remaining alternatives (i.e. the query is not deterministic), display a ? and ask the user, who can use one of the following commands: <RETURN> to stop the execution, ; to compute the next solution or a to compute all remaining solution.
Here is an example of execution of a query (find the lists X and Y such that the concatenation of X and Y is [a,b]):
| ?- append(X,Y,[a,b,c]). | |
X = [] | (here the user presses ; to compute another solution) |
X = [a] | (here the user presses a to compute all remaining solutions) |
X = [a,b] | (here the user is not asked and the next solution is computed) |
X = [a,b,c] | |
no | (no more solutions) |
The user can stop the execution even if there are more alternatives by typing <RETURN>
| ?- (X=1 ; X=2). X = 1 ? yes
The top-level tries to display the values of the variables of the query in a readable manner. For instance, when a variable is bound to a query variable, the name of this variable appears. When a variable is a singleton an underscore symbol is displayed ("_" is a generic name for a singleton variable, it is also called an anonymous variable). Other variables are bound to new brand variable names. When a query variable name X
appears as the value of another query variable Y
it is because X
is itself not instantiated otherwise the value of X
is displayed. In such a case, nothing is output for X
itself (since it is a variable). Example:
| ?- X=f(A,B,_,A), A=k. | |
A = k | (the value of A is displayed also in f/3 for X) |
X = f(k,B,_,k) | (since B is a variable which is also a part of X, B is not displayed) |
| ?- functor(T,f,3), arg(1,T,X), arg(3,T,X) | |
T = f(X,_,X) | (the 1st and 3rd args are equal to X, the 2nd is an anonymous variable) |
read_from_atom('k(X,Y,X).',T). | |
T = k(A,_,A) | (the 1st and 3rd args are unified, a new variable name A is introduced) |
Finally, the top-level computes the user-time (section 8.24.2, page 154) taken by a query and displays it when it is significant. Example:
retractall('p(_)'), assertz('p(0)'), repeat, retract('p(X)'), Y is X + 1, assertz('p(Y)'), X = 1000, !.
which returns:
X = 1000 Y = 1001 (180 ms) yes(the query took 180ms of user time)
Consulting a Prolog program
The top-level allows the user to consult Prolog source files. Consulted predicates can be listed, executed and debugged (while predicates compiled to native-code cannot).
To consult a program use the built-in predicate consult/1
. The argument of this predicate is a Prolog file name or user to specify the terminal. This allows the user to directly input the predicates from the terminal. In that case the input shall be terminated by the end-of-file key sequence (Ctl-D) or its term representation: end_of_file
. A shorthand for consult(FILE ) is [FILE]
. Example:
| ?- [user]. {compiling user for byte code...} even(0). even(s(s(X))):- even(X). {user compiled, 3 lines read - 350 bytes written, 1180 ms} | ?- even(X). X = 0 ?; X = s(s(0)) ? ; X = s(s(s(s(0)))) ? yes | ?- listing. even(0). even(s(s(A))) :- even(A).
When consult/1
is invoked on a Prolog file it first runs the GNU Prolog compiler as a child process to generate a temporary WAM file for byte-code. If the compilation fails a message is displayed and nothing is loaded. If the compilation succeeds, the produced file is loaded into memory using load/1
. Namely, the byte-code of each predicate is loaded. When a predicate P is loaded, if there is a previous definition for P it is removed (i.e. all clauses defining P are erased). We say that P is redefined. Note that only consult
-ed predicates can be redefined. If P is a native-code predicate, trying to redefine it will produce an error at load-time: the predicate redefinition will be ignored and the following message displayed:
native code procedure P cannot be redefined
Finally, an existing predicate will not be removed if it is not re-loaded. This means that if a predicate P is loaded when consulting the file F, and if later the definition of P is removed from the file F, consulting F again will not remove the previously loaded definition of P from memory.
Consulted predicates can be debugged using the Prolog debugger. Use the debugger predicate trace/0
or debug/0
to activate the debugger.
Scripting Prolog
Since version 1.4.0 it is possible to use a Prolog source file as a Unix script-file (shebang support). A PrologScript file should begin as follows:
#!/usr/bin/gprolog --consult-file
GNU Prolog will be invoked as
/usr/bin/gprolog --consult-file FILE
Then FILE will be consulted. In order to correctly deal with the #!
first line, consult/1
treats as a comment a first line of a file which begins with # (if you want to use a predicate name starting with a #, simply skip a line before its definition).
Remark: it is almost never possible to pass additionnal parameters (e.g. query-goal) this way since in most systems the shebang implementation deliver all arguments (following #!/usr/bin/gprolog) as a single string (which cannot then correctly be recognized by gprolog).
Interrupting a query*
Under the top-level it is possible to interrupt the execution of a query by typing the interruption key (Ctl-C). This can be used to abort a query, to stop an infinite loop, to activate the debugger,. . . When an interruption occurs the top-level displays the following message: Prolog interruption (h for help) ?
The user can then type one of the following commands:
The line editor*
The line editor (linedit
) allows the user to build/update the current input line using a variety of commands. This facility is available if the linedit part of GNU Prolog has been installed. linedit is implicitly called by any built-in predicate reading from a terminal (e.g. get char/1, read/1,. . . ). This is the case when the top-level reads a query.
Bindings: each command of linedit is activated using a key. For some commands another key is also available to invoke the command (on some terminals this other key may not work properly while the primary key always works). Here is the list of available commands:
Adjusting the size of Prolog data*
GNU Prolog uses several stacks to execute a Prolog program. Each stack has a static size and cannot be dynamically increased during the execution. For each stack there is a default size but the user can define a new size by setting an environment variable. When a GNU Prolog program is run it first consults these variables and if they are not defined uses the default sizes. The following table presents each stack of GNU Prolog with its default size and the name of its associated environment variable.
Since version 1.4.2, the size of the atom table (the table recording all atoms) is managed similarly to stacks. It is then included in the following table (even if actually it is not a stack but an hash table). In this table, the associated name is atoms
which is the key used in statistics. The environment variable name is derived from the corresponding Prolog flag max_atom
.
The GNU Prolog Compiler
Different kinds of codes
One of the main advantages of GNU Prolog is its ability to produce stand alone executables. A Prolog program can be compiled to native code to give rise to a machine-dependent executable using the GNU Prolog compiler. However native-code predicates cannot be listed nor fully debugged. So there is an alternative to native-code compilation: byte-code compilation. By default the GNU Prolog compiler produces native-code but via a command-line option it can produce a file ready for byte-code loading. This happens to be what consult/1
does as explained above. GNU Prolog also manages interpreted code using a Prolog interpreter written in Prolog. Obviously interpreted code is slower than byte-code but does not require the invocation of the GNU Prolog compiler. This interpreter is used each time a meta-call is needed as by call/1
. This also the case of dynamically asserted clauses. The following table summarizes these three kinds of codes:
Type | Speed | Debugable? | What it is for |
interpreted-code | slow | yes | meta-call and dynamically asserted clauses |
byte-code | medium | yes | consulted predicates |
native-code | fast | no | compiled predicates |