Wiki Discussion: SWI-Prolog in the browser using WASM

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.

Really? That is in sharp contrast with the 155 lines of prolog at https://github.com/josd/eye/blob/2664f8172a71199b66379f783733d4dbdde46573/eye.pl#L11317
which is of course not the same functionality nor the same performance.
I will look how I could get re_replace/4 and re_split/3 in prolog as well.

It is a little less extreme than I had in mind. On x86_64 Linux I get 600Kb for libpcre2-8.so, 1.7Mb for libswipl.so and 510Kb for libgmp.so.

If I recall correctly, good compilation of regular expressions in pure Prolog performs quite well. One of the main reasons for adding pcre is to write implement conforming applications where regular expressions are part of the standard. Implementing a simple regex compiler in Prolog is easy, but implementing the various regex ā€œstandardsā€ completely is a lot of work.

At a more higher level we probably should also consider rewriting some of the current C based extensions to use JavaScript instead. For example, regular expressions :slight_smile: Notably the networking related functionality should work that way. That would make the WASM version leaner. But, less compatible with the native version :frowning:

I think PCRE uses JIT on x64 and various other platforms but non-jit mode elsewhere (like it would on WebAssembly). JIT is a huge part of the codebase and without it the library size should be a lot smaller.

1 Like

You might also look at which ā€œwidthsā€ are supported by library(pcre) ā€“ it only needs ASCII and UTF-8. And the replacement code for re_replace/4 and re_replace/5 is done in Prolog because the older PCRE library didnā€™t have support for that (PCRE2, the current version, does support replacement; but perhaps that can be turned off?)

You can check how the library was built by:

forall(re_config(X), writeln(X)).

Iā€™ve pushed a number of changes to properly support asynchronous programming with the WASM instance. The key is that next to Prolog.query(), which returns an iterator for synchronous querying we now have a new method

Prolog.forEach(goal, [input], [on_answer])

Here, goal and input are handled as in Prolog.query. on_answer is a callback that is called on each answer with an object that represents the output bindings. For example

Prolog.forEach(member(X, List), {List: [1,2,3]}, 
               (r) => console.log(r.X));

The browser remains responsive while this query is processed. The forEach method returns a Promise. When resolved and there is an on_answer, it passes the number of answers. If there is no on_answer it passes the answers as an array. On a Prolog exception the Promise is rejected.

Except that I am not sure about the name (forEach), this seems a viable approach to deal with long running queries.

The second change is to the Prolog predicate js_yield/2. This used to exchange strings. Now it exchanges arbitrary data and it allows the object to yield on to be a Promise. This allows for fancy queries such as below.

?- FP := fetch("test.pl"), js_yield(FP, Response),
   TP := Response.text(), js_yield(TP, T).
FP = <js_Promise>(4),
Response = <js_Response>(5),
TP = <js_Promise>(6),
T = '% :- debug(js) ...'.

It also provides a clean extensible interface for adding other asynchronous operations. The Prolog sleep/1 replacement on WASM now uses this interface, removing this code from the core interface.

#wasm_demo is updated.

3 Likes

Is it possible to load a saved state using fetch(ā€¦) like above?