Hello @mthom! Would it be possible to compile and run scryer as a WebAssembly binary? I worked some time ago on SWI-Prolog WebAssembly port but it turned out to be rather difficult due to:
Also, it was quite inconvenient to provide API for JavaScript. It takes hundreds of lines of handwritten code to bind JS functions to low-level FLI, a SWI-Prolog's FFI.
More info: https://github.com/SWI-Prolog/roadmap/issues/43
It seems like Rust has some support for WebAssembly (https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_wasm).
What do you think:
I have compiled scryer without signal (nix package) under windows OS
Would it be possible to build scryer as a WebAssembly binary?
I did that one or two months ago, based on c342d18f926a5e5778204e35f80e44844482ae45. It seems possible, but it certainly isn't trivial.
rug dependency doesn't work with WebAssembly. I exchanged a few emails with @tspiteri, and it seemed challenging to cross-compile GMP. Workaround: Remove the rug dependency and change the default features to ["num"], which uses num-rug-adapter.I couldn't get the crossterm crate to work, which is not really a surprise given the lack of a terminal in WebAssembly, but there might be a solution that I missed. Workaround: Remove crossterm and adapt get_single_char in src/prolog/machine/system_calls.rs to use an imported function raw_mode_single_char instead (which needs to be implemented in JavaScript):
extern "C" {
pub fn raw_mode_single_char() -> u32;
}
pub fn get_single_char() -> char {
unsafe {
return char::from_u32(raw_mode_single_char()).unwrap();
}
}
ProcessTime won't work in WebAssembly. Workaround: Replace or remove ProcessTime::now() in src/prolog/machine/system_calls.rs.src/main.rs. (Probably not necessary if you are building the project as a library instead of an executable binary.)I don't remember the exact command I used to compile it, but I used rustup's nightly toolchain for the wasm32-wasi target.
I implemented necessary syscalls to an extent that allowed to load a prolog source file from a virtual in-browser file system, and to see output written to the stdout file descriptor. Apart from that, I left most of the wasi syscalls unimplemented, but someone with more time could probably get most of them to work.
I got this tiny "Hello world" to work eventually:
:- initialization(hello_world).
hello_world :-
write('Hello, World!'), nl.
Partial screenshot of the web page (with some debugging info):

I didn't try much more after that, this had already taken me many hours and I mostly did it to gain some experience with Rust → WebAssembly.
@tniessen, thank you! That's awesome. Despite these issues, it seems like porting scryer would be easier.
@tniessen, do you happen to still have your changes somewhere? It would be a nice starting point.
Unless using threads, terminal input cannot work anyway since the blocking read call would also block the browser event loop including input events to read user input. SWI-Prolog wasm version has exactly the same issue.
@rla It might be possible to avoid this in some browsers with only minimal modifications in scryer-prolog. I wrote synchronous-channel a while back to allow WebAssembly in worker threads to retrieve information (such as terminal input) from the main thread. I did not test the library in browsers, but it should work. So the idea would be for the main thread to write terminal input to a SynchronousChannel, and a worker thread running the WebAssembly code could read it from the SynchronousChannel. The library supports both blocking and non-blocking read and write operations.
do you happen to still have your changes somewhere? It would be a nice starting point.
I'll try to find and commit them, but it might take a few days.
Very interested in a wasm module with TCP/UDP bindings. I'm trying to build the SWI-Prolog WASM just to see where I can find those calls... any idea if I'll be able to sniff those calls out with a nodeJS WASI wrapper?
@tniessen when you're ready to dedicate some time to get a WASM going, I'm right here with @rla . I'd like to get an environment up... 'one to bind them all' with prolog - scryer's the most lean implementation I've seen. Please let me know if you're willing to work together :)
Sorry @jacobfriedman and @rla, I am super busy with university, OSS, and everything else right now, but it's still on my list. I'd love to collaborate on this, and I'll try to find time soon-ish, I hope.
Most helpful comment
I did that one or two months ago, based on c342d18f926a5e5778204e35f80e44844482ae45. It seems possible, but it certainly isn't trivial.
Compilation problems
rugdependency doesn't work with WebAssembly. I exchanged a few emails with @tspiteri, and it seemed challenging to cross-compile GMP. Workaround: Remove therugdependency and change the default features to["num"], which usesnum-rug-adapter.I couldn't get the
crosstermcrate to work, which is not really a surprise given the lack of a terminal in WebAssembly, but there might be a solution that I missed. Workaround: Removecrosstermand adaptget_single_charinsrc/prolog/machine/system_calls.rsto use an imported functionraw_mode_single_charinstead (which needs to be implemented in JavaScript):ProcessTimewon't work in WebAssembly. Workaround: Replace or removeProcessTime::now()insrc/prolog/machine/system_calls.rs.src/main.rs. (Probably not necessary if you are building the project as a library instead of an executable binary.)Cross-compiling
I don't remember the exact command I used to compile it, but I used rustup's nightly toolchain for the
wasm32-wasitarget.Syscalls in the browser
I implemented necessary syscalls to an extent that allowed to load a prolog source file from a virtual in-browser file system, and to see output written to the
stdoutfile descriptor. Apart from that, I left most of the wasi syscalls unimplemented, but someone with more time could probably get most of them to work.Result
I got this tiny "Hello world" to work eventually:
Partial screenshot of the web page (with some debugging info):
I didn't try much more after that, this had already taken me many hours and I mostly did it to gain some experience with Rust → WebAssembly.