Previous Up Next

4.2  The GNU Prolog interactive interpreter

4.2.1  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]…  (the % symbol is the operating system shell prompt)

Options:

--init-goal GOALexecute GOAL before entering the top-level
--consult-file FILEconsult FILE inside the top-level
--entry-goal GOALexecute GOAL inside the top-level
--query-goal GOALexecute GOAL as a query for the top-level
--helpprint a help and exit
--versionprint 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 (section 8.18.1) which will produce something like:

GNU Prolog 1.5.0 (64 bits)
Compiled May  3 2021, 16:36:43 with gcc
Copyright (C) 1999-2021 Daniel Diaz

| ?- 

The top-level is ready to execute your queries as explained in the next section.

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 (section 8.18.1).

However, before entering the top-level itself, the command-line is processed to treat all known options (those listed above). All unrecognized arguments are collected together to form the argument list which will be available using argument_value/2 (section 8.27.2) or argument_list/1 (section 8.27.3). The -- option stops the parsing of the command-line, all remainding options are collected into the argument list.

Several options are provided to execute a goal before entering the interaction with the user:

The above order is thus the order in which each kind of goal (init, entry, query) is executed. If there are several goals of a same kind they are executed in the order of appearance. Thus, all init goals are executed (in the order of appearance) before all entry goals and all entry goals are executed before all query goals.

Each GOAL is passed as a shell argument (i.e. one shell string) and should not contain a terminal dot. Example: --init-goal ’write(hello), nl’ under a sh-like. To be executed, a GOAL is transformed into a term using read_term_from_atom(Goal, Term, [end_of_term(eof)]). Respecting both the syntax of shell strings and of Prolog can be heavy. For instance, passing a backslash character \ can be difficult since it introduces an escape sequence both in sh and inside Prolog quoted atoms. The use of back quotes can then be useful since, by default, no escape sequence is processed inside back quotes (this behavior can be controlled using the back_quotes Prolog flag (section 8.22.1)).

Since the Prolog argument list is created when the whole command-line is parsed, if a --init-goal option uses argument_value/2 or argument_list/1 it will obtained the original command-line arguments (i.e. including all recognized arguments).

Here is an example of using execution goal options:

% gprolog --init-goal ’write(before), nl’ --entry-goal ’write(inside), nl’
--query-goal ’append([a,b],[c,d],X)’

will produce the following:

before
GNU Prolog 1.5.0 (64 bits)
Compiled May  3 2021, 16:36:43 with gcc
Copyright (C) 1999-2021 Daniel Diaz

inside
| ?- append([a,b],[c,d],X).

X = [a,b,c,d]

yes
| ?-

NB: depending on the used shell it may be necessary to use other string delimiters (e.g. use " under Windows cmd.exe).

4.2.2  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:

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 = []
Y = [a,b,c] ? ;  (here the user presses ; to compute another solution)
 
X = [a]
Y = [b,c] ? a  (here the user presses a to compute all remaining solutions)
 
X = [a,b]
Y = [c]  (here the user is not asked and the next solution is computed)
 
X = [a,b,c]
Y = []  (here the user is not asked and the next solution is computed)
 
no  (no more solution)

In some cases the top-level can detect that the current solution is the last one (no more alternatives remaining). In such a case it does not display the ? symbol (and does not ask the user). Example:

| ?- (X=1 ; X=2).
 
X = 1 ? ;  (here the user presses ; to compute another solution)
 
X = 2  (here the user is not prompted since there are no more alternatives)
 
yes

The user can stop the execution even if there are more alternatives by typing RETURN.

| ?- (X=1 ; X=2).
 
X = 1 ?  (here the user presses RETURN to stop the execution)
 
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)

The top-level uses variable binding predicates (section 8.5). To display the value of a variable, the top-level calls write_term/3 with the following option list: [quoted(true),numbervars(false), namevars(true)] (section 8.14.6). A term of the form ’$VARNAME’(Name) where Name is an atom is displayed as a variable name while a term of the form ’$VAR’(N) where N is an integer is displayed as a normal compound term (such a term could be output as a variable name by write_term/3). Example:

| ?- X=’$VARNAME’(’Y’), Y=’$VAR’(1).
 
X = Y  (the term ’$VARNAME’(’Y’) is displayed as Y)
Y = ’$VAR’(1)  (the term ’$VAR’(1) is displayed as is)
| ?- X=Y, Y=’$VAR’(1).
 
X = ’$VAR’(1)
Y = ’$VAR’(1)

In the first example, X is explicitly bound to ’$VARNAME’(’Y’) by the query so the top-level displays Y as the value of X. Y is unified with ’$VAR’(1) so the top-level displays it as a normal compound term. It should be clear that X is not bound to Y (whereas it is in the second query). This behavior should be kept in mind when doing variable binding operations.

Finally, the top-level computes the user-time (section 8.24.2) 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, !.
 
X = 1000
Y = 1001
 
(180 ms) yes  (the query took 180ms of user time)

4.2.3  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). For more information about the difference between a native-code predicate and a consulted predicate refer to the introduction of this section (section 4.1) and to the part devoted to the compiler (section 4.4.1).

To consult a program use the built-in predicate consult/1 (section 8.23.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).
   (here the user presses Ctl-D to end the input)
{user compiled, 3 lines read - 350 bytes written, 1180 ms}
 
| ?- even(X).
 
X = 0 ? ;  (here the user presses ; to compute another solution)
 
X = s(s(0)) ? ;  (here the user presses ; to compute another solution)
 
X = s(s(s(s(0)))) ?  (here the user presses RETURN to stop the execution)
 
yes
| ?- listing.
 
even(0).
even(s(s(A))) :-
        even(A).

When consult/1 (section 8.23.1) is invoked on a Prolog file it first runs the GNU Prolog compiler (section 4.4) 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 (section 8.23.2). 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 consulted 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 the memory.

Consulted predicates can be debugged using the Prolog debugger. Use the debugger predicate trace/0 or debug/0 (section 5.3.1) to activate the debugger.

4.2.4  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).

4.2.5  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:

CommandNameDescription
aabortabort the current execution. Same as abort/0 (section 8.18.1)
eexitquit the current Prolog process. Same as halt/0 (section 8.18.1)
bbreakinvoke a recursive top-level. Same as break/0 (section 8.18.1)
ccontinueresume the execution
ttracestart the debugger using trace/0 (section 5.3.1)
ddebugstart the debugger using debug/0 (section 5.3.1)
h or ?helpdisplay a summary of available commands

4.2.6  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:

KeyAlternate keyDescription
Ctl-Bgo to the previous character
Ctl-Fgo to the next character
Esc-BCtl-go to the previous word
Esc-FCtl-go to the next word
Ctl-AHomego to the beginning of the line
Ctl-EEndgo to the end of the line
Ctl-HBackspacedelete the previous character
Ctl-DDeletedelete the current character
Ctl-UCtl-Homedelete from beginning of the line to the current character
Ctl-KCtl-Enddelete from the current character to the end of the line
Esc-L lower case the next word
Esc-U upper case the next word
Esc-C capitalize the next word
Ctl-T exchange last two characters
Ctl-VInsertswitch on/off the insert/replace mode
Ctl-ITabcomplete word (twice displays all possible completions)
Esc-Ctl-IEsc-Tabinsert spaces to emulate a tabulation
Ctl-space mark beginning of the selection
Esc-W copy (from the begin selection mark to the current character)
Ctl-W cut (from the begin selection mark to the current character)
Ctl-Y paste
Ctl-Precall previous history line
Ctl-Nrecall next history line
Esc-P recall previous history line beginning with the current prefix
Esc-N recall next history line beginning with the current prefix
Esc-<Page Uprecall first history line
Esc->Page Downrecall last history line
Ctl-C generate an interrupt signal (section 4.2.5)
Ctl-D generate an end-of-file character (at the begin of the line)
RETURN validate a line
Esc-? display a summary of available commands

History: when a line is entered (i.e. terminated by RETURN), linedit records it in an internal list called history. It is later possible to recall history lines using appropriate commands (e.g. Ctl-P recall the last entered line) and to modify them as needed. It is also possible to recall a history line beginning with a given prefix. For instance to recall the previous line beginning with write simply type write followed by Esc-P. Another Esc-P will recall an earlier line beginning with write,…

Completion: another important feature of linedit is its completion facility. Indeed, linedit maintains a list of known words and uses it to complete the prefix of a word. Initially this list contains all predefined atoms and the atoms corresponding to available predicates. This list is dynamically updated when a new atom appears in the system (whether read at the top-level, created with a built-in predicate, associated with a new consulted predicate,…). When the completion key (Tab) is pressed linedit acts as follows:

Example:

| ?- argu  (here the user presses Tab to complete the word)
| ?- argument_  (linedit completes argu with argument_ and emits a beep)
   (the user presses again Tab to see all possible completions)
argument_counter  (linedit shows 3 possible completions)
argument_list
argument_value
| ?- argument_  (linedit redisplays the input line)
 
| ?- argument_c  (to select argument_counter the user presses c and Tab)
| ?- argument_counter  (linedit completes with argument_counter)

Balancing: linedit allows the user to check that (square/curly) brackets are well balanced. For this, when a close bracket symbol, i.e. ), ] or }, is typed, linedit determines the associated open bracket, i.e. (, [ or {, and temporarily repositions the cursor on it to show the match.

Customization: the behavior of linedit can be controlled via an environment variable called LINEDIT. This variable can contain the following substrings:

nodo not activated linedit (should the only value of the variable)
ansi=nodo not use ANSI escape sequence (unix only)
out=Nuse the file descriptor N for the output (unix only)
gui=noeven if compiled with the GUI console run in text mode (windows)
gui=silentif the GUI console is not found, silently run in text mode (windows)
cp=Nuse code page N (windows text console)
oem_put=nodo not use Char→Oem conversion when emitting a char (windows text console)
oem_get=nodo not use Oem→Char conversion when reading a char (windows text console)

Copyright (C) 1999-2021 Daniel Diaz Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved. More about the copyright
Previous Up Next