Wiki Discussion: SWI-Prolog in the browser using WASM

It is indeed inspired by @nicos work on R/real. I don’t know whether this approach has a name. This can’t deal with functions and other complex syntax. We notably need a way to add event listeners.

There is no need to register anything. Just define the function and call _ := add_par("Hello world!") Of course you can define a wrapper predicate or we can add something that creates these wrappers.

(Nothing of Importance here)

Aside from the above, in EYE we have to do

:- catch(use_module(library(sha)), _, true).
:- catch(use_module(library(pcre)), _, true).
:- catch(use_module(library(http/http_open)), _, true).
:- catch(use_module(library(semweb/rdf_turtle)), _, true).

because those libraries are not yet available in WASM.
This is not a burning issue, I just wondered if there is a plan to support those libraries in WASM?

Yes. What is wrong with that? You can do whatever JavaScript can do except for stuff that requires more complex syntax such as functions. In other words you can access methods, setters and getters and chain these using a.b.c… In fact, internally they are chained as a[b][c]… because . is a function in SWI-Prolog. goal_expansion on a.b.c… translates this to a[b][c]… to avoid the function evaluation. Next step :=/2 translates this into a Prolog term that expresses the actions that need to be done as a list of primitive actions. This is passed through ‘$js_call’/2 which is defined using EM_ASM_INT(), passing the raw Prolog terms to JavaScript. JavaScript calls Prolog.toJSON() (should that be Prolog.toObject()? as it is not really JSON). JavaScript executes the (now) array of actions resulting in a return value. This is handed to Prolog.toProlog() and made available as the result. If the arguments are sufficiently instantiated we could do most of the translation at compile time and simply pass the shared JavaScript object.

Most of them depend on foreign components. I have not yet looked into how easy or hard it is to support these. It seems emscripten has a notion of shared libraries, so it should be possible. Opening a URL as a stream as done by library(http/http_open) may be a real problem. A quick search suggests that emscripten has sockets, but they are a wrapper around websockets, not raw TCP/IP sockets. The obvious solution is to download in JavaScript, similar to Prolog.consult().then(), which downloads a file asynchronously using fetch(), compiles the file and runs the then(). This would not allow streaming parsing of e.g. Turtle input.

1 Like

SWISH is just a server side sandbox. And yes, doing eval() on strings is dangerous if you do not control where the string comes from. Otherwise I don’t see the difference with <script> elements.

I pushed a starting point to deal with this. After reading all the warning on emscripten against using dynamic linking I decided for another route I had in mind to resolve this issue: allow adding the foreign extensions to the core system. This is now a new option -DSTATIC_EXTENSIONS to cmake which is enabled by default for Emscripten. This allows building fully static SWI-Prolog executables with extensions, which surely has value on its own :slight_smile:

Currently includes some of the clib libraries, the sgml library and some of the http libraries.

An overall worry is the size of the whole thing :frowning:

Pushed to #wasm_demo

3 Likes

now works fine, thanks @jan

After seeing

commit 7d8f1e2eba12b0d245ac92ca7a5547f1c6f0589a
Author: Jan Wielemaker <J.Wielemaker@vu.nl>
Date:   Sat Aug 20 10:09:42 2022 +0200

    Included turtle and ntriple libraries in WASM build.

we tested the latest version but get

ERROR:    source_sink `library(semweb/rdf_turtle)' does not exist

I didn’t include this deprecated library. It just loads `library(semweb/turtle)'. Note that the writing part of this library does not yet work as it depends on semweb/rdf_db and the C part of the RDF store heavily depends on threading.

1 Like

Aha, that is nice and I will use library(semweb/turtle) from now on. Thanks!

Can I also use, like in JavaScript?

Par["textContent"] := Text

It seems to me from the term_expansion/2 I find in wasm.pl,
and the operator definition op(50, yf, []).

But I am not 100% sure, whether term_expansion/2 has another
purpose, like avoiding the dot expansion for dicts?

Par[textContent] := Text is the output of the expansion. That should work.

@jan

The instructions note

wget https://gmplib.org/download/gmp/gmp-6.1.2.tar.lz
tar xf gmp-6.1.2.tar.lz
cd gmp-6.1.2

In checking the GMP latest version it is 6.2.1, was that a typo?

I copied that from somewhere … Guess I should have checked (and tested) the latest version.

In the meanwhile #wasm_demo is updated to include GMP, (6.1.2) providing unbounded integers and rational numbers. Note that the GMP library is untested. It might be wise not to trust the results blindly.

GMP 6.2.1 compiles fine too. make check does not work, so it is hard to access how bug free the library is.

3 Likes

I have added swipl-wasm package to NPM after being requested countless times. https://www.npmjs.com/package/swipl-wasm

I had bigger plans at first to add Typescript support, lots of tests and examples but currently this package contains the minimal number of things.

You get the original build output files in dist/swipl directory. The files are built using Docker which should make it much easier for people using Windows. The package already contains built files so you don’t need any build tools at all unless you want to experiment with different build options.

Check https://swi-prolog.discourse.group/t/swi-prolog-in-the-browser-using-wasm/5650 for any advanced usage. Besides 2 runnable examples there is no documentation in the package at all.

The package should probably be versioned in lockstep with main SWI-Prolog releases. @jan can publish new versions of the package. The package files (scripts etc.) should maybe be part of the SWI-Prolog repo.

Edit: package own source code is currently here: https://github.com/rla/npm-swipl-wasm

5 Likes

Thanks, @rla. For any user, please consider the WASM version as a moving target. The interface will be extended and it is not unlikely it will see incompatible changes. There is also a lot of low-level stuff in the current interface that is likely to be removed.

Anyone willing to help to arrive at a clean, functional an documented interface, please make yourself known!

The Prolog end of the WASM port reached a new milestone: most meaningful extensions, including foreign extensions now compile and the corresponding test suites pass. Most of the problems with the test suite was about disabling parts that are require functionality that is not supported (e.g., threads). A remaining issue is that IEEE 754 float rounding seems unimplemented and therefore these tests have been disabled for the Emscripten version.

#wasm_demo has been updated.

4 Likes

@jan is it realistic to expect library(pcre) in WASM?

If the the library builds on Emscripten it should simply mean adding it to the package selection. You might give it a try?

It does touch a general problem though. The libpcre-2.8.so is about as big as SWI-Prolog itself … As the Prolog wrapper supports most functionality of the library we probably get most of the code added to our image :frowning: The size increment from GMP was not as big as I feared because we use only a fairly small part of libgmp.

The current WASM version is getting really big. Part of this are some large Prolog libraries in the swipl-web.data image. Notably the generated library(chr) is about 1.5Mb. In theory we could only ship the core library in swipl-web.data and (on demand?) load the rest from the internet.

Modularizing the WASM core is a different matter. This can work using dynamic linking. The Emscripten says dynamic linking is possible, but comes at a considerable size increment because it includes the C runtime library into the core WASM file and must consider all interfaces of libswipl reachable. My interpretation of their description is “don’t, unless unavoidable”. That is why I explored the route to link extensions statically.

I guess the best option is to provide a complete (as far as possible) binary through npm and simplify making specific package selections. As is, this is a bit hard and the two most sensible solutions are to build everything or just the core.