BinPrologJ: a fast and flexible Prolog-in-Java

USER GUIDE

February 2008

About BinPrologJ

BinPrologJ is multi-threaded compiled Prolog system with powerful Object Oriented extensions, intended to be used as a flexible scripting tool for gluing together knowledge processors in distributed applications. Its high level, portable, secure networking layer provides intelligent interoperation between Java and .NET components on Windows, Mac OS X, Linux/Unix and Java enabled PDAs and cell phones.

Prolog threads are coordinated through blackboards, local to each process. Associative search based on term unification (a variant of Linda) is used as the basic synchronization mechanism. Threads, blackboard and networking operations are controlled with lightweight, fast bytecode Prolog interpreters. The synergy of these features makes BinPrologJ a convenient development platform for distributed AI, and in particular, for building intelligent autonomous agent applications. 

BinPrologJ supports multi-user synchronized transactions and interoperates with the C-based version of BinProlog, a high performance, multi-threaded  Prolog system, well suited for large volume server side operations. BinProlog is a robust, high performance commercial software product of BinNet Corporation. Please contact BinNet Corporation through our Web pages at http://www.binnetcorp.com, by phone at +1 (940) 300-7171 or by e-mail to binnetcorp@binnetcorp.com for licensing and price information.  

The  Prolog compiler integrates a high performance  pure Java based Prolog system featuring multiple logic engines and dynamic databases, with high level networking operations and a GUI development library. Packaged as a 450K Java+Prolog bytecode library, it is a self-contained, lightweight, knowledge and interaction processing component, featuring:

Getting started

Installation and essential files

Make sure you uncompress the provided *.zip or *.tar.gz file containing BinPrologJ . The toplevel interaction starts with something like

java -jar prolog.jar (this loads Prolog's bytecode from compressed and encrypted Java classes)

or

java -classpath prolog.jar prolog.kernel.Main(on a Java platform)

or

netprolog.exe

 (on a .NET platform)

Note that all Prolog and Java files are in fact in the prolog.jar file - once the classes are loaded by Java, Prolog extracts its own bytecode files! To run Prolog from another directory, put prolog.jar in directory /bin together with prolog.bat in Windows (or a similar script in Unix) and then just type

prolog.bat

You might need to adapt this script with other versions of Java or on other platforms. If you prefer, you can use the prolog.bat or netprolog.bat (for .NET execution) scripts - once you edit the absolute path information to reflect the directory where you install them.

Quick info - if you want to try out things right away. Look at the various *.bat files for hints on how to start the various components.

.NET installation:

Make sure the .NET framework  and the  J# redistributable runtime  have been installed on your computer. The are freely available from the download area at www.microsoft.com. From the "prolog" directory type netprolog.exe. At the ?- prompt, type something like vq8:go to see the 8-Queens program running!

Embedded Prolog component installation:

Simply copy the selfcontained prolog.jar file to the required component directory, assuming that the component managment software starts Prolog in a way similar to java -jar prolog.jar. This technique is usable for tools like Webstart or for deploying Prolog as a server side knowledge processor.

Running Prolog as an Applet:

A good way to deploy Prolog as an applet is using something like the following:

<APPLET
  ARCHIVE="prolog.jar"
  CODE="prolog.kernel.PrologApplet.class"
  HEIGHT="220"
  WIDTH="220"
 >
<PARAM
   NAME="command"
   VALUE="applet_ide"
>
</APPLET>


This applet will start by running the command parameter applet_ide as the initial goal.

Running Prolog Applets from the Web using Prolog's Embedded HTTP Server

If you do not have a Web server or want to avoid the hassle of configuring one, you can activate the embedded HTTP server allowing your users to run Prolog applets directly, with something like:

?-run_http_server(8001).

or

?-bg(run_http_server(8001)).

to run the server as a background Prolog thread. You can also add a remote predicate  with bg(run_server) to ensure that your  clients can keep persistent state on the server using a variant of the remote_run command.

Exporting local services with BinPrologJ's TCP-IP tunneling

BinPrologJ supports a simple TCP-IP tunelling API to export services provided by servers behind a firewall (in particular BinPrologJ's own Prolog remote predicate call server, or its HTTP server usable to deploy BinPrologJ applets or server side Prolog agents). The API consists of server and client side tuneling components:

server_tunnel(ServerPort,LinkServerPort,ServerTunnel): Creates a server side TCP-IP tunnel which maps ServerPort to LinkServerPort. When a client connects to LinkServerPort, it will be able to map all the services of a server it has access to (usually behind a firewall or NAT) as if they were provided on ServerPort. This component is meant to be used on a machine visible on the net to which services behind firewalls can connect and become visible. The returned ServerTunnel can be used to stop the (background) threads created to support the tunnel.

client_tunnel(LinkHost,LinkPort,LocalServer,LocalPort,ClientTunnel): Creates a client side TCP-IP tunnel which maps LinkHost, LinkPort, to LocalServer, LocalPort where a server (usually behind a firewall or NAT) listens. This client connects to LinkHost, LinkPort, and remaps the services at LocalServer, LocalPort as if they were provided on ServerPort on the LinkHost machine. This component is meant to be used on a machine invisible on the net (behind firewalls or NAT). The returned ServerTunnel can be used to stop the (background) threads created to support the tunnel.

stop_tunnel(Tunnel): Stops the threads supporting a server or client side tunnel.

For instance, you can start on your real "server" machine with fixed IP address and domain name ( let's say: server.example.com) with something like:

prolog.bat
?-server_tunnel(5555,4444,R).
?-server_tunnel(8888,7777,R).

Let's assume that on the client machine (let's say "localhost"), you run an Apache server on port 80 and a BinPrologJ Web server on port 81, which you have started in its own window with:

prolog.bat
?-run_http_server(81).

Then if you run on the client machine (localhost) in a separate window, something like

?-client_tunnel('server.example.com',4444,localhost,80,R).
?-client_tunnel('server.example.com',7777,localhost,81,R).

you will be able to tunnel the services form localhost to server.example.com (visible on the net) where you will see the
tunnelled Apache server as:

http://server.example.com:8888/

and the tunelled Prolog server as:

http://server.example.com:5555/

iPAQ Pocket PC installation:

Make sure the JEODE Java plugin is installed to your iPAQ from the iPAQ distribution CD or by obtaining it from www.insignia.com. Then, simply copy with ActiveSync the directory pocketProlog to the iPAQ FileStore. Navigate with the iPAQ File Explorer to the pocketPrologdirectory, in which you can find and click on the pprolog script. Type something like  vq8:go to see the 8-Queens program running! To change this default installation edit the pprolog.lnk file on the host to which the iPAQ connects to reflect a different location.

Key BinPrologJ  components - (section relevant ONLY for owners of BinPrologJ Full Source license)

The core of BinPrologJ  is in the Java classes inside the subdirectory prolog.kernel. Look at the BinPrologJ  class documentation in directory doc. The most important Prolog files, allowing you to configure/fix problems with BinPrologJ  are in directory psrc - containing the definition of most built-ins implemented in Prolog. If you have the BinPrologJ  sources, the essential files to look at are Main.java, Top.java,Machine.java in directory prolog.kernel and the psrc/refl.pl showing how to extend BinPrologJ  with new Java operations using Java's Reflection abilities. It also contains multi-threading and networking code as well as (minimal) Personal Java compliant Prolog GUI toolkit - which can run happily from virtually any browser and even on Java enabled PocketPC PDA's like Compaq's iPAQ.
 

Extending BinPrologJ

A new, reflection based extension mechanism has been added to BinPrologJ. In fact it is now used to provide some key internal components of BinPrologJ itself, like multi-threading and networking.

Third party BinPrologJ extensions are collected in a separate directory tree xprolog (obtained by expanding xprolog.zip). A quick way to add your own extensions is to clone one of them and adapt the builtins and related Prolog script files.

Using BinPrologJ  in command line mode:

Entering a query:

At the prompt "?-" type in a one line Prolog query, as usual, for instance, something like:

?- write(hello),nl.

Compiling a new program:

?-[<myprog>].

will read in memory the file <myprog>.pro program replacing similarly named predicates with new ones.

If you start BinPrologJ  with its visual environment, try something like:

?-ide.

to launch an instance of the IDE, or

?- vq8:go.

to run a visual N-Queens program and

?- vtetris:go.

to run a Visual Tetris Agent demo. Note that the <module>:<predicate> notation allows running multiple Prolog applications in different code spaces.

If you have Prolog files in multiple directories you can also add them to the end of BinPrologJ's search path with:

?- add_to_path('<mydirectory>').

On the other hand,

?-path_element(Directory).

will return one by one the directories in the current path. You can add to the top of your search path with:

?- push_to_path('<mydirectory>').

and delete from it with

?- del_from_path('<mydirectory>').

All subsequent references to compile, consult and <module>:<predicate>  will take into account the current path.

Note that by default BinPrologJ's toplevel is non-interactive - i.e. it prints all solutions to a query containing variables. You can change this by typing:

?- interactive(yes).

Using BinPrologJ  through an applet

Type appletviewer DemoGUIApplet.html . Enter, for instance, in the query window:

append(Xs,Ys,[1,2,3]).

You will see the applet displaying the results. Applets can load files located at the site where they originate from. Try

?-vq8:go(12).

to compile over the net and run the N-queens program with 12 queens. To launch an instance of a simple IDE with embedded editor and query processor, type

?-ide.
 

Building simple Prolog programs with BinPrologJ

With the editor of your choice create a text file called simple.pro, with the following content:

c(X):-a(X),b(X).

a(1).
a(2).
a(3).

b(2).
b(3):- !.
b(4).

In a shell window (DOS command, csh, bash etc.), type jc.bat to launch the standalone command line BinPrologJ toplevel, prompting you with "?- " to enter commands/queries. To load the file type:

?-[simple].

then try the query:

?-c(X).

Example of reading/writing to files:

BinPrologJ  can read files or URLs (as well as subcomponents of compressed ZIP or JAR files or URL) and write files in command line mode. In applet mode BinPrologJ  can only read files described as URLs, from from the directory it comes from. Try:

?-tell('example.txt'),write(example(one)),write('.'),nl,told.

followed by:

?-see('example.txt'),read(X),seen,write(X),nl.

BinPrologJ can open a URL over the net, in a way similar to file. Try:

?-see('http://yahoo.com'),get0(X),get0(Y),seen,put(X),put(Y).

to print out a HTML file fetched directly from the Web.

?-token_of('http://yahoo.com',Token),println(Token),fail.

will get a token at a time from a Web page and print it out. With this, writing your own Web data extractor in BinPrologJ is only a few lines away!
 

Creating Standalone BinPrologJ  Applications

BinPrologJ's binary Prolog code is stored in wam.bp (as well as in prolog.jar in encrypted and compressed form, to allow running even in the absence of the wam.bp file). The end  of this file also contains directives instructing the loader to link the code in the BinPrologJ's internal code area. The ucompile/3 operation merely concatenates newly generated bytecode files with the current basic bytecode file and as such allows creating standalone compiled applications. Here is the main command:

ucompile(InFiles,BasicFile,OutFile)

For instance, if  your application containing only the file hello.pl  is located  in directory bin, you can type:

?-ucompile(['hello.pl'], 'wam.bp', 'hello.bp').

After compilation, to run Prolog code from the resulting bytecode file (now containing both BinPrologJ's kernel and you application's bytecode)  you will need to pass the name of the new bytecode file as a parameter to BinPrologJ as in:

java -cp ".;prolog.zip" prolog.kernel.Main hello.bp

or by using a script like urun.bat:

urun.bat hello
 
Please make sure you backup you wam.bp file before experimenting with this command.

Saving and restoring BinPrologJ's state using Java serialization

All BinPrologJ classes are serializable - you can save BinPrologJ's state (code compiled to memory + dynamic database) with the command. serialize(myFile) which creates myFile.jc in the current directory and then resume from the saved state file by passing <MyFile>.jc as a command line parameter to BinPrologJ like in: java -cp prolog.jar prolog.kernel.Main myFile.jc. Such code is loaded significantly faster than by calling the compiler.

Classes, Instances and Inheritance in BinPrologJ's Object Oriented Prolog

BinPrologJ  features a simple, elegant and fast Object Oriented Prolog layer - built as a natural extension to ISO Prolog. Classes are just Prolog files with include declarations - almost no changes  are required to reorganize your existing Prolog code in an Object Oriented style. As the dispatching of method calls is handled at compile time and instances are lightweight, BinPrologJ 's Prolog Objects are extremely efficient. Prolog class files can be located at arbitrary URLs on the Web - you can inherit and override from a virtually unlimited library of existing Prolog files. And what if cycles will form? Not a problem! BinPrologJ  supports multiple cyclic inheritance for building in a scalable way, an arbitrary network of Web or file based interdependent Prolog classes!

A class foo is associated to each Prolog file (or URL) let's say foo.pl. In a way similar with compiling a Prolog file in a conventional way with ?-compile(foo), the user can enter a default instance of the class foo with

?-enter_class(foo).

or call code in it (like in the presence of a conventional module system) from outside with

?-foo:<predicate>.

If foo.pl contains :-[<superclassfile>] declarations, let's say something like :-[bar], definitions not found in foo will be searched in bar. As files and URLs are treated in similar ways, inheritance directives like:-['http://www.my_url.com']can refer to non-local URLs as well.

ISO Prolog's :-initialization((<Goal>)) declarations are processed (in reverse order) after collecting them from included files - as a mechanism of class level initialization, shared among all instances. Code added with assert/1 in such initialization/1 calls will be visible in all instances.

Code inheritance is handled at compile time - through a special ocompile/1 command  which reinterprets include directives like :-[<file>] as inheritance from other Prolog files/classes. Classes are compiled on the fly, at the first use of a class or creation of a new instance.

The multiple cyclical depth first inheritance mechanism is implemented by keeping the path consisting of the list of visited includes, when (at compile time) predicates not defined locally, are brought from files or URLs. In the presence of multiple includes, a "depth-first" order for finding definitions ensures that a dominant main inheritance tree prevails in case of ambiguity. This concept of cyclical inheritance allows reuse of  Prolog code located virtually everywhere on the Web from a local perspective.

Constructors are simply predicates of various arities having the same name as the file (or URL). Constructors (of any arity) are inherited, if not provided. They can freely call constructors defined in their super classes using their predicate names, same as the ones of the superclasses' names. The recommended style is to have lower arity constructors call higher arity ones with some constant default parameters. No special action is provided (and this is different from Java!) for no-arg constructors of supers, except that they are inherited like any other constructors. It is a good programming practice to share some common initialization code among constructors - like for instance initialzing fields, or use ISO Prolog  :-initialization(Goal) directives to initialize fields.This style ensures that fields defined locally or in the main inheritance chain will prevail. Programmers should  initialize fields in constructors or initialization directives, as attempts to access unitialized fields trigger runtime exceptions (rather that simply failing).

Instance Fields are local to each instance. A set operation <field name>  <= <value>  and a get operation <field name>  => <Prolog variable> are provided, like in:

    radius <= 99
    radius => R.

Reference to uninitialized fields results in a runtime exception.

Assert/1 and other database operations are local to instances - but the results of class level asserts are visible in instances - until overridden by a local assert with the same predicate name and arity.

To keep the object model simple, we have not implemented public, private or final modifiers. This can be provided in the future if requested by users. Other, more precise access and security control mechanisms will be added in the future in the context of distributed objects and agent programming constructs.

Instances are created from Constructors (same as class names if havin no arguments, and having the main functor the same as the class name if having arguments) with the new/2 and renew/2 commands:

new(Constructor,Instance)  - which automatically loads te code when the first instance is created and reuses it otherwise

and

renew(Constructor,Instance)  - which forces refreshing the code for the class, before creating the instance

In the process, an internal Class handle is created and if neaded the code for the class is compiled on the fly and attached to it.

You can interact with Instances of Prolog Classes in two ways, as shown in the following examples (see files like queue.pl,stack.pl in classlib for more details).

?-enter_instance(queue).
?-add(a).
?-add(b).
?-remove(X).
?-show.
?-exit.
?-

or call code in it from outside as in:

?-new(queue,Q),Q:add(a),Q:add(b),Q:remove(X),Q:show.

Here is a simple example of 2 Prolog classes circle.pl inheriting from shape.pl.



% file shape.pl

shape:-
  centerX<=0,
  centerY<=0.

show:-
  centerX=>X,
  centerY=>Y,
  println('I am a shape at'(X,Y)).

move_to(X,Y):-
  centerX<=X,
  centerY<=Y.



% file circle.pl

:-[shape].

circle :- radius<=1.

circle(X,Y,R):-
  centerX<=X,
  centerY<=Y,
  radius<=R.

show:-
  centerX=>X,
  centerY=>Y,
  radius=>R,
  println('I am a circle at'(X,Y,R)).



A query like:

?- new(circle(1,1,5),Circle),Circle:show,Circle:(move_to(2,2),show).

will produce:

'I am a circle at'(1,1,5)
'I am a circle at'(2,2,5)

as expected, by overrding show/0 and inheriting move_to/2.

Class Fields are shared among instances. In fact, they are just instance fields belonging to instace 0 - which keeps the state of the class itself. A class field set operation <field name>  <== <value>  and a class field get operation <field name>  ==> <Prolog variable>  are used as in:

    instance_count <== 10
    instance_count ==> R.

When performed from instances of a given class, these operations are applied to the fields of the class, not the instance. Like static variables in Java, they refer to data shared among all instances of a class. Reference to uninitialized fields results in a runtime exception.
 

Backtrackable Field Assignments

Undoables are special fields local to each Prolog Engine providing backtrackable destructive assignment. A set operation <field name> <=# <value>  and a get operation <field name>  #=> <Prolog variable> are provided, like in:

    ?-maybe <=# blue, maybe #=> Color.

returning Color=blue.

Assignement to undoables is backtrackable - this means that something like:

    ?-maybe <=# blue, fail ; maybe #=> Color.

will fail, without provinding a value to Color. Contrast this with conventional field assignment which survives backtracking, which means that:

?-maybe <= blue, fail ; maybe => Color.

will return Color=blue. Their implementation uses the public interface prolog.kernel.Undoable which can provide action on baktracking services to a user's Java objects. Java objects implementing this interface should provide two methods: undo() which will be called each time backtracking moves to the next clause of a given choicepoint and and done() which is called when the current choicepoint is popped off the stack. A user can register such objects (created using BinPrologJ's Reflection based interface) with something like add_undoable(<MyUndoableObject>). Assumming this happens in the first clause of a predicate, for each subsequent failure, the object's undo() method will be called. The object's done() method will be called only once, after all clauses of the predicate have been explored.
 

Assumption Grammars and DCGs

BinPrologJ supports BinProlog-style assumption grammars as an extension to traditional Prolog DCGs. The main difference with BinProlog is tha the Assumption Grammar API works only inside DCG rules (clauses created using -->/2 instead of :-/2 ) and their scope always extends over the "current AND-continuation" - i.e. while the current computation is succeeding.  The API consists in a set of backtrackable operations, which enhance DCG rules by providing a temporary database where information aquired during the parsing process can be added, updated and queried. The API distingushes between "linear assumptions" usable at most once once - which bind variables, and "intuitionistic" assumptions - which can be matched by queries indefinitely (unless removed on backtracking). Assumption Grammar operations provide, together with the use of overriding of DCG rules by organizing them in Prolog classes, a powerful Object Oriented Logic Grammar mechanism.

Assumption Grammar API (operations only usable inside DCG rules):

'#<'(Xs): sets the DCG token list to be Xs

'#>'(Xs): unifies current DCG token list with Xs

#X: matches X against current DCG token

'#+'(X): adds 'linear' assumption +(X) to be consumed at most once, by a '#-' operation

'#*'(X): adds 'intuitionisic' assumption *(X) to be used indefinitely by '#-' operation

'#='(X): unifies X with any matching existing or future +(X) linear assumptions

'#-'(X): consumes +(X) linear assumption or matches *(X) intuitionistic assumption

'#?'(X):  matches +(X) or *(X) assumptions without any binding

The file progs/ag_parser.pl  shows a Prolog complete parser and tokenizer written using BinPrologJ's Assumption Grammars. Among the advanced features implemented: regular expression processing and use of assumptions to create unique variables from variable tokens with identical names.

Multiple Databases in BinPrologJ

  BinPrologJ  supports multiple databases directly - in conventional Prolog style - i.e. something like

?-Db=test_db, db_assert(Db,a(1)), db_clause(Db,a(X),B)
.

which will assert
a(1) in database Db and then bind X=1 and B=true.

BinPrologJ  also provides fast loading and saving of password protected  databases with:

?-db_xsave(Db,File,Pasword).

which saves a protected databased named Db to a file, and

?-db_xload(File,Db,Pwd).

which loads from a file or url URL a protected database named Db.

Multiple Engines, Answer Generation and Control

Independently of its multi-threading mechanism, BinPrologJ  provides first order engines - separate instances of its dynamically growing/shrinking runtime system (heap,stack,trail) which can be controlled through the following API: Example:

?- new_engine(X,(member(X,[1,2]),(X=1,return(good(X));X>1)),E),get(E,A),get(E,B),get(E,C),get(E,D).

A = the(good(1)) B = the(1) C = the(2)  D = no E = 1331 X = _120 ;

This kind of functionality seems the simplest way to implement some agent-style  control mechanism on top of Prolog - it provides a minimal set of language constructs for the  kind of control users want to have interactively over Prolog's answer production.

Threads and Hubs

BinPrologJ  supports a simple multi-threading model, given by the following API:

Thread Coordination with Blackboards

Blackboards are global (one per BinPrologJ process) databases which provide thread coordination through the following (extended Linda) operations: Blackboard operations can be combined with remote_run remote predicate calls to allow interaction between threads distributed in different processes on the same or on different computers on the net. Threads can be launched locally or remotely with bg operations.

Client-Server Programming with Remote Predicate Calls

BinPrologJ  supports a simple client-server Remote Predicate Call mechanism given by the following API: A slightly more efficient client-server API that reuses sockets consists of the following:

 

Sharing a BinPrologJ Shell Server through Remote BinPrologJ Shell Clients

BinPrologJ  provides full redirection of I/O from a BinPrologJ application working as a  shell server to one or more others working as shell clients. This allows seeing/debugging/running a program by multiple users (for instance a teacher and a group of students).

Server Window:

?- shell_server.
Server started on port: 6001

Client Window

?- ?- shell_client.
?- assert(a(99)).
yes

?- a(X).
X = 99
;
no

Note that asserted facts will become visible to all clients. BinNet Corp. also provide lightweight (less then 10K) Java and .NET clients allowing to run server side Prolog applications with minimal client processing effort (like for instance in the case of wireless or cellular phone enabled PDAs).

Calling Java from BinPrologJ  by using Reflection

The Reflection based API connecting compiled Prolog code directly to arbitrary Java classes, objects, methods, fields is very simple but very powerful. Data-types are converted automatically to their most natural equivalents (Java Strings to/from Prolog constants, Java integers and doubles to their Prolog numerical equivlalents etc.). If a given method's parameter signature triggers a Java exception, the interface attempts exhaustive method search and data conversions to invoke what the programmer "means".


A more convenient Prolog layer provides an extended REFLECTION API - which also shows how the main builtins are used.

/*
  add new object using convenient <className>(A1,...An) syntax
*/
new_java_object(ClassName,Object):-
  atom(ClassName),!,
  new_java_class(ClassName,Class),
  new_java_object(Class,void,Object).
new_java_object(ClassAndArgs,Object):-
  functor(ClassAndArgs,ClassName,_),
  new_java_class(ClassName,Class),
  new_java_object(Class,ClassAndArgs,Object).

/*
   invoke static or dynamic Java method using
   more natural <methodName>(A1,...An) syntax
*/
invoke_java_method(ObjectOrClass,MethodName,Result):-
  atom(MethodName),!,
  invoke_java_method(ObjectOrClass,MethodName,void,Result).
invoke_java_method(ObjectOrClass,MethodAndArgs,Result):-
  functor(MethodAndArgs,MethodName,_),
  invoke_java_method(ObjectOrClass,MethodName,MethodAndArgs,Result).

/*
  Get the content of a (public) Java field
  after obtaining a handle to it
*/
get_java_field(Object,FieldName,Val):-
  get_java_field_handle(Object,FieldName,Handle),
  invoke_java_method(Handle,get(Object),Val).
 

/*
  Set the content of a (public) Java field
  to a new value, after obtaining a handle to it
*/
set_java_field(Object,FieldName,NewVal):-
  get_java_field_handle(Object,FieldName,Handle),
  invoke_java_method(Handle,set(Object,NewVal),_).

/*
  Get the a handle to the class to which object O is an instance
*/
get_java_class(O,Class):-
  invoke_java_method(O,getClass,Class).
 

Wrapping Java Classes as BinPrologJ Classes using Reflection

A natural way to interface Prolog with Java is by building Prolog class wrappers around Java classes.  We can build a wrapper class using BinPrologJ's Object Oriented Layer to provide in Prolog the "same" functionality as it does in Java. The following example (code in classlib/hashtable.pl )  shows how we can map Java's Hashtables to Prolog classes.

/*
 Example of wrapper for a Java class as a Prolog class
*/

hashtable:-
  new_java_class('java.util.Hashtable',C),
  new_java_object(C,void,JavaHashTable),
  table<=JavaHashTable.

put_data(Key,Data):-
  table=>T,
  invoke_java_method(T,put(Key,Data),_Result).

get_data(Key,Data):-
  table=>T,
  invoke_java_method(T,get(Key),Data).

remove_data(Key):-
  table=>T,
  invoke_java_method(T,remove(Key),_Result).

Example of Prolog interaction:

?- new(hashtable,H),H:put_data(hello,99),H:get_data(hello,X).
H = '$instance'(hashtable@554,1) X = 99 ;
 

Extending BinPrologJ's functionality through Reflection

Here is a (simplified) example on how we build an interface to BinPrologJ's own networking classes, using this reflection based Java calling interface.

new_server(Port,Server):-
  new_java_object('prolog.kernel.Transport'(Port),Server).

new_service(Server,Service):-
  new_java_object('prolog.kernel.Transport'(Server),Service).

read_from(Service,Query):-
  invoke_java_method(Service,read_from,Query).

write_to(Service,Answer):-
  invoke_java_method(Service,write_to(Answer),_).

% server side RPC handling

run_server(Port,Password):-
  new_server(Port,Server),
  repeat,
    new_service(Server,Service),
    bg(handle_query(Service,Password)),
  fail.
 

Take a look at the Java files in directory psrc  for  more examples of built-ins written directly in Prolog.
 

Calling BinPrologJ  from Java and back


This basic String based interface allows sending a query to BinPrologJ and getting a String back as based on the (first) answer to your queries. Note that BinPrologJ will call back (through the Prolog predicate go/0) the static sayHello() method in class Hello.java.

Hello.java file:

import java.io.*;
import prolog.kernel.*;

public class Hello {
   public static String sayHello(String who) {
     System.out.println("Hello "+who);
     return "got "+who;
   }

   public static void main(String[] args) {
     Machine M=Top.initProlog(args); // args can be null
     System.out.println(M.run("println(hello)")); //calls BinPrologJ
     System.out.println(M.run("X :- X is 1+1")); //gets a result from BinPrologJ
     M.run("mcompile('hello.pl')");
     M.run("go");
   }
}

hello.pl file:

go:-
  call_java_class_method('Hello',sayHello('BinPrologJ'),Result),
  println('back to BinPrologJ'),
  println('Java said: '=Result).
 

Java-Prolog Interface Techniques for Power Users

If your application needs very high performance and/or complex data exchanges between Java and Prolog you can adapt the following examples (provided together with a J++ 6.0 project and some simple script files for Windows) in the directory xjinni2k/JavaCallsBinPrologJAndBinPrologJCallsBack ). Here are some scenarios of what can be achieved:
 

The Java side: file MyMain.java

import prolog.kernel.*;
import java.util.*;

/**
 *  Shows examples of calls form Java to Prolog and back
 */
public class MyMain  {
 
  /**
   *  this class embeds BinPrologJ into your application
   *  make sure prolog.jar is copied in THIS directory
   *  because that's where BinPrologJ will look for its Java code
   *  as well as the for its Prolog bytecode.
   *  Start BinPrologJ with: go.bat, then run.bat
   */
  public static void main (String args[]) {
    Machine M=Top.initProlog(args);
    if(M!=null) {
      String query="compile('myMain.pl')";
        testJavaPrologInterface();
        M.run(query);
      Top.toplevel(M);
    }
    JavaIO.halt(0);
  }

  /**
   * just makes printing easier
   */
  public static void p(String s) {
    JavaIO.dump(s);
  }

/**
 *  creates a Prolog machine and runs on in a few queries
 */  
public static void testJavaPrologInterface() {

  Machine M=Top.new_machine();
 
  {
  // simple String queries
 
    String s=M.run("member(X,[a,b,c])");
    JavaIO.dump("testBinPrologJ: first X in member/3: "+s);   
     
    M.run("assert(a(88))");
    JavaIO.dump("assert works: " + M.run("X:-a(X)"));    
      
    // building a compound term query, getting all answers
 
    Object X=new Var(1);
    Object goal_args[]={X,new Integer(1),new Integer(5)};
    Fun Goal=new Fun("for",goal_args);
    Object[] answer_args={X,Goal};
    Fun Query=new Fun(":-",answer_args);
    if(!M.load_engine(Query)) return;
    for(;;) {
      Object answer=M.get_answer();
      if(null==answer) {
        // this kills the machine - if still alive -
        // it happens after last answer automatically
        M.stop();
        break;
      }
      JavaIO.dump("testBinPrologJ: X in for/3(X,1,5): "+answer);
    }
  }  
  { // passing to Prolog a Java object for a call back method
 
    M=Top.new_machine(); // stop killed the previous one
    Date today=new Date();
    Object R=new Var(1);
    Object goal_args[]={today,"toString",R}; // note first arg "today" - it is a handle to a Date
    Fun Goal=new Fun("invoke_java_method",goal_args); // we build the callback goal
    Object[] answer_args={R,Goal}; // we build the answer pattern to be returned
    Fun Query=new Fun(":-",answer_args); // we put them together as a query with clause syntax
    if(!M.load_engine(Query)) return; // we load the (existing) Prolog engine
    for(;;) {
      Object answer=M.get_answer(); // get an answer
      if(null==answer) {
        M.stop();
        break; // exit loop when finished
      }
      // print out an answer
      JavaIO.dump("testBinPrologJ: Prolog callback on a Java Date object gives ===> "+answer);
    }
  }  
 
  { // some easier ways to build queries - using special purpose Fun constructors
    
    M=Top.new_machine(); // stop killed the previous one
    Fun List=new Fun(".","a",new Fun(".","b","[]"));
    Fun Goal=new Fun("append",new Var(1),new Var(2),List); // we build the Prolog goal
    Fun Query=new Fun(":-",new Fun("result",new Var(1),new Var(2)),Goal); // we put them together as a query with clause syntax
    if(!M.load_engine(Query)) return; // we load the (existing) Prolog engine
    for(;;) {
      Object answer=M.get_answer(); // get an answer
      if(null==answer) {
        M.stop();
        break; // exit loop when finished
      }
      // print out an answer
      JavaIO.dump("testBinPrologJ: Prolog nondeterministic list append gives ===> "+answer);
    }
  }  
}  
  /**
   *  Exhibits work in Java on a term sent from Prolog
   */
  public static Object workOnPrologData(Object oterm) {
    Machine machine=Top.new_machine();
   
    p("TERM===> "+oterm);
    Object O=((Fun)oterm).args[1];
    p("CHOP===> "+O);
    p("NODES===> "+countNodes(O));
    p("LEAVES===> "+getLeaves(O));
    
    Var X=new Var(1); Var Y=new Var(2);
    Object[] args={
      "hello",new Integer(11),
      X,X,Y,Y,
      new Double(3.14),
      "bye"
    };
    return new Fun("fromJava",args);
  }
 
  /**
   * simple recursive count node method
   */
  static int countNodes(Object O) {
    p("count_trace: "+O);
    if(!(O instanceof Fun))
      return 1;
    Fun F=(Fun)O;
    int count=1;
    for(int i=0; i<F.args.length;
      i++) {
      count+=countNodes(F.args[i]);
    }
    return count;
  }
 
  /**
   * collects leaves (Strings, ints etc.) of a Prolog term seen as tree
   */
  static Vector getLeaves(Object O) {
    Vector V=new Vector();
    try {
      getLeaves(O,V);
    }
    catch(Exception e) {
      JavaIO.errmes("error in getLeaves",e);
    }
    return V;
  }
 
  /**
   * recurses over Prolog term seen as a tree
   */
  static void getLeaves(Object O,Vector V) {
    if(!(O instanceof Fun))
      V.addElement(O);
    else {
      Fun F=(Fun)O;
      for(int i=0; i<F.args.length; i++) {
        getLeaves(F.args[i],V);
      }
    }
  }
}


The Prolog side: file myMain.pl


test:-
   new_java_class('MyMain',Class),
   Term=f(X,g(3.14,hello,X,99),h(Y,Y)),
   invoke_java_method(Class,workOnPrologData(Term),Result),
   println(Result).

likes('Joe',beer).
likes('Mary',wine).

:-println('PLEASE TYPE test TO RUN THE OTHER EXAMPLES').
 

Fluents

BinPrologJ  can be seen as a collection of Prolog engines working on a given compiled  clause database and calling built-in operations. Through built-in calls, they possess the ability to create other query first class engines and interoperate with Java through a set of stateful objects called Fluents.

Fluents are created with specific constructors or by converting from other fluents or conventional Prolog data structures like Terms, Lists or Databases. All Fluents are disabled with a stop/1  operation which releases their resources (most Fluents also call stop/1 on backtracking to free resources).

Sources are Fluents enabled with an extra get/2 operation. Typical Sources are Horn Clause Engines, File, URL or String Readers, Fluents built form Prolog lists. Sinks are fluents enabled with an extra put/2 and collect/2 operation.Typical fluents are ClauseWriters or CharWriters targeted to Files,  TermCollectors  (implemented as a Java Vectors collecting Prolog terms), StringSinks (implemented as a Java StringBuffers colecting String representations of Prolog terms). Prolog Databases are first class citizens implemented as extensions of Sources which provide add/2, remove/2, collect/2 operations.

Fluents are resources which go throuh state transitions as a result of  put/2 or get/2 operations. They end their life cycle in a stopped state when all the resources they hold are freed. An algebra of Fluent composers provides abstract operations on fluents. For instance, append_sources/3 creates a new Source with a get/2 operation such that when the first Source is stopped, iteration continues over the elements of the second source.   Compose_sources/3 provides a cartesian product style composition, the new get/2 operation returning pairs of elements of the first and second Source. Split_source/3 creates 2 Source objects identical to the Source given as first argument. It allows writing programs which iterate over a given Source multiple times. Sources and Sinks are related through a discharge(Source,Sink)operation which sends all the elements of the Source to the given Sink.

Here is some Prolog code showing some fluent operations:
 

element_of(I,X):-get(I,the(A)),select_from(I,A,X).

select_from(_,A,A).
select_from(I,_,X):-element_of(I,X).

split_source(S,S1,S2):-
  findall(NewS,nth_source_clone(2,S,NewS),[S1,S2]).

split_source(S,S1,S2,S3):-
  findall(NewS,nth_source_clone(3,S,NewS),[S1,S2,S3]).

uniquify_source(S,SWithNoDups):-
  findall(E,element_of(S,E),Es),
  sort(Es,Sorted),
  new_engine(X,member(X,Sorted),SWithNoDups).

nth_source_clone(N,S,NewS):-
  findall(X,element_of(S,X),Xs),
  for(_,1,N),
  new_engine(X,member(X,Xs),NewS).

compose_sources(S1,S2,S):-new_engine(X,element_of_either(S1,S2,X),S).

element_of_either(S1,_,X):-element_of(S1,X).
element_of_either(_,S2,X):-element_of(S2,X).

File, socket, URL and string based I/O

BinPrologJ extends Prolog's file and stream I/O based operations to URLs and strings - to give a uniform view of all of them as streams% fluent style readers.
 

Source Fluent based Input Stream constructors:

new_char_reader(Fname,Reader)

new_term_string_reader(Fname,Reader)

new_string_char_reader(String,Reader)

new_string_term_string_reader(String,Reader)

new_token_reader(Fname,Reader)

new_string_token_reader(String,Reader)
 

Reading a term from the standard input:


read_term(Term,VarsAndNames)

read(Term)
 

Reading from a string (Prolog atom)


sread(AtomString,TermAndVars): reads a term and vars from a string

sread_term(AtomString,Term)

sread_term(AtomString,Term,Vars)

sread_term(AtomString,Term,Vars,Names)

sread_clause(AtomString,clause(Clause,Vars,Names)

sread_clauses(S,C): read multiple clauses from a string

sread_terms(S,C): reads multiple terms from a string

sread_goal(AtomString,Term,NamesAndVars)

string_clause_reader(AtomString,ReaderStream)

get0(X): classic Prolog binary byte reader - returns a 0..255 integer code

get_code(X) - same as classic Prolog's get0(X)
get_char(C) - returns an atomic char

get(R) - returns an ascii code ignoring spaces

see(File): opens for reading

exists_file(F): checks if a file exists

find_file(File,NewFile): searches default directories and URLs for a file and  returns path+name
 

Prolog file input

pred_of(File,FN): extracts all predicate names F/N found in a Prolog file or URL

clause_of(File,Clause): extracts all clauses from a Prolog file or URL

clause_of(File,Clause,VarsAndNames): extracts all clauses from a Prolog file or URL with Vars

term_of(F,C): extracts all clause terms from a Prolog file or URL

term_of(F,C,Vs): extracts all clause terms from a Prolog file or URL and their Vars

term_string_of(F,S): extracts atom (Prolog string) representation of terms in a Prolog file

url2tokens(URL,Tokens): extract a list if tokens from a file or URL

token_of(FileOrURL,Token): extracts and backtracks over tokens found in a URL or file

url2codes(URL,Cs): reads a URL as lists of ascii codes

url2string(URL,S): reads a file or URL to a Prolog atom

file2string(F,S): reads a file or URL to a Prolog atom

char_of(URL,C): reads and backtracks over a URL or file returning ascii codes
 

Prolog Output Operations

put_code(AsciiCodeOrByte): writes it to default output
put_char(Char): writes it to default output

write(Term): writes it to default output
print(Term): writes it to default output
writeq(Term): writes it to default output in a way it can be read back

write_unquoted(Term): writes it to default output

pp(Term): pretty prints a term or clause to default output

println(X): writes adding an end of line

ttynl: writes end of line to standard output

ttyput(AsciiCode): writes to standard output

ttyprint(Term): writes term to standard output

tell(File): sets File as default output device

tab(N): writes N spaces to default output device
 

Fluent Composers

Fluent composers provide abstract operations on Fluents. They are usually implemented with lazy semantics.

For instance, append_sources/3 creates a new Source with a get/2 operation such that when the first Source is stopped, iteration continues over the elements of the second Source.

Compose_sources/3 provides a cartesian product style composition, the new get/2 operation returning pairs of elements of the first and second Source. Reverse_source/2 builds a new Source such that its get/2 method returns its elements in reverse order.

Split_source/3 creates two Source objects identical to the Source given as first argument. It allows writing programs which iterate over a given Source multiple times. Sources and Sinks are related through a discharge(Source,Sink) operation which sends all the elements of the Source to the given Sink.

Fluent Modifiers (unimplemented)

Fluent modifiers allow dynamically changing some attributes of a give Fluent. For instance set_persistent(Fluent,YesNo) is used to make a Fluent survive failure, by disabling its undo method, which, by default, applies the Fluent's stop method on backtracking.

Engines as Source Fluents

Let us put to work in a more specific way the view of Interpreters as Fluents. All we have to do, is provide is a Fluent constructor, creating an Answer Source from an Answer Pattern and a Goal given to an Interpreter. As a result, we will cover negation, limited pruning through once/1, if-then-else/3, findall/3,var/1, and, beyond standard Prolog, forms of lazy, on-demand generation of sets of solutions, as well as a uniform set of built-ins for manipulation of first class Prolog databases and external objects like Files or URLs.

Answer Sources (engines) can be seen as generalized iterators, allowing a given program to control answer production in another. Each Answer Source works as a separate Prolog  interpreter.

The Answer Source constructor initializes a new interpreter.

new_engine(AnswerPattern,Goal,AnswerSource)
creates a new Horn Clause solver, uniquely identified by AnswerSource (a Source Fluent), which shares code with the currently running program and is initialized with resolvent Goal. AnswerPattern is a term, usually a list of variables occurring in Goal.

The get/2 operation (provided by all Sources) is used to retrieve successive answers generated by an Answer Source, on demand.

get(AnswerSource,AnswerInstance)
tries to harvest the answer computed starting from Goal, as a instance of AnswerPattern. If an answer is found it is returned as the(AnswerInstance), otherwise no is returned. Note that once no has been returned, all subsequent get/2 on the same AnswerSource will return no. Bindings are not propagated to the original Goal or AnswerPattern when get retrieves an answer, i.e. AnswerInstance is obtained by first standardizing apart (renaming) the variables in Goal and AnswerPattern, and then backtracking over its alternative answers in a separate Prolog interpreter. Therefore, backtracking in the caller interpreter does not interfere with the new Answer Source's iteration over answers. Note however that backtracking over the Answer Source's creation point as such makes it unreachable and therefore subject to garbage collection.

Finally, an Answer Source is stopped with the stop/1 operation implemented by all Sources.

stop(AnswerSource)
The stop/1 operation is called automatically when no more answers can be produced as well as through the Fluent's undo operation on backtracking.
 

Note on BinPrologJ 's Syntax

BinPrologJ 's fast Java-based parser actually supports a set of fixed operators, matching something like 80-90% of  those occurring in most Prolog programs.

Unicode based language internationalization is supported by using only the (wide) char type and appropriate JDK 1.x and later reader and writer classes. BinPrologJ 's parser and tokenizer are very small and based on Java's own built-in parsing/tokenizing libraries (StreamTokenizer). This allows relying on code already in  browsers instead of having to load classes over the network. In the future, as BinPrologJ  is heading towards plain Natural Language input through speech recognition software, it's internal syntax (which might already look too restrictive to Prolog programmers) will be generated through a preprocessor supporting user defined distfix operators. As such, BinPrologJ  is expected to be able to emulate full Prolog syntax and more. However, BinPrologJ 's current syntax is restricted to standard prefix Prolog terms of the form f(T1,..Tn), with a number of predefined prefix and infix operators. After parsing all clause and expression level operators everything becomes a Prolog Term - which can be described as follows:

Term ::                                   Const | VARIABLES | CompoundTerm
CommaSeparatedTerms ::     Term {,Term}*
CompoundTerm ::                  IDENTIFIER(CommaSeperatedTerms)
Const ::                                  IDENTIFIER  |  INTEGER  |  REAL | SystemObject

IDENTIFIERs starting with lower case characters and containing only alphabetical characters do not require quoting, for instance is(X,99) is a valid term. When IDENTIFIERs start with upper case or contain non-alphabetical characters they require single quotes, i.e. you should write something like ':-'(a(X),b(X)) or '+'(3,4). Variables start with upper case characters or "_". Both % one line and /* ... */ multiple line comments are supported.

BinPrologJ can also make use of the full BinProlog parser, supporting full operator syntax. Type

?-full_parser(on).

?-full_parser(off).

to selectively turn it on and off.

Tracing and debugging

BinPrologJ provides tracing and debugging of both interpreted (dynamic) and compiled code through the use of a tracing metainterpreter. To start tracing of interpreted code just type:

?-trace(Goal).

Compiled code needs to be loaded with a special command

?-debug(File).

After this, tracing works the same way as with interpreted code:

?- trace(nrev([1,2],Reversed)).
'Call: 'nrev([1,2],_133)
 'Call: 'nrev([2],_466)
  'Call: 'nrev('[]',_716)
  'Exit: 'nrev('[]','[]')
  'Call: 'app('[]',[2],_466)
  'Exit: 'app('[]',[2],[2])
 'Exit: 'nrev([2],[2])
 'Call: 'app([2],[1],_133)
  'Call: 'app('[]',[1],_1451)
  'Exit: 'app('[]',[1],[1])
 'Exit: 'app([2],[1],[2,1])
'Exit: 'nrev([1,2],[2,1])
Reversed = [2,1] ;
'Redo: 'nrev([1,2],[2,1])
 'Redo: 'app([2],[1],[2,1])
  'Redo: 'app('[]',[1],[1])
  'Fail: 'app('[]',[1],_1451)
 'Fail: 'app([2],[1],_133)
 'Redo: 'nrev([2],[2])
  'Redo: 'app('[]',[2],[2])
  'Fail: 'app('[]',[2],_466)
  'Redo: 'nrev('[]','[]')
  'Fail: 'nrev('[]',_716)
 'Fail: 'nrev([2],_466)
'Fail: 'nrev([1,2],_133)
no  


BinPrologJ 's Prolog built-in predicates

BinPrologJ 's built-in predicates follow the spirit of Prolog while simplifying when possible and adjusting to its restricted syntax. For their complete executable specification and typical use cases we refer to the file tarau/jinni/lib.pro containing the BinPrologJ  system library.

Basic built-ins of the compiler: covers a subset of BinProlog - an Edinburgh style compiler close to ISO Prolog.

Note: unimplemented built-ins are marked with ??? Currently some of the built-ins work only in the compiled mode - the behavior is expected to become identical once the interpreter/compiler merge is finished. Launch the interpreter with ji.bat, launch the compiler with jc.bat
  1. !/0 : succeeds like true/0, but removes pending choices in calls at its LEFT and makes things look as if this were the LAST clause of the predicate
  2. * /3 : multiply
    EXAMPLE(S): 
    ?-*(10,3,A).
    A = 30;

    no
  3. (+)/3 : add
    EXAMPLE(S): 
    ?-+(10,3,A).
    A = 13;

    no
  4. (,)/2 : A,B succeeds if A suceeds and B, called after A, succeeds
  5. (-)/1 : calls and possibly consumes assumed clause with matching head
  6. (-)/3 : subtract
    EXAMPLE(S): 
    ?--(10,3,A).
    A = 7;

    no
  7. (->)/2 : Cond->Then executes Cond once; if it succeeds it also executes Then
  8. / /3 : division
    EXAMPLE(S): 
    ?-/(10,3,A).
    A = 3.33333;

    no
  9. // /3 : integer division
    EXAMPLE(S): 
    ?-//(10,3,A).
    A = 3;

    no
  10. /\ /3 : bitwise AND
    EXAMPLE(S): 
    ?-/\(1,2,A).
    A = 0;

    no
  11. (;)/2 : A;B succeeds if A succeeds or B, called after A, succeeds
  12. (<)/2 : numeric comparison
  13. << /3 : left shifts arg 1 by arg 2 bits
    EXAMPLE(S): 
    ?-<<(1,5,A).
    A = 32;

    no
  14. (=)/2 : (X,Y) true if  terms X and Y unify - cyclic terms can result from =/2, as occur check is not performed
    EXAMPLE(S): 
    ?-f(A,s(a)) = f(B,B).
    A = s(a);
    B = s(a);

    no
  15. (=..)/2 : called univ -this is bidirectional- it converts between a term and its view as a alist of components
    EXAMPLE(S): 
    ?-f(a,b) =.. A.
    A = [f,a,b];

    no
    ?-A =.. [f,a,b].
    A = f(a,b);

    no
  16. (=:=)/2 : numeric comparison
  17. (=<)/2 : numeric comparison
  18. (==)/2 : true if args are identical terms
  19. (=\=)/2 : numeric comparison
  20. (>)/2 : numeric comparison
  21. (>=)/2 : numeric comparison
  22. >> /3 : right shifts arg 1 by arg 2 bits
    EXAMPLE(S): 
    ?->>(16,2,A).
    A = 4;

    no
  23. (@<)/2 : instance of compare/3 with arg 1: <
  24. (@=<)/2 : instance of compare/3 with arg 1: = or <
  25. (@>)/2 : instance of compare/3 with arg 1: >
  26. (@>=)/2 : instance of compare/3 with arg 1: > or =
  27. 'C'/3 : DCG connect predicate
  28. \ /3 : bitwise complement
    EXAMPLE(S): 
    ?-\(0,2,A).
    A = -3;

    no
  29. (\+)/1 : succeeds if its argument is executed and fails
  30. \/ /3 : bitwise OR
    EXAMPLE(S): 
    ?-\/(1,2,A).
    A = 3;

    no
  31. (\=)/2 : true if args fail to unify
  32. (\==)/2 : true if arg 1 is not identical to arg 2
  33. ^ /2 : calls arg 2 and binds arg 1
    EXAMPLE(S): 
    ?-A^eq(A,1).
    A = 1;

    no
  34. abolish/1 : abolish(F/N) deletes predicate F/N
  35. abort/0 : returns to toplevel
  36. and/2 : conjunction, like comma
  37. append/3 : concatenates/decomposes lists
    EXAMPLE(S): 
    ?-append([1,2],[3,4],A).
    A = [1,2,3,4];

    no
    ?-append(A,B,[1,2]).
    A = [];
    B = [1,2];

    A = [1];
    B = [2];

    A = [1,2];
    B = [];

    no
  38. arg/3 : arg(I,T,X) extracts arg I of term T to be unified with X
    EXAMPLE(S): 
    ?-arg(2,f(a,b),A).
    A = b;

    no
  39. assert/1 : adds a clause
  40. asserta/1 : adds a clause to be first in a predicate definition
  41. assertz/1 : adds a clause to be last in a predicate definition
  42. atom/1 : true if symbol (functor of arity 0)
    EXAMPLE(S): 
    ?-atom(a).

    yes
  43. atom_chars/2 : (Atom,CharAtoms): converts between an atom and its list of char atoms representation
    EXAMPLE(S): 
    ?-atom_chars(hello,A).
    A = [h,e,l,l,o];

    no
    ?-atom_chars(A,[104,101,108,108,111]).
    no
  44. atom_codes/2 : (Atom,CharCodes): converts between an atom and its list of char code representation
    EXAMPLE(S): 
    ?-atom_codes(hello,A).

    A = [104,101,108,108,111];

    no
    ?-atom_codes(A,[104,101,108,108,111]).
    A = hello;

    no
  45. atomic/1 : true if an integer or symbolic constant
  46. bagof/3 : all solutions predicate generating unsorted bags of possibly dupplicated answers
    EXAMPLE(S): 
    ?-bagof(A,member(A,[3,2,2,1]),B).
    A = _x56274;
    B = [3,2,2,1];

    no
  47. bb_def/3 : bb_def(K1,K2,T) associates to K1 and K2 (a copy of) T on the blackboard
  48. bb_let/3 : bb_let(K1,K2,T) updates or defines the term associated with K1 and K2 to be T
  49. bb_rm/2 : removes the term associated with K1 and K2 from the blackboard
  50. bb_set/3 : bb_set(K1,K2,T) updates the term associated with K1 and K2 to be a copy of T
  51. bb_val/3 : bb_val(K1,K2,T) T is (a copy of) the term associated with keys K1 and K2
  52. bg/1 : runs Goal in background thread
  53. bg/2 : runs Goal in new background thread, which is returned in second arg
  54. jboot/0 : regenerates file wam.bp in psrc directory (from where it should be run)
  55. call/1 : executes (atomic!) arg 1
  56. call/2 : efficient call/N variant
  57. call/3 : efficient call/N variant
  58. call/4 : efficient call/N variant
  59. catch/3 : ISO Prolog exception operator: executes arg 1 and if it catches arg 2, it executes arg 3
  60. clause/2 : clause(H,B) generates a clause with head matching H and body B
  61. compare/3 : returns <,=,> in arg 1 after comparing arg 2 with arg 3
    EXAMPLE(S): 
    ?-compare(A,1,2).
    A = (<);

    no
    ?-compare(A,f(b),f(a)).
    A = (>);

    no
    ?-compare(A,s(B),s(B)).
    A = (=);
    B = _x56284;

    no
  62. compile/1 : applies current compilation method to the file arg 1
  63. compound/1 : true if it has arity > 0
  64. EXAMPLE(S):
    ?-compound(f(a)).

    yes

  65. consult/1 : consults with possible duplication of clauses, allows later dynamic recompilation depending on db_ratio/1
  66. copy_term/2 : returns a copy of arg 1 with fresh variables
    EXAMPLE(S): 
    ?-copy_term(f(A,A,B,B),C).
    A = _x56275;
    B = _x56277;
    C = f(_x56718,_x56718,_x56720,_x56720);

    no
  67. new_engine/3 : new_engine(Answer,Goal,IntHandle) creates an engine IntHandle
  68. get/2 : get(EngineOrFluent,Answer): gets an answer the(X) orno from an engine or other source fluent
  69. stop/1 : stops or closes engine or other fluent
  70. ctime/1 : gets elapsed cpu time in ms
    EXAMPLE(S): 
    ?-ctime(A).
    A = 5247;

    no
  71. get_db/1 : gets the name (a Java Object) of currently active database
  72. current_thread/1 : gets thread id number of current thread
  73. db_abolish/2 : db_abolish(DB,F/N) removes predicate F/N from DB
  74. db_assert/2 : does assert/1 arg 2 into database given as arg 1
  75. db_asserta/2 : does asserta/1 arg 2 into database given as arg 1
  76. db_assertz/2 : does assertz/1 arg 2 into database given as arg 1 ???
  77. db_clause/3 : clause(DB,H,B) generates a clause found in database DB with head matching H and body B
  78. db_clean/0 : abolishes all predicates in currently active database
  79. db_clean/1 : db_clean(DB) abolishes all predicates in DB
  80. db_move/2 : db_move(FromDB,ToDB) moves the content of database FromDB over database ToDB while replacing similar predicates ???
  81. db_retract/2 : does retract/1 arg 2 from database given as arg 1
  82. db_retract1/2 : deletes from database given as arg 1 a matching clause
  83. db_retractall/2 : removes from database given as arg 1, all matching clauses
  84. db_save/1 : db_save(File) saves all the clauses of the current database to File
  85. db_save/2, db_load/2, db_clean/2 : db_save(Db,File) saves using qprint/1 all the clauses of Db to File, db_load loads it, db_clean resets it to empty
  86. display/1 : writes to terminal while ignoring operator definitions
  87. end_of_file/0 : Prolog atom returned by read when at the end of a file
  88. eq/2 : unifies arg 1 and arg 2, like =
  89. errmes/2 : writes error message and fails
  90. exists_file/1 : true if file exists
  91. return/1 : leaves current engine with arg 1 as returned value instead of computed answer
  92. expand_term/2 : expands a term according to DCG expansion rules
  93. fail/0 : always fails
  94. findall/3 : findall(X,G,Xs) collects copies of all answers X of G to Xs. If less then half of the heap is free, it allocates new engine for running G
    EXAMPLE(S): 
    ?-findall(s(A),(member(A,[1,2,3]),A 
    >  1),B).
    A = _x56278;
    B = [s(2),s(3)];

    no
  95. findall/4 : findall(X,G,Xs,Ys) appends the list of answers X of G to Ys to obtain Xs
  96. EXAMPLE(S):
    ?-findall(s(A),(A = 1 ; A = 2),B,[3,4]).
    A = _x56285;
    B = [s(1),s(2),3,4];

    no

  97. float/1 : true if represented as a 64 bit float number (C-double)
    EXAMPLE(S): 
    ?-float(3.14).

    yes
  98. floor/2,ceiling/2,round/2 : float to int functions
    EXAMPLE(S): 
    ?-floor(1.3,A).
    A = 1;

    no
  99. foldl/4 : (Op,InitialValue,List,?Result) accumulates values interating over List with binary Op
    EXAMPLE(S): 
    ?-foldl(+,0,[10,20,30],A).
    A = 60;

    no
  100. foldr/4 : (Op,InitialValue,List,?Result) accumulates values interating over List with binary Op
    EXAMPLE(S): 
    ?-foldr(+,0,[10,20,30],A).
    A = 60;

    no
  101. for/3 : generates an integer in a range
    EXAMPLE(S): 
    ?-for(A,1,3).
    A = 1;

    A = 2;

    A = 3;

    no
  102. forall/2 : forall(A,B) for each answer returned by A computes and igonres all answers comuted by B without any binding - used for its side effects - (note: not expressed with the usual double negation trick - this really makes sure all alternatives of A are explored!)
  103. functor/3 : builds or decomposes a coumpound term
    EXAMPLE(S): 
    ?-functor(f(a,b),A,B).
    A = f;
    B = 2;

    no
    ?-functor(A,f,3).
    A = f(_x56878,_x56879,_x56880);

    no
    ?-functor(f(a),f,1).

    yes
  104. gensym/2 : generates a new name based on arg 1
  105. get/1 : inputs the next char code after skiping over white space
  106. get0/1 : reads a char as an ascii code
  107. get_char/2 : (Stream,CharAsOneLetterConstant): inputs a char from a stream -ISO Prolog ???
  108. get_code/1 : ISO char code reader ???
  109. get_code/2 : inputs a char code from a stream - ISO ???
  110. get_password/1 : gets default password for user ???
  111. ground/1 : true if arg has no free variables  ???
    EXAMPLE(S): 
    ?-ground(f(a,b)).

    yes
  112. halt/0 : stops Prolog
  113. if_any/3 : (Cond,Then,Else): executes Cond; each time when Cond succeeds it also executes Then; if Cond never succeds it executes Else
  114. in/1 : waits to remove a term from Linda server
  115. integer/1 : true if an integer
  116. integer/2 : float to int cast ???
  117. interactive/1: toggles interactive query answering/tracing with arg 1 = yes or no
  118. (is)/2 : calls the function evaluator, mostly for arithmetics
    EXAMPLE(S): 
    ?-A is 3+4*2.
    A = 11;

    no
  119. is_builtin/1 : recognizes a predicate head as a builtin
    EXAMPLE(S): 
    ?-is_builtin(var(A)).
    A = _x56271;

    no
  120. is_compiled/1 : true if head of a compiled predicate
  121. is_dynamic/1: checks if dynamic
  122. is_prolog/1 : recognizes two modes jinni_compiled and jinni_interpreted - useful for portability
  123. keysort/2 : sorts while grouping similar keys
    EXAMPLE(S): 
    ?-keysort([3-a,1-a,2-b,1-c,2-d],A).
    A = [1-a,1-c,2-b,2-d,3-a];

    no
  124. length/2 : generates/mesures length of a list
  125. listing/0 : lists current database
  126. listing/1 : lists given predicate if in current database
  127. listing/2 : lists predicate F of arity N if in current database
  128. log/2 : float function  ???
  129. log/3 : returns log in base arg 1 of arg 2, a float ???
    EXAMPLE(S): 
    ?-log(2,8,A).
    A = 3;

    no
  130. login/1 : assumes default (nick)name for user ???
  131. main/0 : user defined optional startup predicate
  132. main/1 : default entry predicate
  133. map/3 : maps a predicate with 2 args to a list
    EXAMPLE(S): 
    ?-map(+1,[10,20],A).
    A = [11,21];

    no
  134. max/3 : (X,Y,Max): Max is the max of 2 numbers X, Y
  135. member/2 : (X,Xs): checks if an element X unifies with an element on a list Xs or generates successively longer lists if Xs is unbound or open ended
    EXAMPLE(S): 
    ?-member(2,[1,2]).

    yes
    ?-member(A,[1,2]).
    A = 1;

    A = 2;

    no
  136. merge_sort/3 : (Order,List,Sorted)
    EXAMPLE(S): 
    ?-merge_sort(>,[1,3,2,2,4],A).
    A = [4,3,2,2,1];

    no
  137. metacall/1 : calls the interpreter
  138. min/3 : (X,Y,Min): Min is the min of 2 numbers X, Y
  139. mod/3 : modulo
    EXAMPLE(S): 
    ?-mod(10,3,A).
    A = 1;

    no
  140. move/0 : first key Mobile Code operation: picks up wrapped continuation and moves to remote site is there/0 has been set, or runs it locally if here/0 has been set ???
  141. name/2 : bi-directional: converts atomic to/from list of chars
    EXAMPLE(S): 
    ?-name(hello,A).
    A = [104,101,108,108,111];

    no
    ?-name(A,[98,121,101]).
    A = bye;

    no
  142. namecat/4 : concatenates 3 names
    EXAMPLE(S): 
    ?-namecat(a,:,b,A).
    A = a:b;

    no
  143. new/2 : new(ClassFile,Instance): creates class instance using code from file, i.e. new('progs/nrev',Instance)
  144. nl/0 : writes a new line character
  145. nonvar/1 : true if currently instantiated
  146. not/1 : negation as failure
  147. notify_about/1 : notifies a suspended matching wait_for(Term,Contraint), if Constraint holds, that Term is available
  148. nth_member/3 : retrieves N-th element of a list
    EXAMPLE(S): 
    ?-nth_member(A,[a,b,c],B).
    A = a;
    B = 1;

    A = b;
    B = 2;

    A = c;
    B = 3;

    no
  149. number/1 : true if integer or float
  150. number_chars/2 : (Number,CharAtoms): converts between a number and its list of char atoms representation
    EXAMPLE(S): 
    ?-number_chars(1999,A).
    A = [1,9,9,9];

    no
  151. number_codes/2 : (Number,CharCodes): converts between a number and its list of char code representation
    EXAMPLE(S): 
    ?-number_codes(1999,A).
    A = [49,57,57,57];

    no
    ?-number_codes(A,[50,48,48,49]).
    A = 2001;

    no
  152. numbervars/3 : binds to $VAR(I) with I over distinct integers variables in a term
  153. out/1 : puts a term on Linda server or trigers resumption of a matching in/1 waiting for this data
  154. password/1 : assumes default password for user ???
  155. phrase/2 : (Axiom, ?InputChars): DCG evaluator, starting from Axiom, consuming/producing InputChars
  156. phrase/3 : (Axiom, ?InputChars, ?OutputChars): DCG evaluator, staring from Axiom
    EXAMPLE(S): 
    ?-phrase(([a],[b]),[a,b|A],A).
    A = _x56276;

    no
  157. pow/3 : (Base,Expo,Val) computes power function
    EXAMPLE(S): 
    ?-pow(2,3,A).
    A = 8;

    no
  158. pp_clause/1 : prints out a clause with some care on how it looks
  159. println/1 : synchronized printing of a term on a line
  160. prod/2 : (List, ?Result): product of a list
    EXAMPLE(S): 
    ?-prod([10,20],A).
    A = 200;

    no
  161. put/1 : writes and ascii code as a char
    EXAMPLE(S): 
    ?-put(99).
    c
    yes
  162. put_char/2 : (Stream,CharAsOneLetterConstant): outputs a char to a stream -ISO Prolog
  163. put_code/1 : ISO char code writer
    EXAMPLE(S): 
    ?-put_code(99).
    c
    yes
  164. put_code/2 : outputs a char code to a stream - ISO
  165. qprint/1 : prints out a clause such that a variant of it can be always read back ???
  166. random/1 : returns a random integer
    EXAMPLE(S): 
    ?-random(A).
    A = 3439;

    no
  167. qcompile/1 : compiles quickly to memory
  168. rd/1 : reads a term matching arg 1 from Linda server
  169. read/1 : reads a term
  170. read_term/2 : reads a term and also a list of variable-name associations
  171. sread/2 : reads a term from a constant
    EXAMPLE(S): 
    ?-sread(f(X,X,Y,Y),A).
    A = f(_1,_1,_2,_2)-['X'=_1,'Y'=_2];

    no
  172. reconsult/1 : reconsults a file to current database as interpreted code, replaces duplicate predicate definitions already existing
  173. remote_run/1 : runs Goal on localhost Linda server using default password
  174. remote_run/3 : (Answer,Goal,Result): runs Goal on loclhost server and binds Answer with result
  175. remote_run/6 : (Host,Port,Answer,Goal,Password,Reply): if Password matches password of the remote server, runs Goal there and returns Reply from server
  176. repeat/0 : backtracks until its continuation succeeds; defined as repeat. repeat:-repeat.
  177. restart/0 : cleans up data areas and reinitializes symbol tables
  178. retract/1 : backtracks over deleting matching clauses
  179. retract1/1 : deletes first matching clause in the current database
  180. retractall/1 : deletes all matching clauses
  181. return/1 returns a value (arg 1) as answer of an engine
  182. return/0 : second key Mobile Code operation: forces the moved continuation to come back and run left over goals ???
  183. round/2 : float to int function  ???
    EXAMPLE(S): 
    ?-round(1.51,A).
    A = 2;

    no
  184. rtime/1 : gets elapsed real time in secs since start ???
    EXAMPLE(S): 
    ?-rtime(A).
    A = 6;

    no
  185. run_server/0 : runs foreground server on localhost, on a free port
  186. run_server/1 : runs foreground server on given or on free Port
  187. run_server/2: (Port,Passwd): runs server protected with Passwd  on Port - server will answer remote_run requests
  188. see_at/1 : seeks a seekable file at a give offset (in bytes) ???
  189. see_or_fail/1 : opens a file if it exists, otherwise fails
  190. seeing/1 : gets file name opened and set by see/1
  191. seeing_at/1 : retrieves position in current file opened by see/1 ???
  192. seen/0 : close file opened by see/1
  193. set_input/1 : sets current input stream ???
  194. setof/3 : all solutions predicate generating sorted sets of unduplicated answers
    EXAMPLE(S): 
    ?-setof(A,member(A,[3,2,2,1]),B).
    A = _x56274;
    B = [1,2,3];

    no
  195. sign/2 : int function ???
  196. sin/2 : float function
  197. sort/2 : sorts and removes duplicates -see also merge_sort/3-
    EXAMPLE(S): 
    ?-sort([2,1,3,1,4,4,2],A).
    A = [1,2,3,4];

    no
  198. spy/1 : set spy point on goal, triggering trace when interpreted
  199. spying/1 : checks what we are spying
  200. sqrt/2 : returns square root of arg 1, a float ???
    EXAMPLE(S): 
    ?-sqrt(2,A).
    A = 1.41421;

    no
  201. sread/2 : reads a term from a string (atom) :
    ?-sread('f(X,Y)',A-B).
    A = f(_x56645,_x56646);
    B = ['X' = _x56645,'Y' = _x56646];

    no

  202. stat/0 : short hand for statistics
  203. statistics/0 : shows info about data areas
    EXAMPLE(S): 
    ?-statistics.
    runtime = [1191,1191]
    global_stack = [199,824]
    local_stack = [5,3]
    trail = [7,1]
    code = [12784,19984]
    symbols = [552,0]
    htable = [776,1272]


    yes
  204. statistics/2 : returns info about data areas
  205. stop/0 : exits current engine
  206. stop/1 : stops and frees resources held by an engine or other fluent (may happen automaticaly if an engine fails)
  207. sum/2 : (List,?Result): sum of a list
    EXAMPLE(S): 
    ?-sum([10,20],A).
    A = 30;

    no
  208. swrite/2 : writes a term to a string (atom)
  209. symcat/3 : makes new identifier from arg 1 and arg 2
    EXAMPLE(S): 
    ?-symcat(a,b,A).
    A = a_b;

    no
    ?-symcat(a,1,A).
    A = a_1;

    no
  210. system/1 : passes a command to the OS
  211. system/2 : passes a command to the OS and gets back return code ???
  212. tab/1 : outputs N blanks
  213. talk/0 : sends what you type in, to another BinPrologJ   user ???
  214. tan/2 : float function
  215. tell/1 : focuses output on a file
  216. tell_at/1 : moves output file pointer to a given offset (in bytes) ???
  217. tell_at_end/1 : focuses output on file opened in append mode ???
  218. telling/1 : gets file name opened and set by tell/1
  219. telling_at/1 : retrieves output file position (in bytes)
  220. term_append/3 : efficiently concatenates 2 terms
    EXAMPLE(S): 
    ?-term_append(f(a,b),g(c,d),A).
    A = f(a,b,c,d);

    no
  221. term_expansion/2 : can be used to define a hook into the default DCG expansion mechanism
  222. term_hash/2 : computes hash code on main functor and functors of arguments; fails if something is unbound ???
    EXAMPLE(S): 
    ?-term_hash(t(a,b),A).
    A = 40050;

    no
    ?-term_hash(t(a,c),A).
    A = 40051;


    no
  223. term_hash/3 : computes hash code based on main functor and functors of listed argument positions; fails if something is unbound ???
    EXAMPLE(S): 
    ?-term_hash(t(a,b),[1,2],A).
    A = 40050;

    no
    ?-term_hash(t(a,c),[1,2],A).
    A = 40051;

    no
  224. this/1 : returns current object handle ???
  225. throw/1 : ISO Prolog exception operator: throws a term to be caught by a matching catch
  226. told/0 : closes file opened by tell/1
  227. topcall/1 : calls arg 1 and signals uncaught exception thrown by ISO Prolog built-in throw/3
  228. toplevel/0 : interactive toplevel Prolog loop
  229. true/0 : always succeeds
  230. truncate/2 : float to int function ???
    EXAMPLE(S): 
    ?-truncate(1.51,A).
    A = 1;

    no
  231. var/1 : true if currently an unbound variable
  232. wait_for/2 : wait_for(Term,Constraint) waits for a term on the blackboard, such that Constraint hold
  233. write/1 : writes to current output stream set with tell/1, defaults to Prolog's stdio
     

The BinPrologJ GUI

Visual BinPrologJ  is a GUI library built through a set of reflection, engines, threads based Java and Prolog interaction. The Prolog side  works as a high-level visual scripting language on top of a Personal Java (roughly jdk 1.1.8). As a result, it runns happily on Java enabled  PocketPCs lik Compaq's iPAQ with the Jeode Java runtime. We will reproduce here the complete Prolog code - which is self-explanatory - hoping that it gives an idea on the power and flexibility of BinPrologJ's reflection based interface - and on putting the various capabilities of BinPrologJ together.

/*
  Reflection based GUI 
  - the default AWT GUI can be overriden by an external Swing based
    alternative Java implementation
  - looking the same from Prolog, although the external GUI is expected to
    offer extra functionality with time
*/

/* builtin to Java connection tools - based on Reflection based interface */

get_gui_class(CName):-get_global_prop(gui,swing),!,CName='jgui.Start'.
get_gui_class('prolog.core.GuiBuiltins').

call_gui_method(MethodAndArgs):-call_gui_method(MethodAndArgs,_).

call_gui_method(MethodAndArgs,Result):-
   get_gui_class(CName),
   call_java_class_method(
      CName,
      MethodAndArgs,
      Result
   ).


/* builtins */

show(Container)
 
resize(Component,H,V)

move(Component,H,V)

/* creates a new frame - top window */

new_frame(Frame):

new_frame(Title,Frame):

new_frame(Title,Layout,Frame):
 
new_frame(Title,Layout,Kind,Frame):

new_panel(Parent,Layout,Panel):

new_label(Parent,Name,Label):

new_button(Parent,Name,Action,Button):

new_button(Parent,Name,Action,Output,Button):

set_label(Label,String):

new_file_dialog(Mode,Result):

new_text(Parent,Component):

new_text(Parent,String,Component):
  
new_text(Parent,String,Rows,Cols,Component):

set_text(Component,String):

% direct access to underlying TextSink - for visual displays only

set_text(String):

add_text(String):

get_text(String):

clear_text:

clear_text(Component):

% end of TextSink API

add_text(Component,String)

get_text(Component,String):

set_max_display(Max):

/* Colors */

new_color(R,G,B,Color):

make_white(Color):
make_blue(Color):
make_light_blue(Color):
make_gray(Color):
make_green(Color):
make_red(Color):
make_black(Color):
  
set_fg(Component,Color):

set_bg(Component,Color):

set_color(Component,Color):

/* Default Colors */

set_fg_color(R,G,B):

set_bg_color(R,G,B):

   
/* Default Fonts */

set_font_name(Name):
set_font_style(Style):
set_font_size(Size):
inc_font_size(Size):

to_default_font(Component):

remove_all(Container):

destroy(Component):
 

set_layout(Container,Layout):

/* used on Panels and Frames with border layout */

set_direction(Container,String):

/* Prolog IDE - contains simple editor and console */

ide:

ide(Name):

ide(Name,Query):

new_ide(Name):

new_ide(Container,Query):

new_ide(Container,Query,Output):
 
new_ide(Container,Query,Output,EditArea):

/* simple Prolog Console - reads, evaluates, prints answers */
     
console:
     
new_console:

new_console(Query):
  
new_console(Container,Query):

new_console(Container,Query,Output):

new_console(Container,Query,Rows,Cols, Output):

/* Prolog Dialog Box - implemented using Hubs - to synchronize consumer and producer threads*/
dialog(Q,A):

dialog(Q,WhereX,WhereY,A):

dialog(Q,Y,N,WhereX,WhereY,SizeX,SizeY,A):

dialog_in(Parent,Q,A):

dialog_in(Parent,Q,Y,N,A):

/* reads a string from a new box - from which sread can be used
   to extract Prolog terms */
     
gui_readln(StringRead):
  
/* Prolog Editor - squeeze to small default initial size size to
   fit on PocketPCs - resize at will ! */  

new_file_editor:

new_file_editor(Container,Editor):


The Prolog3D Java3D-based Agent-Oriented 3D-Animation and 3D-Graph Layout API


This BinPrologJ extension requires installing the Java3D open source component, available as a Java extension module from java.net. The API is Agent-Oriented - i.e. each element of the 3D world is an agent implemented as an autnomous Java3D behavior.  The basic idea of this API is very simple: a Prolog user can update/retrive the state of an agent at will. The Java3D renderier will periodically revisit and render the current state of each agents. Besides efficiency, this provides an interaction model which can meke use of verious local and remote BinPrologJ resources independly of the Java3D animation and geomtry rendering process.

From a Prolog user's perspective agent animation is achieved simply by sending translation, rotation and scaling commands (or can be put on autopilot). A combination of Java3D custom made shapes, loaded *.obj models built with external tools and Prolog based planning or knowledge processing allows a programmer to build complex 3D programs like games, data visualisations, virtual reality applications etc.

As an application of the API we have developed an agent-based 3D layout tool where vertices and edges are represented as agents driven by a combination of spectral and force based layour algorithm. The layout tool allows printing the resulting graphs and saving them as PNG images. After the layout algorithm terminates, a compact "frozen graph" representation is also provided that represents graphs containing thousands of nodes as a single point/line geometry node.


% shows a Prolog3D canvas

show3d(RG):-show3d(RG,20,400,400,400).

% shows a Prolog3D canvas for Timout msecs in a world
% with given 3D radius and screen dimensions
show3d(RG,TimeOut,Radius,Width,Height):

% creates a new 3D world in which various 3D agents are placed
new_world(U)

% simple objects represented as agents
sphere(U,A)
point(U,A)
line(U,A)
tetra(U,A)
box(U,A)
color_cube(U,A)
pyram(U,A)
cone(U,A)
cylinder(U,A)
text2D(U,Text,A)
text3D(U,Text,A)
model3D(U,FileOrURL,A): imports Wavefront OBJ models as animated agents

% creation of agents=object+behaviors

% adds an agent with text content (i.e. 2D or 3D text)
add_agent(U,Shape,Text,A):

% adds an agent of a given shape 
add_agent(U,Shape,A):

% adds randomly shaped/positioned agent
add_agent(U,A)

% adds a vertex agent - to be positioned by a LayoutEngine
vertex_agent(U,L,V)

% adds an edge agent - to be positioned by a LayoutEngine 
edge_agent(U,From,To,Label,E)
 
% adds an edge agent - represented as an animated object moving from source to target
mobil_edge_agent(U,From,To,Label,E)
 
% display the world in frame of given size    
show_world(U,Width,Height)

% displays a default size world
show_world(U):-show_world(U,400,400).
 
% destroys world U 
stop_world(U)

% stops and discards an agent
stop_agent(A)

% resets an agent in center of the world and default parameters
reset_agent(A)

% makes an agent invisible
hide_agent(A)

% makes an agent visible again
show_agent(A)

% moves an agent A to coordinates X,Y,Z
move_to(A,X,Y,Z)

% rotates an agent A using 3 Euler angles X,Y,Z
rotate_to(A,X,Y,Z)

% scales an agent A uniformly to S
scale_to(A,S)
 
% scales an agent by different rations on each coordinate 
scale_to(A,X,Y,Z)

% increment an agent's current postion by X,Y or Z
inc_x(A,X)
inc_y(A,Y)
inc_z(A,Z)

% rotates an agent by a give Euler angle X,Y or Z
inc_rot_x(A,X)
inc_rot_y(A,Y)
inc_rot_z(A,Z)

% sets the time after which the renderer will redisplay
% changes made to the state of the agent
set_wake_up(A,Tick)

% starts/stops autmatic random animation for agent A 
auto(A,BoolString)

% gets current X,Y or Z coordinate of an agent
get_x(A,R)
get_y(A,R)
get_z(A,R)

% gets current X,Y or Z Euler angle rotation of an agent
get_rot_x(A,R)
get_rot_y(A,R)
get_rot_z(A,R)

% gets current X,Y or Z scale factor of agent A
get_scale_x(A,R)
get_scale_y(A,R)
get_scale_z(A,R)

% gets the complete translation/rotation/scale of an agent
get_agent_state(A,[pos(X,Y,Z),rot(RX,RY,RZ),scale(SX,SY,SZ)]):-
  get_x(A,X),
  get_y(A,Y),
  get_z(A,Z),
  get_rot_x(A,RX),
  get_rot_y(A,RY),
  get_rot_z(A,RZ),
  get_scale_x(A,SX),
  get_scale_y(A,SY),
  get_scale_z(A,SZ).

% returns a string rrepresentation of agent A
agent_to_string(A,S)

ViewPoint animation is supported by either setting the view directly or by "attaching" the ViewPoint to any agent. As the agent moves, the viewpoint is following it a proviedes a first person view centered on the agent.

% sets the coordinates of the 'camera' the world is viewed from   
set_view(U,X,Y,Z):-
  invoke_java_method(U,setView(X,Y,Z),_).

% sets a given agent as a 'holder' of the 'camera' that will follow its transforms  
set_holder(U,A):-
  invoke_java_method(U,setHolder(A),_).

remove_holder(U):-
  invoke_java_method(U,removeHolder,_). 

Demos:

Type test3d to run a demo test suite for Prolog3D.

Creating a set of animated agents in Prolog3D is very easy, as seen in the following examples:

simple3d:-
  N=6,
  new_world(U),
  findall(A,(
    for(I,0,N),
      add_agent(U,I,A),
      Z is -(1+I),inc_z(A,Z),
      Y is (I-N/2)/3,inc_y(A,Y)
    ),As),
  show_world(U),
  sleep(5),
  foreach(member(A,As),auto(A,true)),
  sleep(10),
  foreach(member(A,As),stop_agent(A)),
  stop_world(U).

other3d:-
  N=3,Txt='Hello!',
  new_world(U),
  findall(A,(
    for(I,0,N),
      add_agent(U,I,Txt,A),
      Z is -(1+I),inc_z(A,Z),
      Y is (I-N/2)/3,inc_y(A,Y)
    ),As),
  show_world(U),
  sleep(10),
  foreach(member(A,As),auto(A,true)),
  sleep(10),
  foreach(member(A,As),stop_agent(A)),
  stop_world(U).
 
g31:-
  new_world(U),
  text3D(U,'Rotate me!',A),
  show_world(U),
  sleep(10),
  stop_agent(A),
  stop_world(U).
  
g32:-
   new_world(U),
   sphere(U,A),
   show_world(U,240,320),
   sleep(10),
   stop_agent(A),
   stop_world(U).
  
g33:-
   new_world(U),
   color_cube(U,A),
   show_world(U),
   sleep(5),
   inc_z(A,-1),
   set_wake_up(A,500),
   (for(_I,1,10),
     inc_rot_z(A,0.1),
     inc_x(A,0.1),
     get_agent_state(A,S),
     println(state=S),
     % sleep(1),
     fail
   ;true
   ),
   (for(_I,1,10),
     inc_rot_z(A,0.1),
     inc_x(A,-0.1),
     get_agent_state(A,S),
     println(state=S),
     % sleep(1),
     fail
   ;true
   ),
   sleep(5),
   auto(A,true),
   sleep(10),
   stop_agent(A),
   stop_world(U).
  
   
g34:-    
   new_world(U),
   text3D(U,'Hiding!',A),
   inc_x(A,0.2),
   show_world(U),
   (for(_,1,3),
     sleep(5),
     println(hiding=A),
     hide_agent(A),
     sleep(5),
     println(showing=A),
     show_agent(A),
     fail
   ; true
   ),
   stop_agent(A),
   stop_world(U).

% graph layout demos
   

gr1:-
  new_world(U),
  vertex_agent(U,hello,V1),move_to(V1,-0.5,0.5,-0.5),
  vertex_agent(U,bye,V2),move_to(V2,0.5,0,-0.5),
  edge_agent(U,V1,V2,bye,_E),
  show_world(U),
  sleep(10),
  stop_world(U).

gr:-
  random_ranked_graph(G,Info),
  trim_ranked_graph(G,2,20,TG),
  println(Info),
  show3d(TG).
  
gr2:-
  new_world(U),
  gr2(U),
  show_world(U),
  sleep(15),
  stop_world(U).
         
gr2(U):-
  random_ranked_graph(G,Info),
  println(Info),
  findall(V-VA,(
       vertex_of(G,V),
       % vertex_data(G,V,VD),
       VD=V,
       vertex_agent(U,VD,VA),
       auto(VA,true)
     ),
     VAs),
   member(V-VA,VAs),
     outgoing_of(G,V,To),
       member(To-ToA,VAs),
       %edge_data(G,V,To,ED),
       ED=e(from=V,to=To),
         edge_agent(U,VA,ToA,ED,_EA),
  fail.
gr2(_).         
  

The BinPrologJ  Google API adaptor

The adaptor (available as part of the BinNet Internet Progrtamming Toolkit and theBinNet Natural Language Processing Toolkit) allows building BinPrologJ agents which can extract high level data in Prolog form by using the Google Web Services API (subject to obtain your own free 1000 hits/day Google search key code from them and download their last Java based API adaptor).

?- url_of('Free Lunch',URL).
URL = http://www.nofreelunch.org/
;
URL = http://www.economy.com/freelunch/
;
URL = http://www.freelunchdesign.com/
...

?- snippet_of('Agent Programming with BinPrologJ',Answer).
Answer = 'Agent Programming Constructs and Object Oriented Logic Programming in BinPrologJ .
The BinPrologJ  Ontology: Orthogonal Language Constructs for Agent Programming'

?- category_of('Agent Programming',Answer).
Answer = Top/Computers/Programming/Agents/Microsoft_Agent

?- title_of('Matrix Reloaded',Answer).
Answer = The Matrix: Revolutions
;
Answer = Apple - Trailers - Matrix Reloaded
;
Answer = The Editing Room - The Matrix Reloaded: The Abridged Script
....

?- summary_of('The Matrix Reloaded',Answer).
Answer = Official site for the series. Cast and crew biographies, interviews,
ph otographs, production notes,...
;
Answer = A short academic analysis of religious allegories in the film.
...

The BinPrologJ  SAX 2.0 based XML Processing Component


Located in the jinni_xml sub-directory of the xjinni2k component collection, the XML Processor contains the following:
Located in the jinni_xml sub-directory of the xjinni2k component collection, the XML Processor contains the following:

The BinPrologJ  adaptor to the SAX 2.0 event based XML Parser

The SAX 2.0 parser coming with Sun's Java (1.4 or later) is event based. As the parser walks through an XML file - a number of events are generated. They are calls to a DefaultHandler instance that can be overriden selectively by the application programmer. Instead of the naive approach consisting off attaching these events to BinPrologJ calls resulting in side effects on the Prolog side we have set up a producer-consumer mechanism which allows a BinPrologJ thread to collect and process XML events in a natural and (if the programmer prefers so) completely side effect free style. Each class of BinPrologJ's handler hierarchy implements the interface Runnable - as it will run on a separate thread communicating with its Prolog peer. Our design is meant to be easily extended both on the Prolog and the Java side. This component comes in source form - to allow quick changes and fixes for your application. If you wish, some of those changes and improvements will be merged in our source code.

The class hierarchy is organized as follows:

Class Hierarchy



FullXMLHandler

This class sends events to Prolog for each member of the DefaultHandler SAX adaptor. It ensures that everything a programmer can do in Java with these events can also be done in Prolog. It provides a "complete" mapping from the set of events provided by the DefaultHandler of the SAX parser to similarly named Prolog terms. Each of the following methods is sent to Prolog as a term to be handled by xml_event_of(FileOrURL,Event) which backtracks over the set of XML events resulting from calling the SAX parser on a file or URL.

Method Summary
 void characters(char[] buf, int offset, int len)
          Receive notification of character data inside an element.
 void endDocument()
          Receive notification of the end of the document.
 void endElement(java.lang.String namespaceURI, java.lang.String sName, java.lang.String qName)
          Receive notification of the end of an element.
 void endPrefixMapping(java.lang.String prefix)
           
 void error(org.xml.sax.SAXParseException e)
           
 void fatalError(org.xml.sax.SAXParseException e)
           
 void ignorableWhitespace(char[] ch, int start, int length)
           
 void notationDecl(java.lang.String name, java.lang.String publicId, java.lang.String systemId)
           
 void processingInstruction(java.lang.String target, java.lang.String data)
           
 void setDocumentLocator(org.xml.sax.Locator locator)
           
 void skippedEntity(java.lang.String name)
           
 void startDocument()
          Receive notification of the start of the document.
 void startElement(java.lang.String namespaceURI, java.lang.String sName, java.lang.String qName, org.xml.sax.Attributes attrs)
           
 void startPrefixMapping(java.lang.String prefix, java.lang.String uri)
           
 void unparsedEntityDecl(java.lang.String name, java.lang.String publicId, java.lang.String systemId, java.lang.String notationName)
           
 void warning(org.xml.sax.SAXParseException e)
           
 
Methods inherited from class xml_converter.GenericBinPrologJXMLHandler
bg_run,run,send_term,signal_end


GenericBinPrologJXMLHandler

This class acts as a no-action adaptor for various SAX Parser based Handlers. While it actually calls the parser, it only sends end of processing notification, needed to properly terminate the Prolog side processing.

SimpleXMLTermHandler and XMLTermHandler

These adaptors focus on XML represented handling Prolog terms. They only override the (few) methods needed for the task. A quick look at prolog_data.xml, which will be read in by xml2prolog_parser/2as a set of Prolog terms, gives an idea on the structure of the XML data this class handles.

This root class creates a handler, based on a given input file or URL and Hub. The hub receives SAX events converted to BinPrologJ's external Prolog term representation. This is its PRODUCER side. A CONSUMER Prolog thread pulls out these events as if they were "read" from a stream.

The constructor:
XMLTermHandler(String infile,Hub hub) called from Prolog using BinPrologJ's reflection based Java interface 
creates a handler, based on a given input file and Hub used to communicate with the Prolog side.
XMLTermBuilder

This adaptor focuses on XML represented handling Prolog terms. This class builds, for faster processing - Prolog terms directly - the work happens in Java. It really gives the illusion of having a DOM parser. The complete "syntax tree" it builds is a (set of) Prolog terms. The design can be extended to process other XML data which requires "complete" processing in Java before data is passed to Prolog.
Prolog API

These Prolog predicates reflect the functionality of the previously described Java classes. We will only provide a description of the most important ones here. We refer the reader to xml_converter.pl - where a few 2-3 line predicates describe this API in a self-documenting way.

prolog2xml(PrologFileOrURL,XMLFile): Reflection based interface for Prolog-to-XML converter. The converter takes a file or URL containing Prolog clauses and builts an XML representation in another file.

xml2prolog_parser(XMLFileorURL,Terms): Converter from XML represented Prolog trees to Prolog terms - uses a DCG parser to aggregate low level SAX parser events.

xml_event_of(FileOrURL,Event): backtracks over the set of XML events resulting from calling the SAX parser on a file or URL.

xml_term_of(FileOrURL,Term): backtracks over the set of XML represneted Prolog terms resulting from calling the SAX parser on a file or URL containing such terms

You can use findall/3 to collect all events - or the get/2 a predicate of a new engine to explore the stream of events produced by the last two predicates through a recursive loop.