There are two functions in this category: Prolog extended to be able to evaluate Tcl expressions in a Tcl interpreter; Tcl extended to be able to evaluate a Prolog expression in the Prolog system.
There is a mechanism for describing Tcl commands in Prolog as Prolog terms. This is used in two ways: firstly, to be able to represent Tcl commands in Prolog so that they can be subsequently passed to Tcl for evaluation; and secondly for passing terms back from Tcl to Prolog by doing the reverse transformation.
Why not represent a Tcl command as a simple atom or string? This can indeed be done, but commands are often not static and each time they are called require slightly different parameters. This means constructing different atoms or strings for each command in Prolog, which are expensive operations. A better solution is to represent a Tcl command as a Prolog term, something that can be quickly and efficiently constructed and stored by a Prolog system. Variable parts to a Tcl command (for example command arguments) can be passed in through Prolog variables.
In the special command format, a Tcl command is specified as follows.
Command | ::= Name
| |
| chars( code-list)
| ||
| write( term)
| ||
| writeq( term)
| ||
| write_canonical( term)
| ||
| format( Fmt, Args)
| ||
| dq( Command)
| ||
| br( Command)
| ||
| sqb( Command)
| ||
| min( Command)
| ||
| dot( ListOfNames)
| ||
| list( ListOfCommands)
| ||
| ListOfCommands
| ||
Fmt | ::= atom
| |
Name | ::= atom | { other than [] }
|
| number
| ||
ListOfCommands | ::= []
| |
| [ Command | ListOfCommands ]
| ||
ListOfNames | ::= []
| |
| [ Name | ListOfNames ]
| ||
Args | ::= []
| |
| [ term | Args ]
|
where
chars(
PrologString)
write(
Term)
writeq(
Term)
write_canonical(
Term)
Please note: In general it is not possible to reconstruct Term from the string printed bywrite/1
. If Term will be passed back into Prolog it therefore safest to usewrite_canonical(
Term)
.
format(
Fmt,
Args)
dq(
Command)
br(
Command)
sqb(
Command)
min(
Command)
dot(
ListOfName)
list(
ListOfCommands)
br(
ListOfCommands)
when any of the elements contains spaces, braces or other characters treated
specially by TCL.
list(
ListOfCommands)
is a better choice.
Examples of command specifications and the resulting Tcl code:
[set, x, 32] ⇒ set x 32
[set, x, br([a, b, c])] ⇒ set x {a b c}
[dot([panel,value_info,name]), configure, min(text), br(write('$display'/1))] ⇒ .panel.value_info.name configure -text {$display/1
['foo bar',baz] ⇒foo bar baz
list(['foo bar',bar]) ⇒ {foo bar} baz
list(['foo { bar'',bar]) ⇒ foo\ \{ \bar baz
Prolog calls Tcl through the predicate tcl_eval/3
, which
has the following form:
tcl_eval(+TclInterpreter, +Command, -Result)
which causes the interpreter TclInterpreter to evaluate the Tcl command Command and return the result Result. The result is a string (a code-list) that is the usual return string from evaluating a Tcl command. Command is not just a simple Tcl command string (although that is a possibility) but a Tcl command represented as a Prolog term in the special Command Format (see Evaluation Functions).
Through tcl_eval/3
, Prolog has a method of synchronous
communication with an embedded Tcl interpreter and a way of manipulating
the state of the interpreter.
An example:
| ?- tcl_new(Interp), tcl_eval(Interp, 'set x 1', _), tcl_eval(Interp, 'incr x', R).
which creates a Tcl interpreter the handle of which is stored in the
variable Interp
. Then variable x
is set to the value "1"
and then variable x
is incremented and the result returned in
R
as a string. The result will be "2". By evaluating the Tcl
commands in separate tcl_eval/3
calls, we show that we are
manipulating the state of the Tcl interpreter and that it remembers its
state between manipulations.
It is worth mentioning here also that because of the possibility
of the Tcl command causing an error to occur in the Tcl interpreter,
two new exceptions are added by the tcltk
library:
tcl_error(Goal, Message) tk_error(Goal, Message)
where Message is a code-list detailing the reason for the
exception. Also two new user:portray_message/2
rules are
provided so that any such uncaught exceptions are displayed at the
Prolog top-level as
[TCL ERROR: Goal - Message] [TK ERROR: Goal - Message]
respectively.
These exception conditions can be raised/caught/displayed in the usual
way through the built-in predicates raise_exception/3
,
on_exception/1
, and portray_message/2
.
As an example, the following Prolog code will raise such an exception:
| ?- tcl_new(X), tcl_eval(X, 'wilbert', R).
which causes a tcl_error/2
exception and prints the following:
{TCL ERROR: tcl_eval/3 - invalid command name "wilbert"}
assuming that there is no command or procedure defined in Tcl called
wilbert
.
The Tcl interpreters created through the SICStus Prolog Tcl/Tk library have been extended to allow calls to the underlying Prolog system.
To evaluate a Prolog expression in the Prolog system from a Tcl
interpreter, the new prolog
Tcl command is invoked. It has the
following form:
prolog PrologGoal
where PrologGoal is the printed form of a Prolog goal. This
causes the goal to be executed in Prolog. It will be executed in
the user
module unless it is prefixed by a module
name. Execution is always determinate.
The return value of the command either of the following:
"1"
"0"
If succeeded (and "1" was returned), any variable in
PrologGoal that has become bound to a Prolog term will
be returned to Tcl in the Tcl array named prolog_variables
with
the variable name as index. The term is converted to Tcl using the
same conversion as used for Tcl commands (see Evaluation Functions). As a
special case the values of unbound variables and variables with
names starting with ‘_’, are not recorded and need not conform to
the special command format, this is similar to the threatment of such
variables by the Prolog top-level.
An example:
test_callback(Result) :- tcl_new(Interp), tcl_eval(Interp, 'if {[prolog "foo(X,Y,Z)"] == 1} \\ {list $prolog_variables(X) \\ $prolog_variables(Y) \\ $prolog_variables(Z)}', Result), tcl_delete(Interp). foo(1, bar, [a, b, c]).
When called with the query:
| ?- test_callback(Result).
will succeed, binding the variable Result
to:
"1 bar {a b c}"
This is because execution of the tcl_eval/3
predicate
causes the execution of the prolog
command in Tcl, which executes
foo(X, Y, Z)
in Prolog making the following bindings:
X = 1
, Y = bar
, Z = [a, b, c]
. The bindings
are returned to Tcl in the associative array prolog_variables
where prolog_variables(X)
is "1", prolog_variables(Y)
is
"bar", and prolog_variables(Z)
is "a b c". Then Tcl goes on to
execute the list
command as
list "1" "bar" "a b c"
which returns the result
"1 bar {a b c}"
(remember: nested lists magically get represented with curly brackets)
which is the string returned in the Result part of the Tcl call,
and is ultimately returned in the Result
variable of the
top-level call to test_callback(Result)
.
If an error occurs during execution of the prolog
Tcl command, a
tcl_error/2
exception will be raised. The message part of the
exception will be formed from the string ‘Exception during Prolog
execution: ’ appended to the Prolog exception message. An example is
the following:
| ?- tcl_new(T), tcl_eval(T, 'prolog wilbert', R).
which will print
{TCL ERROR: tcl_eval/3 - Exception during Prolog execution: wilbert existence_error(wilbert,0,procedure,user:wilbert/0,0)}
at the Prolog top-level, assuming that the predicate
wilbert/0
is not defined on the Prolog side of the system. (This
is a tcl_error
exception containing information about the
underlying exception, an existence_error
exception, which was
caused by trying to execute the non-existent predicate
wilbert
.)