The WASM SWI-Prolog distribution consists of three files:
swipl-web.js
<script>
element. It defines a global function SWIPL
that loads the
other components and gives access to the SWI-Prolog system.swipl-web.wasm
swipl-web.data
/swipl
. It contains the Prolog startup code and libraries.
Below is the skeleton for getting access to the Prolog system. We
first define the global Module object, optionally with
configuration parameters. We use a global variable because some of the
Emscripten configuration parameters require access to the
Module. SWIPL
returns a Promise
that resolves when the WASM system is loaded and initialized. This sets
Module.prolog
to an instance of the class Prolog
that provides a high level interface from JavaScript.
let Prolog; const Module = { // Provide options for customization }; SWIPL(Module).then((module) => { Prolog = Module.prolog; // Start using Prolog };
Module defines customization properties for the Emscripten module as well as for Prolog. We highlight the important properties below.
Array
of String
objects that define the
commandline arguments for initializing Prolog. argv[0]
is not
part of this array. Few arguments are useful in this context. The
-q may be used to suppress the Prolog banner.Function
that is used to translate swipl-web.wasm
and
swipl-web.data
into a (relative) URL. Default is to find
these resources in the same directory of the server. For example, to
load
swipl-web.wasm
and swipl-web.data
from /wasm
use
var Module = { ..., locateFile: (file) => '/wasm/' + file }
Function
that is called when Prolog writes to
user_output
or user_input
. It is passed two
arguments: a
String
containing the text to emit and one of the constant
strings "stdout"
or "stderr"
to indicate the
output stream. This uses the Emscripten Module.FS.init
option to rebind the output and error streams, providing behaviour that
is similar to the Emscripten properties print
and printErr
.
However, our passed string contains the newline character and the
handler is called when Prolog flushes the output. Normally the
callback should insert a <span>
element that has (at
least) the following style:
.stderr, .stdout { white-space: pre-wrap; font-family: monospace; overflow-wrap: anywhere; }
Here is a simple implementation of print(), assuming the document has
a <div id="output">
node.
function print(line, cls) { const output = document.getElementById("output"); const node = document.createElement('span'); node.className = cls; node.textContent = line; output.appendChild(node); };
The WASM build ships with the Prolog library and thus Prolog
libraries can be loaded as normal using use_module/1,
etc., for example, we can include the lists
library using
this directive. Note that the normal autoloading of library
code works in the WASM version.
:- use_module(library(lists)).
When Prolog is in asynchronous mode, i.e., called through
Prolog.forEach(),
we can also load code from a URL. For example, we can load the CHAT80
demo program directly from
GitHub
using238The
c
continues the quoted atom from the next line after removing leading
white space.
\
?- consult('https://raw.githubusercontent.com/JanWielemaker/\c chat80/master/prolog/chat80.pl').
Larger files can be loaded as .qlf
files. See section
4.3.3 and
qcompile/2.
Notably we can create a single qlf file from an application using the include(user)
option. Below we create a .qlf
file from CHAT80.
The resulting chat80.qlf
can be loaded from a URL using consult/1
as above.
?- qcompile('chat80.pl', [include(user)]).
There are three ways to load Prolog code from JavaScript: (1) loading
from a string, (2) loading from <script>
elements and
(3) loading from URL. Note that all the loading methods return a Promise
that is resolved when loading the data is completed.
/string/1
, /string/2
, ... .type
set to text/prolog
. The file reference for the loaded
script is /script/Id
, where Id is derived from
(1) the
id
of the script, (2) the name
of the script
or (3) being the nth Prolog script in the document. When resolved, the
promise returns an array with the names of the loaded scripts.library(lists)
or a URL. The
sources are downloaded and processed sequentially. This uses
Prolog.forEach()
calling load_files/1.
The returned
Promise
returns 1 on success.