-
Notifications
You must be signed in to change notification settings - Fork 6
Add web demo #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add web demo #21
Conversation
Fixes hydromatic#13 To make it easy to call Morel from WASM, we need a simple interface to the shell. We already have `process_line`, which takes a string and returns a string. It is stateful, so variables assigned in one statement are available in the next statement: ```sml let mut shell = Shell::new(&[]); let mut result; let in_1 = "val x = 5\n\ and y = 6\n"; let out_1 = "> val x = 5 : int\n\ > val y = 6 : int\n"; result = shell.process_statement(in_1, None).unwrap(); assert_eq!(result, out_1); let in_2 = "x + y\n"; let out_2 = "> val it = 11 : int\n"; result = shell.process_statement(in_2, None).unwrap(); assert_eq!(result, out_2); ``` The current API is string-in, string-out. The input must be a single declaration or expression (without a semicolon). The output prints a value and its type. Future enhancements could be multi-statement input (separated by semicolons) and output as a structured value.
|
Looks great! I got the server running. In https://github.com/julianhyde/morel-rust/tree/0013-browser I added another couple of commits (enabling linting, and adding a favicon). Can you please review? If you're Ok I'll squash and merge to main. I had a couple of problems, as you can see in this screenshot. I plan to fix these in follow up commits.
I also plan to add banner and version properties, that you can access like this: Any ideas what I should replace 'wasm x.y' with, and how the wasm runtime can get that information? |
|
I logged hydromatic/morel#319 for the properties. |
|
You can add your commits directly to this PR if you push them to the main branch on my personal fork. I believe you can do that as long as this PR is open because I checked the "Allow edits by maintainers" box when I opened it. Then I could add my comments to your commits using the regular GH review tools. That said, your commits seems pretty focused and entirely unobjectionable :). I'm still too much of a Morel novice to spot the issues in that screenshot. Is it specific to the Wasm build? Regarding the runtime, that's up to the user (V8 for Chrome, SpiderMonkey for FF, etc.) but there are a bunch of stats you can use to describe the module itself. TL;DR perhaps calling it It's a core Wasm module (as opposed to a component module). That means it deals exclusively in numeric primitives (just 8, 16, 32, or 64-bit integers or 32 or 64-bit floats) and linear memory (just a big byte array you can index into), so things like strings have to be modeled in a Rust-native way (pointer+length). When JS passes a string to a Rust function, it first encodes the UTF-16 JS string as a UTF-8 Rust string, then allocates space in the module's linear memory using a special function exported by the module, then writes the UTF-8 bytes into that space, then passes the pointer+length to the Rust function its invoking. It's all very low-level and language-specific, which is why the JS glue is necessary. The You can verify that it's a core Wasm module with $ wasm-tools metadata show target/wasm32-unknown-unknown/release/morel.wasm
╭────────┬────────────┬──────┬───────┬───────────┬────────╮
│ KIND ┆ NAME ┆ SIZE ┆ SIZE% ┆ LANGUAGES ┆ PARENT │
╞════════╪════════════╪══════╪═══════╪═══════════╪════════╡
│ module ┆ morel.wasm ┆ 2.7M ┆ 100% ┆ Rust ┆ <root> │
╰────────┴────────────┴──────┴───────┴───────────┴────────╯
╭──────────────┬───────────────────────────────────────╮
│ KIND ┆ VALUE │
╞══════════════╪═══════════════════════════════════════╡
│ name ┆ morel.wasm │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ kind ┆ module │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ range ┆ 0x0..0x298397 │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ language ┆ Rust │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ processed-by ┆ rustc [1.91.0 (f8297e351 2025-10-28)] │
╰──────────────┴───────────────────────────────────────╯
If there were a component in that file, it would have shown up with kind This language-specificity is the main motivation for the Component Model, which aims to formalize the system of glue code into standardized "lifting" and "lowering" logic exported by the modules to convert native representations to canonical representations and back, allowing them to share things like strings, lists, structs, etc. across language boundaries in a much more natural way. It's an extra layer of rich typing, on top of the core modules that implement all the actual logic but deal exclusively in primitives and have language-specific representations for everything. Pretty much all the metadata from the module is stored in custom sections. Use $ wasm-tools objdump target/wasm32-unknown-unknown/release/morel.wasm
types | 0xb - 0x17d | 370 bytes | 40 count
imports | 0x180 - 0x385 | 517 bytes | 9 count
functions | 0x388 - 0xc32 | 2218 bytes | 2216 count
tables | 0xc34 - 0xc3b | 7 bytes | 1 count
memories | 0xc3d - 0xc40 | 3 bytes | 1 count
globals | 0xc42 - 0xc5b | 25 bytes | 3 count
exports | 0xc5e - 0x1bcc | 3950 bytes | 72 count
elements | 0x1bcf - 0x2000 | 1073 bytes | 1 count
code | 0x2005 - 0x23bef9 | 2334452 bytes | 2216 count
data | 0x23befd - 0x2616e8 | 153579 bytes | 2 count
custom "__wasm_bindgen_unstable" | 0x261703 - 0x263284 | 7041 bytes | 1 count
custom "name" | 0x26328d - 0x2982b1 | 217124 bytes | 1 count
custom "producers" | 0x2982bd - 0x298300 | 67 bytes | 1 count
custom "target_features" | 0x298313 - 0x298397 | 132 bytes | 1 countSome of these custom sections are standardized, like the Finally, there are 3 major versions of the Wasm spec. You can validate it against each version: $ wasm-tools validate target/wasm32-unknown-unknown/release/morel.wasm --features=wasm1
$ wasm-tools validate target/wasm32-unknown-unknown/release/morel.wasm --features=wasm2
$ wasm-tools validate target/wasm32-unknown-unknown/release/morel.wasm --features=wasm3I found that it's valid against 2.0 and 3.0, but not 1.0. Also, it's built using the If you wanted to build this library as a component instead of a core module, you could use |
Ah, sorry, I posted the wrong screenshot. The errors were because I gave a statement with a syntax error: and then (understandably) the environment seems to be hosed for the next statement: I'll log a bug for error-handling. By the way, I like your suggestions about Thank you for your exhaustive explanations of WASM and its toolchain. I know I will come back to your comments when I hit problems in future.
Nice idea. Curiously, I've never done this. My personal style is to accept the PR, rework it a bit if necessary (as a copy-editor would rework a newspaper article), and then move on. If it's good enough for main branch, put it in main branch, and there can be follow-on PRs. I'll merge this work shortly. Seeing Morel queries and their output appear in a web browser has got me excited. For example, I could finally work through an example program that compute K-means clusters (see hydromatic/morel#294) and display the clusters -- say the latitude/longitude of US state capitals -- as 2-d plots. We finally escape the bounds of textual output. |
|
FYI this guy does a way better job of doing an intro to wasm-tools than I can: https://www.youtube.com/watch?v=9O5NNOUuHPI |

There was an issue with the profiling logic in the unifier in the web environment. I guess Wasm core modules don't get clock access, which was causing a panic. I added a feature flag to gate the feature, where previously you were using
if false. Hopefully all my edits there make sense.The demo works now, at least for basic statements like
val x = 10. Seeing if I can run all the unit tests withwasm-bindgen-test.For the record, I used
console_error_panic_hookto debug the panic, and it worked like a charm. See this example. I was surprised to see a lot of therustwasmstuff was recently sunset, but I suppose it was always a stopgap measure until proper Component Model support arrives for the browser. Then, you wouldn't needwasm-bindgenorwasm-packanymore.Some things that took me a weirdly long time to learn about Rust:
eprintln!in addition toprintln!. The former goes to stderr instead of stdout. I feel like that's probably what you want to use in most of the places where you useprintln!.thiserrororsnafuinstead of custom enums to define and handle your errors. See also this discussion (unless you have a life, I guess)Fixes #13