Node:Query Processing, Next:Query Handling Predicates, Previous:Message Handling Predicates, Up:Messages and Queries
All user input in the Prolog system is handled by a single predicate:
ask_query(+QueryClass, +Query, +Help, -Answer)
QueryClass, described below, specifies the form of the query interaction. Query is an abstract message term specifying the query text, Help is an abstract message term used as a help message in certain cases, and Answer is the (abstract) result of the query.
ask_query/4
is a built-in predicate, so that users can invoke it
to have their own queries processed in the same way as the system
queries.
The processing of queries is highly customizable. For example, this allows changing the language of the input expected from the user, or to make queries appear in dialog windows rather than on the terminal.
Queries posed by the system can be classified according to the kind of input they expect, the way the input is processed, etc. Queries of the same kind form a query class.
For example, queries requiring a yes/no answer form a query class with the following characteristics:
(y or n)
is used as the prompt;
yes
or no
, respectively, as the abstract answer;
There are built-in query classes for reading in yes/no answers, toplevel queries, debugger commands, etc.
A query class is characterized by a ground Prolog term, which is
supplied as the first argument to the query processing predicate
ask_query/4
. The characteristics of a query class are normally
described by the extendible predicate
'SU_messages':query_class(+QueryClass, -Prompt, -InputMethod, -MapMethod, -FailureMode).
The arguments of the query_class
predicate have the following
meaning:
For example, a built-in input method is described by the atom
line
. This requests that a line is input from the user, and the
list of character codes is returned. Another built-in input method is
term(Options)
; here, a Prolog term is read and returned.
The input obtained using InputMethod is called raw input, as it may undergo further processing.
In addition to the built-in input methods, the user can define his/her own extensions.
For example, the built-in map method char([yes-"yY", no-"nN"])
expects a list of character codes as raw input, and gives the answer
term yes
or no
depending on the first non-whitespace
character of the input. As another example, the built-in map method
=
requests that the raw input itself is returned as the answer
term--this is often used in conjunction with the input method
term(Options)
.
In addition to the built-in map methods the user can define his/her own extensions.
This is used only when the mapping of raw input fails, and
the query must be repeated. This happens for example if the user typed
a character other than y or n in case of the
yes_or_no
query class. FailureMode determines what to
print before re-querying the user. Possible values are:
help_query
: print a help message, then print the text of
the query again
help
: only print the help message
query
: only print the text of the query
none
: do not print anything
Query processing is done in several phases, described below. We will
illustrate what is done in each phase through a simple example: the
question put to the user when the solution to the toplevel query X
is 1+1
is displayed, requesting a decision whether to find alternative
answers or not:
| ?- X is 1+1. X = 2 ? no Please enter ";" for more choices; otherwise, <return> ? ;
We focus on the query X = 2 ?
in the above script.
The example query belongs to the class next_solution
, its text is
described by the message term solutions([binding("X",2)])
, and
its help text by the message term bindings_help
. Accordingly,
such a query is executed by calling:
ask_query(next_solution, /* QueryClass */ solutions([binding("X",2)]), /* Query */ bindings_help, /* Help */ Answer)
In general, execution of ask_query(+QueryClass, +Query,
+Help, -Answer)
consists of the following phases:
query
severity). HelpLines may be printed later, and QueryLines
printed again, in case of invalid user input.
The characteristics of QueryClass (described in the previous subsubsection) are retrieved to control the exact behavior of the further phases.
In our example, the following parameters are set in the preparation phase:
QueryLines | = | [[],['~s = '-["X"],write_term(2)]]
|
HelpLines | = |
|
[['Please enter ";" for more choices; otherwise, <return>'-[]]]
Prompt | = | ' ? '
|
InputMethod | = | line
|
MapMethod | = | char([yes-";", no-[0'\n]])
|
FailureMode | = | help
|
QueryLines is displayed immediately, printing:
X = 2
(Note that the first element of QueryLines is []
, therefore
the output is preceded by a newline. Also note that no newline is
printed at the end of the last line, because the query
severity
is used.)
The subsequent phases will be called repeatedly until the mapping phase succeeds in generating an answer.
'SU_messages':query_input(+InputMethod, +Prompt, -RawInput).
This phase uses the Prompt and InputMethod characteristics of the query class. InputMethod specifies the method of obtaining input from the user. This method is executed, and the result (RawInput) is passed on to the next phase.
The use of Prompt may depend on InputMethod. For example,
the built-in input method line
prints the prompt unconditionally,
while the input method term(_)
passes Prompt to
prompt/2
.
In the example, first the ?
prompt is displayed. Next, because
InputMethod is line
, a line of input is read, and the
list of character codes is returned in RawInput. Supposing that
the user typed no<RET>
, RawInput becomes " no"
=
[32,110,111]
.
'SU_messages':query_map(+MapMethod, +RawInput, -Result, -Answer).
This phase uses the MapMethod parameter to control the method of converting the raw input to the abstract answer.
In some cases RawInput is returned as it is, but otherwise it has to be processed (parsed) to generate the answer.
The conversion process may have two outcomes indicated in the Result returned:
In the latter case a message describing the cause of failure may be returned, to be printed before the query is repeated.
In our example, the map method is char([yes-";", no-[0'\n]])
.
The mapping phase fails for the RawInput passed on by the previous
phase of the example, as the first non-whitespace character is n,
which does not match any of the given characters.
First, if a message was returned by the mapping, then it is printed. Subsequently, if requested by the FailureMode parameter, the help message HelpLines and/or the text of the query QueryLines is printed.
The query is then repeated--the input and mapping phase will be called again to try to get a valid answer.
In the above example, the user typed an invalid character, so the
mapping failed. The char(_)
mapping does not return any
message in case of failure. The FailureMode of the query class
is help
, so the help message HelpLines is
printed, but the query is not repeated:
Please enter ";" for more choices; otherwise, <return>
Having completed the query restart phase, the example script continues
by re-entering the input phase: the prompt ?
is printed,
another line is read, and is processed by the mapping phase. If the
user types the character ; this time, then the mapping phase
returns successfully and gives the abstract answer term yes
.
As explained above, the major parts of query processing are implemented
in the 'SU_messages'
module in the file
library('SU_messages')
through the following extendible
predicates:
'SU_messages':query_class(+QueryClass, -Prompt,
-InputMethod, -MapMethod, -FailureMode)
'SU_messages':query_input(+InputMethod, +Prompt,
-RawInput)
'SU_messages':query_map(+MapMethod, +RawInput,
-Result, -Answer)
This is to enable the user to change the language used, the processing
done, etc., simply by changing or replacing the
library('SU_messages')
file.
To give more control to the user and to make the system more robust (for
example if the 'SU_messages'
module is corrupt) the so-called
four step procedure is used in the above three cases--obtaining
the query class parameters, performing the query input and performing
the mapping. The four steps of this procedure, described below, are
tried in the given order until the first one that succeeds. Note that
if an exception is raised within the first three steps, then a warning
is printed and the step is considered to have failed.
_hook
to
it, e.g. user:query_class_hook/5
in case of the query class.
If this hook predicate exists and succeeds, then it is assumed to have
done all necessary processing, and the following steps are skipped.
'SU_messages'
module is called
(this is the default case, these are the predicates listed above).
Normally this should succeed, unless the module is corrupt, or an
unknown query-class/input-method/map-method is encountered.
These predicates are extendible, so new classes and methods can be added
easily by the user.
'SU_messages'
is called. This is necessary because the
library('SU_messages')
file is modifiable by the user, therefore
vital parts of the Prolog system (e.g. the toplevel query) could be
damaged.
The following InputMethod types are implemented by the default
'SU_messages':query_input(+InputMethod, +Prompt,
-RawInput)
(and these are the input methods known to the third,
fall-back step):
line
read_line/2
and the list of character codes is returned as
RawInput.
term(Options)
prompt/2
), and a Prolog
term is read by read_term/2
using the given Options, and is
returned as RawInput.
FinalTerm^term(Term,Options)
T-Vs^term(T,[variable_names(Vs)])
input
method will return the term read, paired with the list of variable
names.
The following MapMethod types are known to
'SU_messages':query_map(+MapMethod, +RawInput,
-Result, -Answer)
and to the built-in fall-back mapping:
char(Pairs)
Pairs is a list of Name-Abbreviations
pairs,
where Name is a ground term, and Abbreviations is a list of
character codes. The first non-layout character of RawInput is
used for finding the corresponding name as the answer, by looking it up
in the abbreviation lists. If the character is found, then Result
is success
, and Answer is set to the Name found;
otherwise, Result is failure
.
=
success
.
debugger
unknown(Line,Warning)
is returned. This is to allow the user to
extend the debugger command language via debugger_command_hook/2
,
see Debug Commands.
The details of this mapping can be obtained from the
library('SU_messages')
file.
Note that the fall-back version of this mapping is simplified, it only accepts parameterless debugger commands.
Most of the default query classes are designed to support some specific
interaction with the user within the Prolog development environment. The
full list of query classes can be inspected in the file
library('SU_messages')
. Here, we only describe the two classes
defined by 'SU_messages':query_class/5
that may be of general
use:
QueryClass | yes_or_no | yes_no_proceed
|
Prompt | ' (y or n) ' | ' (y, n, p, s, a, or ?) '
|
InputMethod | line | line
|
MapMethod | char([yes-"yY", no-"nN"])
| char([yes-"yY", no-"nN", proceed-"pP", suppress-"sS", abort-"aA"])
|
FailureMode | help_query | help_query
|