As is, no. What you can do is download the state using JavaScript to the WASM file system and pass ["-x", "/mystate"] to the initialization arguments. I think that is good enough
That error comes from fetch(). You can view it like below. Guess the message infrastructure should do this translation for you
?- catch((P := fetch("https://www.swi-prolog.org/Download.html"), js_yield(P,R)), E, true), Msg := E.toString()
E = <js_TypeError>(8),
Msg = 'TypeError: NetworkError when attempting to fetch resource.'.
Checking out the browser console we see
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.swi-prolog.org/Download.html. (Reason: CORS header āAccess-Control-Allow-Originā missing). Status code: 200.
The function to download a file and consult it could give some inspiration (Iāll probably change this implementation though as we can avoid the file).
function consult_one(url)
{ let file = "/tmp/"+url.replace(/\//, "+");
return fetch(url)
.then((response) => response.text())
.then((text) =>
{ console.log(`Downloaded ${url} to ${file}`);
Module.FS.writeFile(file, text);
prolog.call(`consult('${file}')`);
})
}
This is a great example, as it shows practically how to make a small prolog app in an html page. Thanks for sharing it and for adding support for it in the code.
It would be great if we could embed a saved state within an html page (perhaps wiith a base64 encoded data url?) or load a saved state with a method like Prolog.load_saved_state(Url).
Data urls in firefox can be 32MB, so it is quite sufficient for a saved state.
Prolog.load_saved_state(Url) would allow us to develop the app in regular SWI-Prolog and then simply store the saved state in a URL accessible by the web server.
Storing the saved state in a data url would allow us to embed the prolog app and the dependencies within the html page.
This will tremendously simplify dependencies, etc. Of course there would be some limitation on what the code inside the saved state can do, but any browser application would have those limitations.
Iām afraid it aināt that simple States do depend on conditional compilation and the core of the system and these differ between the normal executable and the WASM version. If, as with this example, you want to manipulate the DOM there is no link to the normal desktop version. What could work is to allow loading QLF files from a URL. At the moment I am looking into loading .pl files from a URL from Prolog. If that would work fine you can server a directory with .pl files and dynamically load from there. That is probably doable, though it comes with some problems
This project is getting a little big It is about getting time to seek for (financial) support. If anyone knows about organizations willing to invest in this, please drop me a line.
Wow, thatās really nice, and fast! Thanks for using CBG chords as an example and tagging me, itās great to see. With an easy WASM prolog I might have to revisit some old client-side ideas!
I liked the example Can I ask a little favor? The file has no copyright. The various Linux maintainers will complain ā¦ Ideally you create a pull request adding a copyright to the file src/wasm/cbg.html. Anything works, as long as it is open source compliant (public domain, BSD-2, BSD-3, MIT, Apache, GPL, etc.) As SWI-Prolog is BSD-2, my preference goes to BSD-2 or public domain. If a PR is too complicated, just send me your decision. Thanks!
About the fetch(url).then(...).then(...) form, which uses Promises ā¦ itās probably better for new code to use the newer async syntax instead; it makes error handling easier and itās a bit easier to read.
function fetchFromServer(request, callback) {
// callback should take a single arg, the response from the server,
fetch(...)
.then(response => response.json())
.then(callback)
.catch(err => {
alert('***fetch ' + JSON.stringify(request) + ': ' + err) });
}
became:
async function fetchFromServer(path, request, callback) {
// callback should take a single arg, the response from the server.
try {
const response = await fetch(...);
callback(await response.json());
} catch(err) {
alert('***fetch ' + JSON.stringify(request) + ': ' + err);
}
}
and calls to fetchFromServer() were prepended by await.
I have already started doing so in some places. As I think of it, the js_yield(+Promise, -Result) is in fact await, no? It does (if I understand this correctly) the same: yield from the current (Prolog) execution and resume if the thing we are waiting for is resolved. I was already quite happy after replacing the original (string) request and reply with arbitrary objects to conclude that the only thing you need as input for js_yield/2 is a Promise If so, we should name it await/2
The main issue is that SWI-Prolog cannot yield from everywhere Notably it cannot yield from Prolog ā C ā Prolog calls. Possibly some cases can be fixed by hand (the general case requires a yield mechanism in C). The other nasty part is the debugger. It is probably hard to yield from the debugger as each of the ports comes with its own state that needs to be saved and restored.
Please let everybody who sees potential in this for his/her work look around for opportunities. One big investor is easiest, but several smaller ones work as well. I think I proved that the yield based design is viable. What needs to be done for a nice polished system is roughly this (might have missed something?)
Allow yield to work from many more places, including the debugger.
Write a proper debugger (we have all the stuff from the GUI traces that is also used by @oskardrums debug adapter, so it is proven to be reusable).
Provide asynchronous implementations for many more of the currently blocking primitives.
Get engines to the single threaded version. With engines we can have multiple threads of control. Engines do not need threads, but the current engine implementation sits on top of the thread implementation. These would need to be untangled.
Error handling, testing.
Documentation, demos, ā¦
(Modular) packaging and simple deployment of applications.
Got one more step: we can now load files from URLs. This is now available on #wasm_demo, where you can run
?- ['scasp/prolog/scasp'].
to get s(CASP) loaded. It also runs The sCASP source is made available from the server under /wasm/scasp/. The way it works is
If we try to load a file that doesnāt exist in the virtual file system, ensure it has a .pl extension and try to download it as a relative URL.
When loading from a URL, all relative paths are loaded relative to the URL of the file being loaded.
You can also load from an absolute URL, provided the server allows cross-origin access.
The loading performance is a little disappointing. I also noted that Prolog files from the virtual file system are loaded rather slow. On my machine it takes a little over 2 seconds to load s(CASP) compared to 0.29 using the native SWI-Prolog
Havenāt tested whether it also loads .qlf files, but I fear it will try to interpret them as UTF-8 files somewhere along the process ā¦
It was a bit of a challenge The WASM version can now load .qlf files from a URL. That is nice. Even nicer is that I managed to extend qcompile/2 to include indirectly loaded files except for files from the Prolog system. So, we can do
And end up with a single file scasp.qlf that you can load in any compatible SWI-Prolog instance. If you go to #wasm_demo you an run this and get going:
?- time(['scasp.qlf']).
% 1,671,738 inferences, 0.638 CPU in 0.638 seconds (100% CPU, 2620280 Lips)
true.
The current scasp source conditionally depends on a file that is not part of the WASM version. I created the .qlf file using node /path/to/swipl.js to get system with the same libraries.
Note that these work too. Both times are reported on second invocation, thus using the browser cache.
?- time(['scasp/prolog/scasp']).
% 2,278,937 inferences, 1.465 CPU in 1.465 seconds (100% CPU, 1555588 Lips)
true.
Or, loading from GitHub raw content:
?- time(['https://raw.githubusercontent.com/SWI-Prolog/sCASP/master/prolog/scasp.pl']).
% 2,278,481 inferences, 1.425 CPU in 1.425 seconds (100% CPU, 1598934 Lips)
true.
This is really great it starts to make deployment quite feasible.
I donāt have emscripten setup, is there a place where I could download the swipl.js file? I would like to try out generating some qlf files and then loading them in the browser.
As is, there is no swipl.js. If there is no conditional compilation in your code you can just use any recent 8.5.x version to create the .qlf. If there is conditional compilation, you can probably tweak the condition such that you can set some flags to get the result that you need for the browser version.
Streamlining the distribution of the WASM version is still something to consider. Maybe @rla has experience? (How) is the npm version updated? Could/should we have the stuff on a CDN?
It is, but only in the build environment. I see that the npm package does provide swipl.js, but that doesnāt help much as it doesnāt ship with the .data file that provides the Prolog part for the browser. swipl.js is built as a necessary step in the WASM build process to perform the Prolog parts of the build using node.
@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: