Alexander Kopilovitch
aek@acm.org, aek@vib.usr.pu.ru
subtype VString is Ada.Strings.Unbounded.Unbounded_String; Nul : VString renames Ada.Strings.Unbounded.Null_Unbounded_String;Also, throughout this Manual a predicate's instance (in a Prolog program) means a dynamic instance, that is, a non-backtracking call of the predicate.
Open
function (for a
Prolog session, see section 3.1 below) or an instantiation
of the Prolog.Library
generic package (for a Logical Extension Library, see
section 4.1 below).
The predicate table is an array of predicate descriptors. Each descriptor is a row (record) in the predicate table. That row describes the predicate usage for the Logic Server (tells it the predicate's functor and arity) and for the TAP bindings themselves (specifies the predicate's kind), and provides an access to the function that actually computes the predicate. The predicate table is supposed to be declared with the following way:
PTable : constant Predicate_Table := ( 1st predicate-descriptor, 2nd predicate-descriptor, ... );A predicate-descriptor is a record with the following four fields:
To_Unbounded_String
function;Pure_Predicate
Generator_Shared
Generator_Reenterable
Here is an example of the predicate-descriptor:
(+"hit", 2, Pure_Predicate, Find_And_Hit'Access)
Call_Context_Error
exception is raised
(with the EM_Int_1
message) when this rule is violated.
There are 3 subroutines that deal with the predicate's parameters inside the
predicate: Get_Parameter_Mode
, Get_Parameter
and Set_Parameter
.
The first argument for these functions is always the index of the predicate's
parameter to deal with. The Call_Argument_Error
exception (with the EM_Index
message) is raised when that argument appears greater than actual number of the
predicate's parameters.
The function Get_Parameter_Mode
tells whether the investigated parameter
is in input mode (that is, contains a value) or it is in output mode (that is, can
accept a value). In the former case this function returns the Mode_In
constant,
in the latter case it returns the Mode_Out
constant. Here is the declaration
of this function:
function Get_Parameter_Mode( Index : in Positive ) return Parameter_Mode;The function
Get_Parameter
returns current string value of the parameter.
It raises the Call_Access_Error
exception when applied to a parameter in
output mode (with the EM_Get
message in this case) or when it can't retrieve the
parameter's value for another reason (with the EM_Ret message in this case).
Here is the declaration of this function:
function Get_Parameter( Index : in Positive ) return VString;Note, that in a case of a complex term (that is, not a string, atom or number) in a parameter, there is a limitation for the resulting string length, though not too restrictive (see
Term_String_Length_Limit
constant in the private part of the Prolog
package
spec; the value of this constant corresponds to the default value of the readbuffer configuration
parameter for the Amzi Logic Server).
The procedure Set_Parameter
sets new value for the paraneter. That new value
is passed to the procedure as its second argument. The third argument of the
procedure tells the Logic Server how to interpret the string value -- either
as an Prolog atom or as a string literal. For the former interpretation
Meaning => Name
should be used, and Meaning => Literal
-- for the latter. The Call_Access_Error
exception (with the EM_Set
message) is raised at an attempt to set a value to non-variable parameter
(that is, to a parameter in input mode). Here are two declarations of this procedure (one
for String
argument, and another -- for VString
, that is,
Unbounded_String
argument):
procedure Set_Parameter( Index : in Positive; Value : in String; Meaning : in Prolog_String );
procedure Set_Parameter( Index : in Positive; Value : in VString; Meaning : in Prolog_String );The procedure
Get_Predicate_Profile
extracts the functor (name), arity
(number of arguments) and the kind of the predicate itself. Here is the declaration
of this procedure:
procedure Get_Predicate_Profile( Functor : out VString; Arity : out Natural; Kind : out Predicate_Kind );The function
Get_Call_Purpose
tells whether current call of the predicate
is the first call of this predicate instance or not. In the former case it returns
the Call_Start
constant, and in the latter case (which includes all backtracking
calls) it returns Call_Continue
constant. Here is the declaration of this
function:
function Get_Call_Purpose return Call_Purpose;The function
Get_Instance_Id
returns the number of this predicate instance
(among all instances of the current predicate). It is useful for the predicates
of the Generator_Reenterable
kind only, because for other tho kinds of the
predicates this function always returns 1. Here is the declaration of this
function:
function Get_Instance_Id return Positive;
Additionally, an Ada program that creates a Prolog session may also implement the external predicates. In other words, the Logic Server may use the external predicates from the program that creates the session, as well as from the dedicated Logical Extension Libraries.
So, there are two basic configurations of an application that combines an Ada program with a Prolog program using the Logic Server. In the first of these configurations, the Ada program is the main program of the application; it creates a Prolog session, telling the Logic Server three things:
In the second basic configuration, the main program of the application is a Prolog program (actually, the Logic Server, which runs that Prolog program). The Logic Server loads the Logical Extension Libraries for that Prolog program, using the names/locations supplied in the separate configuration file. When the Logic Server loads a Logical Extension Library, it issues the initialization call to the library. In response to that call, the library supplies the description of its external predicates -- the predicate table. At the second initialization call from the Logic Server, the library possibly asserts some additional rules (that is, adds them to the Prolog database), which facilitate the use of the supplied external predicates.
An Ada program may create several Prolog sessions (with different Prolog programs), and use them simultaneously.
The Open function takes from two to four arguments: the first argument is
a string - the filename of a Prolog program to be loaded (if that filename
does not include a suffix then ".xpl
" is appended to it as the default suffix);
the second argument is access to a predicate table (or No_Extended_Predicates
constant, if the program does not supply extended predicates). Optional third
argument is an Unbounded_String
that presents the names (separated by commas)
of Logical Library Extensions to be loaded together with the Prolog program.
Optional fourth argument is an Unbounded_String
that presents the configuration
parameters for the Logic Server (see Amzi Logic server documentation for them).
The function returns a value of the private type Session. That value identifies
the session, and must be passed as the first argument within all other calls
of the Logic Server except of the functions dedicated to the use from inside
the external predicates.
Here is the declaration of the Open
function:
function Open( Program_Name : in String; Predicates : in Predicate_Table; Libraries : in VString := Nul; Configuration_Parameters: in VString := Nul ) return Session;
The Close procedure has single argument - the identification of the Prolog
session. The procedure invalidates that session identification. Here is the
declaration of the Close
procedure:
procedure Close(Process: in out Session); -- the argument becomes null as a result
Apply
function should be called, and a Prolog
"goal" string must be passed as the second argument within the invocation.
The first argument of the call must be the session identification (that was
received as the result of the Open
function), and the third argument is a
Boolean permission for subsequent retries (provided that the original Apply
succeeded).
The Apply
function returns True
if the "goal" succeeded in the Prolog program,
otherwise it returns False
.
The "goal" string may contain Prolog variables. When the Apply
succeeds, those
variables obtain their values (become bound in the Prolog terminology), and
those values may be retrieved for inspection using the Display_All
and
Display_Variable
functions (see Section 3.3 below).
Here is the declaration of the Apply
function:
function Apply( -- Prolog.Synonyms.Query Process : in Session; -- session identification Cause : in String; -- Prolog "goal" Multi_Step : in Boolean := true -- permission for Proceed ) return Boolean;As already mentioned in this section, the "goal" may be retried, provided that previous call of the
Apply
function succeeded and the Multi_Step
argument for
that call was True
. The Proceed
function exists for that purpose.
The Proceed
function accepts single argument -- the session identification.
It returns True
or False
, like the Apply
function.
When it succeeds (that is, returns True
), new values are assigned ("bound")
to the Prolog variables within the "goal". The Proceed
function may be called
repeatedly while it succeeds. Here is the declaration of the Proceed
function:
function Proceed(Process: in Session) -- Prolog.Synonyms.Retry return Boolean;
Apply
and
Proceed
functions, a caller may also inspect the detailed results by retrieving
the string values of the variables embedded into the "goal" string.
Two functions are provided for this purpose: Display_Variable
and Display_All
.
Also, the third function, Count_Variables
is provided for convenience.
All those three functions may be called only after Apply
, provided that the
recent Apply
or Proceed
returned True
.
If the latter condition is violated then the Call_Context_Error
exception
(with the EM_Disp
message) occurs.
All those functions take the session identification as the first argument.
The Display_Variable
function takes a variable's name as its second argument,
and returns the string value of the variable. If there is no variable with the
name supplied then the Call_Argument_Error
exception (with the EM_Var
message)
occurs. Here is the declaration of the Display_Variable
function:
function Display_Variable( Process : in Session; Name : in String ) return VString; -- value of variableThe
Display_All
function returns the string that contains a sequence of the
"name=value" pairs for all variables that participate in the "goal" string.
Using the second argument, the caller may provide own separator string, which
will be inserted between those pairs. Here is the declaration of the Display_All
function:
function Display_All( Process : in Session; Separator : in String := ", " ) return VString; -- consists of the name=value pairs with the -- Separator between themThe
Count_Variables
function returns the number of Prolog variables in the
"goal" string. Here is the declaration of the Count_Variables
function:
function Count_Variables(Process : in Session) return Integer;
Prepend
, Append
,
and Remove
. All them take the session identification as the first
argument, and the Prolog clause to be added or deleted - as the second argument.
The manipulations on the Prolog dynamic database generally require a closer look at the Prolog side of the session than it is necessary for other interactions with the Prolog program within a session. For the simple cases the proceedings are quite straightforward, but there may be problems with the more complicated ones. First, if the Prolog program uses modules then some preliminary measures are needed (in the Prolog program) that facilitate an addition of substantially new clauses (see Amzi Logic Server documentation for that). Second, a deletion uses Prolog's unification algorithm (and not the exact comparison of the string representation of the terms) during the search, so it cannot be guaranteed without a care at the Prolog side that the deleted clause will be intended one.
The procedure Prepend
inserts a Prolog clause at the beginning of the Prolog
database. Here is the declaration of the Prepend
procedure:
procedure Prepend( -- Prolog.Synonyms.Assert_A Target : in Session; Clause : in String );The procedure
Append
inserts a Prolog clause at the end of the Prolog database.
Here is the declaration of the Append
procedure:
procedure Append( -- Prolog.Synonyms.Assert_Z Target : in Session; Clause : in String );The function
Remove
deletes a Prolog clause from the Prolog dynamic database.
It returns True
if a clause was successfully deleted, and False
otherwise
(that is, if it did not find an approriate clause to delete). Note, that
the second argument - the clause presented for deletion - is considered not
as exact search key, but as the pattern for the Prolog's unification; thus
a careless deletion may produce unpleasant surprises if the Prolog database
contains several fitting candidates. Here is the declaration of the Remove
function.
function Remove( -- Prolog.Synonyms.Retract Target : in Session; Clause : in String ) return Boolean;
amzi.dll
.
Therefore, that DLL must be on the search path at run time, and an Ada program
that creates a Prolog session must be linked with an appropriate import library.
The import library libamzi.a
, which is supplied with the TAP distribution,
should be used for linking, instead of the standard amzi.lib
from the Amzi
Logic Server installation. The latter cannot be used for that purpose due to
uncommon combination of the call convention and the entry point names in
amzi.dll
(which are reflected in amzi.lib
, and are incompatible with the code
geneterated by the GNAT compiler). So, the libamzi.a
from the TAP distribution,
and not the amzi.lib
from the Amzi Logic Server installation, must be present
on the linker's search path.
In anticipation of possible new releases of Amzi Logic Server, and consequently,
new versions of amzi.dll
, the method for creation of libamzi.a
(from amzi.dll
) is provided here. Given the amzi.dll
and
the file amzi.def
(the latter is included in the TAP distribution),
the following command produces libamzi.a
:
gnatdll -d amzi.dll -e amzi.def -k(
gnatdll
is a tool from the GNAT installation; note the switch -k
,
it is crucial in that special case).
InitPreds
and LSAPI_Ready
, which will be called
by the Logic Server within the process of the Logical Extension Library
initialization. The InitPreds
function is required by the Amzi Logic
Server (see the docs in its installation), while LSAPI_Ready
function
is required by the TAP bindings for the proper initialization of the
predicates of the Generator_Shared
and Generator_Reenterable
kind.
The rules for the predicate table in a Logic Extension Library are the same as those for the predicate table already described for use with a Prolog session (see Section 1.1 above).
The exported functions InitPreds
and LSAPI_Ready
are standard with the TAP
bindings, and should not be changed unless there is a special need for that.
The only problem with them is that the InitPreds
function body needs the
predicate table, which certainly can't be universal. Therefore, those two
functions are placed together into generic child package Prolog.Library
, and
the predicate table is made its single parameter.
So, for providing the required exported functions, it is sufficient (and
recommended), to instantiate (in the Logical Extension Library package spec,
after the predicate table) that Prolog.Library
package with the name of the
predicate table as its parameter (the package name used for the instantiation
does not matter).
MyLSX.o
, MyLSX.ali
,
the following command builds the MyLSX.dll
:
gnatdll -d MyLSX.dll -e AdaLSX.def MyLSX.ali -nwhere the switch -n prevents creation of the import library
libMyLSX.a
(that import library would be of no use, so there is no reason to build it).
The AdaLSX.def
file is provided in the TAP distribution, and has the
following contents:
EXPORTS InitPreds=InitPreds@8 LSAPI_Ready=LSAPI_Ready@8
Logic_Server_Exception | -- the Logic Server signals an exceptional condition |
String_Overflow | -- the size of a string argument is greater than established maximum |
Call_Argument_Error | -- improper argument value |
Call_Access_Error | -- attempt to retrieve Mode_Out
parameter or to set Mode_In parameter |
Call_Context_Error | -- a subroutine is called out of proper
context, e.g., Proceed without prior Apply or
Get_Parameter from outside of a predicate function etc. |
Call_Environment_Error | -- a subroutine is called from inside an incompatible environment, that is, from a main program instead of a predicate library or vice versa. |
Each time when one of those exceptions is raised, an appropriate message is
associated with it. All those messages are declared individually, as the
constant strings, in the Prolog package. Therefore the identifiers of those
messages may be used as a tightening of an exceptional case, when an advanced
diagnostic is needed inside a program. All those exception message identifiers
have the prefix EM_
. See the Prolog package spec for the full list of those
messages.
Prolog.Lists
provides the facilities for dealing with Prolog
lists in the parameters of an external predicate. These facilities rely upon the
VString_Array
type as the Ada representative for Prolog lists. Here is the
declaration of this type:
type VString_Array is array(Integer range <>) of VString;A homogeneous linear Prolog list (that is, a list without sub-lists, and in which either all elements are names/atoms or all them are (back)quoted literals) may be directly translated to a parameter of an external predicate from a
VString_Array
vector using the additional form of the
Set_Parameter
procedure, provided within this package. This procedure accepts an array of
VString_Array
type, converts it to a Prolog list, and sets the predicate's parameter
accordingly. Here is the declaration of this procedure:
procedure Set_Parameter( Index : in Positive; Value : in VString_Array; Meaning : in Prolog_String );Note, though, that you cannot use this procedure for heterogeneous or non-linear lists (for example, you cannot intermix in the list names with literals, and you cannot directly build trees with a single call of this procedure).
Alternatively, you may convert a VString_Array
vector to the string representaion of the
corresponding Prolog list with the function Format_Linear_List
(provided within this package),
and then assign the resulting string to the predicate's parameter using the basic form of the
Set_Parameter
procedure. The Format_Linear_List
function takes a
VString_Array
vector, converts it to the string representation of the corresponding Prolog list,
(perhaps quoting the elements of the list, according to the second argument), and returns that string. Here
is the declaration of this function:
function Format_Linear_List( Value : in VString_Array; Meaning : in Prolog_String ) return VString;Note, that it is possible to create non-linear (and to some degree, heterogeneous) lists using multiple calls of this
Format_Linear_List
function, that is, submitting the result of a previous call to
the subsequent call (as an element of the vector argument).
For dealing with input parameters of external predicates, four subroutines are provided within
this package: Is_List
function, Count_Linear_List
function, and two forms of
Extract_Linear_List
subroutine -- procedural and functional. All those subroutines take the
string argument, and try to treat it as the string representation of a Prolog list (which may be non-linear,
but the subroutines do not dive into the deeper levels, except for the syntax check). So, for dealing with
a list, which is the contents of an input parameter of an external predicate, you should first take that
contents as a string (VString
) using the Get_Parameter
function, and then apply
these subroutines to that string.
The function Is_List
tells whether its argument is a correct string
representation of a Prolog list (perhaps non-linear) -- it returns True
in the
case, and False
otherwise. Check a string with this function before submitting it to the
Count_Linear_List
and Extract_Linear_List
subroutines, to be safe from the possible
List_Format_Error
exception (unless the proper list syntax is guaranteed by other
means). Also, this function facilitates the multi-step extraction of a non-linear list, which is mentioned
below, at the end of this section. Here is the declaration of this function:
function Is_List( Value : in VString ) return Boolean;The function
Count_Linear_List
assumes that its argument is a string
representation of a Prolog list, and returns the number of elements in that list (counting the upper level
only). This number may be zero, indicating an empty list. Here is the declaration of this function:
function Count_Linear_List( Value : in VString ) return Natural;The function
Extract_Linear_List
assumes that its argument is a string
representation of a Prolog list, and returns its contents as an array of VString_Array
type
(considering the upper level only, that is, returning a sub-list of the list as its full string
representation, assigning it to a single element of the output vector).
Here is the declaration of this function:
function Extract_Linear_List( Value : in VString ) return VString_Array;The procedure
Extract_Linear_List
is a procedural form of the Extract_Linear_List
function described above. The only difference from the latter is the way of returning the resulting vector::
in this procedure the vector is returned via the second, output parameter. Here is the declaration of this
procedure:
procedure Extract_Linear_List( Value : in VString; Result : out VString_Array );Note, that using the
Extract_Linear_List
function or procedure you can extract the contents of
non-linear lists (e.g. trees) -- calling it separately for each sub-list extracted (as a string -- an element
of the array) by the previous call.
Unify_Parameter
is for use inside an external predicate. It
provides an opportunity to imitate an IN OUT mode for a predicate's parameter.
Also, it exploits the general unification algorithm (instead of mere copying) for
setting the parameter's value. The first argument of the procedure is the
parameter's index (exactly as with other subroutines that deal with the
predicate parameters) and the second argument is a string representation of
a term to be unified with the current value of the parameter. Here is the
declaration of the Unify_Parameter
procedure:
procedure Unify_Parameter( Index : in Positive; Value : in String );A whole result of a Prolog query as an unified term may be retrieved using the function
Get_Unified
, which should be called for that (if needed) immediately after a successful Apply
or Proceed call.
The function Get_Unified
takes the session identification
as its single argument, and returns the string representation of the unified
term. Here is the declaration of the Get_Unified
function:
function Get_Unified(Process: Session) return VString;Both
Unify_Parameter
and Get_Unified
subroutines are provided within the child
package Prolog.Unification
.
Prolog.Synonyms
contains the renamings for several functions from
the Prolog
package. Those renamings provide the names that are customary
to the experienced Prolog programmers. Here is the table of those synonyms:
synonym | renames |
---|---|
Query | Apply |
Retry | Proceed |
Assert_A | Prepend |
Assert_Z | Append |
Retract | Remove |
To use those synonyms in your own Ada package, make sure that your package has the following context clause:
with Prolog.Synonyms;and perhaps, the corresponding "use" clause:
use Prolog.Synonyms;Note, that the names of parameters of
Query
and Retry
functions
are different from those used in Apply
and Proceed
:
Query/Retry | Apply/Proceed |
---|---|
Database | Process |
Goal | Cause |
Retry_Permitted | Multi_Step |
ext_
.
Here is the whole list of those reserved names:
ext_display ext_gate ext_exec_* (where * is the name (functor) of some external predicate) ext_cont_* (where * is the name (functor) of some external predicate).