Tau Prolog is a Prolog interpreter fully implemented in JavaScript. Tau Prolog is not just an implementation of the logic programming resolution mechanism, but a complete Prolog interpreter. Furthermore, the implementation of this interpreter has been directed by the ISO Prolog Standard (see ISO directives, control constructs and builtins, ISO Prolog Conformity Testing).
What makes Tau Prolog special regarding other server-side interpreters is its integration with web pages' elements. Tau Prolog installation is quick and simple: you only need to add a JavaScript file to the header of the web page in which it's intended to be run. On Downloads, you can download a single customized file including Tau Prolog's core and the modules you may require.
Once the customized Tau Prolog file is downloaded and properly located, using it is just a matter of adding a script tag on the header of a web page.
<script type="text/javascript" src="tau-prolog.js"></script>
You can also download the sources separately and add them to the web page one by one. In that case, keep in mind that the core script must be loaded before any other Tau Prolog script since the mechanism that allows instantiating modules is defined on the core. Else, the library won't work.
<script type="text/javascript" src="tau-prolog/core.js"></script>
<script type="text/javascript" src="tau-prolog/lists.js"></script>
...
All Tau Prolog functionality is embedded in a JavaScript object named pl
, which is visible in the global scope. This pl
object is located under the window
object (global
in Node.js).
Tau Prolog is session-oriented. A session allows you to analyse and load multiple programs and modules, as well as submit goals and queries. In order to create a new session, the library provides with the pl.create
function, which returns a pl.type.Session
object (every prototype implemented on Tau Prolog is defined on pl.type
).
var session = pl.create();
This function can receive an optional argument limit
which limits the number of resolution steps that the interpreter can make. This way, the browser won't crash, something which could be possible if the interpreter took too much time to find an answer or if it got caught in an infinite SLD tree. If, while looking for an answer, the interpreter returns null
, this means that it has reached that set limit without finding anything. Nevertheless, if you repeat the query, Tau Prolog will keep looking for an answer starting from the last choice point (where the interpreter was when it returned null
). The default limit is 1000
.
With the aim of analysing and loading programs in a session, the pl.type.Session
prototype includes a consult
method, which receives the program as a string and, if it is correct, adds the rules on it to the database, executing a given callback afterwards. The consult
method can take a string with the Prolog program, an URL/path to a Prolog file, or the identifier id
of a <script>
tag with id="{id}.pl"
and type="text/prolog"
.
session.consult(" \
% load lists module \
:- use_module(library(lists)). \
\
% fruit/1 \
fruit(apple). fruit(pear). fruit(banana). \
\
% fruits_in/2 \
fruits_in(Xs, X) :- member(X, Xs), fruit(X). \
", {
success: function () {
/* Program parsed correctly */
},
error: function (err) {
/* Error parsing program */
},
});
Now, after parsing that program, session
contains three facts defining the fruit/1
predicate and a rule defining the fruits_in/2
predicate. member/2
is a predicate from lists
module, so we need to import it using the use_module
directive. This way, the predicates implemented on the lists
module will be available in this session.
Errors are generated as Prolog terms (see Prototypes and Prolog objects from Tau Prolog manual), with information about where has the error been raised (line and column), the found token (if any) and the next expected character.
We can query the database to check if a goal is true or not. In order to do so, we must first add the said goal to the stack of choice points and, later, check for answers. The pl.type.Session
prototype has a query
method, which receives a goal as a string and, after adding the goal to the stack, executes a callback.
session.query("fruits_in([carrot, apple, banana, broccoli], X).", {
success: function (goal) {
/* Goal parsed correctly */
},
error: function (err) {
/* Error parsing goal */
},
});
Once the goal has been added to the stack, the answer
method in pl.type.Session
allows us to look for answers (facts or rules) which make the goal true. Tau Prolog is asynchronous, which means that answer
will not return the results, but call a callback function if it finds something. This feature gives the Prolog predicates the ability to make asynchronous operations, as sleeping the execution or to make Ajax queries. In this case, the callback is given to answer
as an argument.
session.answer({
success: function (answer) {
console.log(answer); // {X/apple}
session.answer({
success: function (answer) {
console.log(answer); // {X/banana}
},
// error, fail, limit
});
},
error: function (err) {
/* Uncaught error */
},
fail: function () {
/* No more answers */
},
limit: function () {
/* Limit exceeded */
},
});
If an answer is found, this will be returned inside a pl.type.Substitution
object, where every variable of the goal is linked to a value. The pl.type.Substitution
prototype includes a toString
method which returns the substitution as a string in the format {X/a, Y/b, Z/c, ...}
(see Prototypes and Prolog objects from Tau Prolog manual). Another way to express a substitution as a string is the format_answer
method in Session
, which receives a substitution object and returns a string in the format X = a, Y = b, Z = c, ...
, or true
if the Substitution
object has no variables.
var show = function (answer) {
console.log(session.format_answer(answer));
};
session.answer({
success: function (answer) {
show(answer); // X = apple ;
session.answer({
success: function (answer) {
show(answer); // X = banana ;
},
// error, fail, limit
});
},
// error, fail, limit
});
Note that you can pass a generic callback instead of an object with all these methods. If the interpreter doesn't find an answer, it will call the callback with either false
or null
: false
if there wasn't an answer in the whole database, null
if the interpreter hasn't found an answer within the resolution steps limit. If the returned value is null
and you try to find an answer again, the interpreter will keep looking for answers from the point where it reached the limit last time.
This is a general scheme of how to use Tau Prolog:
// Consult
session.consult(program, {
success: function () {
// Query
session.query(goal, {
success: function (goal) {
// Answers
session.answer({
success: function (answer) {
/* Answer */
},
error: function (err) {
/* Uncaught error */
},
fail: function () {
/* Fail */
},
limit: function () {
/* Limit exceeded */
},
});
},
error: function (err) {
/* Error parsing goal */
},
});
},
error: function (err) {
/* Error parsing program */
},
});