Embedding a standalone prolog interperter in Tcl

If the following prolog code is saved in a file called interp.pl, then it can be executed with SWI-Prolog [1] with the command line

 plcon -s interp.pl -t main -q

(plcon is the name of the console executable on windows--On Unix use pl, on MacOSX use swipl).

The code will issue the prompt prompt: to stdout. Prolog terms will be read from stdin. The terms are allow to either be assertions via the assert functor, or queries that contain either 0 or one variable. If there are no variables then the result of a successful query will be displayed as a single line containing "1". A failed query will display "0". If there is one variable, then the results will be displayed as a Tcl list using curly braces. A failed query will display the empty list {}.

The Tcl code follows the prolog code.


 displayListAsTcl([]).
 displayListAsTcl([H|T]):-
    writef('{%w} ', [H]),
    displayListAsTcl(T).

 % mycall is a wrapper around call that will display results of queries
 % as a Tcl list

 % No variables means simple true (1) or false (0) output
 mycall(Goal,[]):-
    call(Goal),
    write('1\n').
 mycall(_Goal,[]):-
    write('0\n').

 % If there is a single variable then return a list of the results
 mycall(Goal,[Var]):-
    %free_variables(Goal,Var),
    findall(Var,Goal,List),
    write('{'),
    displayListAsTcl(List),
    write('}\n').

 mycall(_Goal,[_Var]):-
    write('{}\n').

 % More than one variable is not allowed
 mycall(_Goal,[_Var1,_Var2|_Tail]):-
    write('ERROR: Queries cannot have more than one unbound variable\n').

 % Database insertions are also allowed
 mycall(assert(X)):-
    assert(X),
    write('asserted\n').

 mycall(Goal):-
    free_variables(Goal,_Vars),
    mycall(Goal,_Vars).

 handleError(Error):-
    writef('ERROR: %w\n',[Error]),
    main.

 main:-
    prompt(_Old,'prompt:\n'),
    read(Term),
    catch(mycall(Term),Error,handleError(Error)),
    main.

 % Prevent the above main to exit
 % when the call fails
 main:-
    main.

The Tcl code:

 namespace eval prolog {

 proc init {} {
    variable prologInterp
    set prologInterp  [open "|c:/apps/prolog/bin/plcon -s interp.pl -t main -q" r+]
    set prompt [gets $prologInterp]
    fconfigure $prologInterp -blocking 0 -buffering none
    fileevent $prologInterp readable [list prolog::unexpectedPrologOutput $prologInterp]
    return
    }
 proc close {} {
    variable prologInterp
    fileevent $prologInterp readable ""
    puts $prologInterp "halt."
    ::close $prologInterp
    }
 proc unexpectedPrologOutput {interp} {
    set result [gets $interp]
    if {[string length $result] == 0} {
        error "Prolog interpreter closed unexpectedly"
        close $interp
    } else {
        error "Received unexpected output from interpreter: $result"
        }
    }
 proc getPrologResults {interp} {
    variable prologResult
    variable pendingResult
    set result [gets $interp]
    if {[string length $result] == 0} {
        close $interp
    } else {
        #puts "Read line: $result"
        switch -glob $result {
            prompt:* {set prologResult $pendingResult}
            ERROR:* {set pendingResult $result}
            asserted {set pendingResult ""}
            default {set pendingResult [lindex $result 0]}
        }
    }
 }
 proc prolog {prologTerm} {
    variable prologInterp
    variable prologResult
    fileevent $prologInterp readable [list prolog::getPrologResults $prologInterp]
    puts $prologInterp $prologTerm
    set prologResult {}
    vwait prolog::prologResult
    fileevent $prologInterp readable [list prolog::unexpectedPrologOutput $prologInterp]
    return $prologResult
 }
 }

Example use (assuming the above Tcl code is stored in a file call prolog.tcl):

 % source prolog.tcl
 % prolog::init
 % prolog::prolog assert(child(a,b)).
 % prolog::prolog assert(child(a,c)).
 % prolog::prolog child(a,b).
 1
 % prolog::prolog child(a,X).
 {b} {c}
 % prolog::prolog child(X,b).
 {a}
 %


Updated 30 Jun 2003, 15:22 GMT
Search - Recent Changes - Reference - About WiKit - Go to Tkoutline - Help
Sourceforge logo