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_PredicateGenerator_SharedGenerator_ReenterableHere 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 variable
The 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 them
The 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).