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:
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.
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.
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/
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.
At the prompt "?-" type in a one line Prolog query, as usual, for instance, something like:
?- write(hello),nl.
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).
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.
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!
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.
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.
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.
:-[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)).
?- 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.
?-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.
'#>'(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.
?- 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.
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).
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).
/*
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 ;
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.
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).
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 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).
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)
read_term(Term,VarsAndNames)
read(Term)
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
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
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
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.
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.
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.
?-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
EXAMPLE(S):
?-*(10,3,A).
A = 30;
no
EXAMPLE(S):
?-+(10,3,A).
A = 13;
no
EXAMPLE(S):
?--(10,3,A).
A = 7;
no
EXAMPLE(S):
?-/(10,3,A).
A = 3.33333;
no
EXAMPLE(S):
?-//(10,3,A).
A = 3;
no
EXAMPLE(S):
?-/\(1,2,A).
A = 0;
no
EXAMPLE(S):
?-<<(1,5,A).
A = 32;
no
EXAMPLE(S):
?-f(A,s(a)) = f(B,B).
A = s(a);
B = s(a);
no
EXAMPLE(S):
?-f(a,b) =.. A.
A = [f,a,b];
no
?-A =.. [f,a,b].
A = f(a,b);
no
EXAMPLE(S):
?->>(16,2,A).
A = 4;
no
EXAMPLE(S):
?-\(0,2,A).
A = -3;
no
EXAMPLE(S):
?-\/(1,2,A).
A = 3;
no
EXAMPLE(S):
?-A^eq(A,1).
A = 1;
no
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
EXAMPLE(S):
?-arg(2,f(a,b),A).
A = b;
no
EXAMPLE(S):
?-atom(a).
yes
EXAMPLE(S):
?-atom_chars(hello,A).
A = [h,e,l,l,o];
no
?-atom_chars(A,[104,101,108,108,111]).
no
EXAMPLE(S):
?-atom_codes(hello,A).
A = [104,101,108,108,111];
no
?-atom_codes(A,[104,101,108,108,111]).
A = hello;
no
EXAMPLE(S):
?-bagof(A,member(A,[3,2,2,1]),B).
A = _x56274;
B = [3,2,2,1];
no
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
yes
EXAMPLE(S):
?-copy_term(f(A,A,B,B),C).
A = _x56275;
B = _x56277;
C = f(_x56718,_x56718,_x56720,_x56720);
no
EXAMPLE(S):
?-ctime(A).
A = 5247;
no
EXAMPLE(S):
?-findall(s(A),(member(A,[1,2,3]),A
> 1),B).
A = _x56278;
B = [s(2),s(3)];
no
no
EXAMPLE(S):
?-float(3.14).
yes
EXAMPLE(S):
?-floor(1.3,A).
A = 1;
no
EXAMPLE(S):
?-foldl(+,0,[10,20,30],A).
A = 60;
no
EXAMPLE(S):
?-foldr(+,0,[10,20,30],A).
A = 60;
no
EXAMPLE(S):
?-for(A,1,3).
A = 1;
A = 2;
A = 3;
no
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
EXAMPLE(S):
?-ground(f(a,b)).
yes
EXAMPLE(S):
?-A is 3+4*2.
A = 11;
no
EXAMPLE(S):
?-is_builtin(var(A)).
A = _x56271;
no
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
EXAMPLE(S):
?-log(2,8,A).
A = 3;
no
EXAMPLE(S):
?-map(+1,[10,20],A).
A = [11,21];
no
EXAMPLE(S):
?-member(2,[1,2]).
yes
?-member(A,[1,2]).
A = 1;
A = 2;
no
EXAMPLE(S):
?-merge_sort(>,[1,3,2,2,4],A).
A = [4,3,2,2,1];
no
EXAMPLE(S):
?-mod(10,3,A).
A = 1;
no
EXAMPLE(S):
?-name(hello,A).
A = [104,101,108,108,111];
no
?-name(A,[98,121,101]).
A = bye;
no
EXAMPLE(S):
?-namecat(a,:,b,A).
A = a:b;
no
EXAMPLE(S):
?-nth_member(A,[a,b,c],B).
A = a;
B = 1;
A = b;
B = 2;
A = c;
B = 3;
no
EXAMPLE(S):
?-number_chars(1999,A).
A = [1,9,9,9];
no
EXAMPLE(S):
?-number_codes(1999,A).
A = [49,57,57,57];
no
?-number_codes(A,[50,48,48,49]).
A = 2001;
no
EXAMPLE(S):
?-phrase(([a],[b]),[a,b|A],A).
A = _x56276;
no
EXAMPLE(S):
?-pow(2,3,A).
A = 8;
no
EXAMPLE(S):
?-prod([10,20],A).
A = 200;
no
EXAMPLE(S):
?-put(99).
c
yes
EXAMPLE(S):
?-put_code(99).
c
yes
EXAMPLE(S):
?-random(A).
A = 3439;
no
EXAMPLE(S):
?-sread(f(X,X,Y,Y),A).
A = f(_1,_1,_2,_2)-['X'=_1,'Y'=_2];
no
EXAMPLE(S):
?-round(1.51,A).
A = 2;
no
EXAMPLE(S):
?-rtime(A).
A = 6;
no
EXAMPLE(S):
?-setof(A,member(A,[3,2,2,1]),B).
A = _x56274;
B = [1,2,3];
no
EXAMPLE(S):
?-sort([2,1,3,1,4,4,2],A).
A = [1,2,3,4];
no
EXAMPLE(S):
?-sqrt(2,A).
A = 1.41421;
no
no
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
EXAMPLE(S):
?-sum([10,20],A).
A = 30;
no
EXAMPLE(S):
?-symcat(a,b,A).
A = a_b;
no
?-symcat(a,1,A).
A = a_1;
no
EXAMPLE(S):
?-term_append(f(a,b),g(c,d),A).
A = f(a,b,c,d);
no
EXAMPLE(S):
?-term_hash(t(a,b),A).
A = 40050;
no
?-term_hash(t(a,c),A).
A = 40051;
no
EXAMPLE(S):
?-term_hash(t(a,b),[1,2],A).
A = 40050;
no
?-term_hash(t(a,c),[1,2],A).
A = 40051;
no
EXAMPLE(S):
?-truncate(1.51,A).
A = 1;
no
/*
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 */<