@jan, @swi sorry for a slow reaction to SWI-Prolog releases. The NPM package https://www.npmjs.com/package/swipl-wasm is still very unstable but it bundles all necessary files including swipl.js, swipl.data and swipl.wasm. They are in dist/swipl/*. The package README contains how to use these files.
I just built and published new version based on 8.5.17. Right now building has to be done manually but I hope we find a solution for that.
Regarding 8.5.17, it does not run on node anymore. The issue is in src/wasm/prolog.js which contains:
You are doing well Thanks! Pushed a fix for the HTMLCollection. I still have trouble understanding the browser vs node issue. As I understand it, we have
swipl-web.js + swipl-web.wasm + swipl-web.data. These work in the browser. Notably swipl-web.data provides the content for the Emscripten file system that appears as /swipl in the browser.
swipl.js and swipl.wasm. These work with node. I think swipl-web.wasm and swipl.wasm are pretty much the same, but Iâm not sure. However, there is no swipl.data and instead the files are on the host physical file system.
If this picture is correct, the node version should be shipped with a directory structure that provides the Prolog home. The build system creates swipl.js and swipl.wasm as these components are needed to run the Prolog steps for the build using node. In this setup the Prolog resources are in the home directory of the build tree.
Given the (large) Prolog resources that need to be distributed completely differently, it seems the browser and node versions are pretty much disjoint and the best we can do is to distribute them as different npm packages. Right?
P.s. node src/swipl.js works fine despite the HTMLCollection issue. Why is that?
Handling reexport/1,2 was the problem. The code for dealing with .qlf files created from multiple Prolog files is a bit old ⌠Fixed with 7e685450f6117d2d20c2e45d123f635faaa13484.
I will take a look. It used to be that .wasm and .data files were common for both nodejs and browser variants but seems like they are not anymore. It used to be:
for the browser, start the node http-server in the root of the cloned repo using http-server ., open your browser at the indicated location and navigate to examples/browser.html. That should print a to the browser window. Why not âhello worldâ?
For node, simply run node examples/run-on-node.js, which again prints a.
I was confused about the development environment, where after the build you can run node src/swipl.js to get a normal interactive SWI-Prolog instance that âseesâ the physical file system. Looks like this does work in my local built, so something may be wrong with the way it is build in docker. Iâll sync both.
I donât get it. In my local build as well as when looking into @rlaâs build container I can run node swipl.js and when all is in the right place, it runs the normal Prolog toplevel. Without everything in the right place I end up with an error message, either not being able to load swipl.wasm or from Prolog not being able to find its startup resources.
When I go to the dist directory though, node swipl.js exits immediately without any message. Using strace to trace its system call suggests it doesnât even try to find the .wasm file Maybe someone more familiar with the node and JavaScript eco system can solve this?
I debugged these issues a bit. I donât seem to be able to run toplevel and I think thatâs expected behavior since PL_initialise does not run toplevel. https://www.swi-prolog.org/pldoc/doc_for?object=c(âPL_initialiseâ) PL_initialise will print the banner and then exit as nothing keeps the nodejs process around.
The MODULARIZE option (which we also use) for Emscripten turns generated SWI-Prolog module code into a factory function that needs to be imported and then executed. This will locate (at least it will try) .wasm and .data files. Thatâs why the example in the package also uses:
const SWIPL = require("./swipl/swipl"); // imports the module
// ...
const swipl = await SWIPL({ options }); // runs the factory function and places the instance into swipl variable
With Qt 6.4 we are taking the support for WebAssembly out of technology preview. With Qt for WebAssembly, Qt developers can use their existing skills, and often existing code, to target the web. Applications targeting the web assembly platform can run in most modern web browsers, and are easily distributed like any other web content. Thanks to near-native performance, and the rich UI and 3D features of Qt Quick and Qt Quick 3D, solutions requiring heavy data processing and demanding visualization can now easily be built for the web.
Replace the <textarea> with CodeMirror and use the Prolog mode that is part of SWISH. Doesnât use SWISHâs enhanced Prolog mode using Prolog support (which should be a lot simpler using the WASM version than server interaction).
Allow yield while loading local files. This does keep the browser responsive while loading large files and it allows loading from URLs from a local file. So, we can copy paste this: and use it:
:- use_module('https://raw.githubusercontent.com/SWI-Prolog/sCASP/master/prolog/scasp.pl').
p :- not q.
q :- not p.
Provide server-side example programs. These are added to the file dropdown and when selected copied to your local store, so you can load and edit them. Currently there are only two, one loading s(CASP) from Github and one manipulating the shell itself through DOM manipulations.
If you have nice examples, please share them.
Changed the console to use a more structured DOM. The console now has a sequence of div elements that each reflect a query and its answers. This allows collapsing and removing queries and in theory a lot more. As experiment it now collapses all old queries. That gives much more overview of the history and allows selectively opening them. Please share whether you like it.
support the edit/1 interface.
A mayor problem when you do fancy stuff is that it is easy to get Prolog involved in multiple asynchronous queries. That is fine, as long as they are strictly nested, i.e., we can start a new asynchronous query while yielded from an older one. We must however complete the new one before we can continue the old one. That cannot be solved without engines. All we can hope for on the short term is to detect this (Prolog now crashes badly).
Hello, I wrote a little online prolog editor along the lines of SWISH but using this WASM build. I mainly needed it to draw the trace as a graphical tree, but it might be useful to someone.
P.S. I implemented âasynchronousâ queries by running them in separate workers. Live Version
You are correct, I did mean to write parallel queries. Some times the two terms are used interchangeably, but it is better to be precise. More in detail, Javascript is run as a single thread in a page (for the most part), so blocking queries such as sleep would freeze the interface entirely, which was not great. As far as I know workers are the most straightforward way to perform parallel tasks in JS, so I created a pool of workers which then execute a single query each before reinstantiating. They are entirely separated from each other and the main thread (besides messages of course).
The WASM implementation offers asynchronous queries (using forEach), however very long to execute steps (Such as sleep, or Example 4 in the editor) will slow down or freeze the page if they are in the main thread. Again, using workers they do no longer affect the editor itself.
Testing the library on Node, calling JS from Prolog does not seem to work (:=/2 or js_script/2). The error is âwindow object undefinedâ, so itâs still expecting to be on a browser.
I tried to create a dummy global window object and assign names to it, but it didnât work either.
Then I found that in the repository examplejs_run_script/1 is used rather than js_script/2 and this seems to work, provided one assigns functions and variables to the global scope first.
Functions/variables must be assigned globally when using CommonJS modules or to a global object when using ES modules (I found this through experimenting).
Example with CommonJS modules:
const SWIPL = require("swipl-wasm");
const data = [];
// Just add some data
add = function () {
data.push(1);
};
(async function () {
const swipl = await SWIPL({ arguments: ["-q"] });
swipl.prolog.call(`js_run_script("add()")`); // js_script/1 throws, js_script/2 requires 'window'
console.log(data); // Correctly outputs [1]
})();
Example with ES modules:
import SWIPL from "swipl-wasm";
const data = [];
// Just add some data
global.add = function () {
data.push(1);
};
const swipl = await SWIPL({ arguments: ["-q"] });
swipl.prolog.call(`js_run_script("add()")`); // js_script/1 throws, js_script/2 requires 'window'
console.log(data); // Correctly outputs [1]
So, I donât know whether js_run_script/1 (which is undocumented) is the right predicate for calling JS from Prolog rather than js_script/2. Also, the example provided in the documentation seems to have the wrong arity (js_script/1 is used).
Thank you Jesse @jeswr . I did try your bundle. Very useful. It went well cloning, npm install, npm run build. Then I started testing the examples. This runs fine npm run test:serve-http. But the nodeâs example does not:
$node --unhandled-rejections=strict examples/run-on-node.js
---/npm-swipl-wasm/examples/run-on-node.js:5
console.log(swipl.prolog.query("member(X, [a, b, c]).").once().X);
^
TypeError: Cannot read property 'query' of undefined
at ---/npm-swipl-wasm/examples/run-on-node.js:5:28
The code in that js file is not the same that in the wiki:
wiki:
const SWIPL = require("swipl-wasm/dist/swipl");
in run-on-node.js
const SWIPL = require("../dist/swipl-node");
Can you confirm which is the correct module to require?
Note that if you clone the repo (which you should not do unless you are contributing to the project) then you can just use const SWIPL = require("../dist") for node and browser as we export an isomorphic bundle there.
If you are importing swipl-wasm in your own project like discussed in the getting started guide above; you get the same files by doing const SWIPL = require("swipl-wasm") or import SWIPL from "swipl-wasm"
Thank you for the getting started guide @jeswr - I carefully followed the instructions and got the same error. But then I noticed I was using an old version of node. I updated to v20.1.0 and it worked as expected.
But, how can I test the second example if I don´t have the scripts settings npm run test:serve-http nor the examples dir in there? (because I am not cloning the repo).
Btw, what I want is to bundle in my own prolog code, which is not a single file but a whole pack with prolog modules. Is that possible?