plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


0f2502c — Peter John 2026-10-25T16:08:43+05:30
feat(hosts): link plum::crash host import (prints to stderr, then traps)
plum-cli/src/main.rs CHANGED
@@ -233,6 +233,26 @@ fn hostImports(
233
233
  },
234
234
  )))
235
235
  }
236
+ ("plum", "crash") => {
237
+ let func_ty = imp.ty().func().cloned().ok_or_else(|| {
238
+ anyhow::anyhow!("plum::crash import must be a function")
239
+ })?;
240
+ Ok(wasmtime::Extern::Func(wasmtime::Func::new(
241
+ &mut *store,
242
+ func_ty,
243
+ |mut caller, params, _results| {
244
+ let wasmtime::Val::AnyRef(Some(s)) = &params[0] else {
245
+ return Err(wasmtime::Error::msg("crash expects a Str argument"));
246
+ };
247
+ let arr = unwrapStrToArray(&mut caller, s)?;
248
+ let len = arr.len(&caller)?;
249
+ let mut buf = vec![0u8; len as usize];
250
+ arr.copy_to_i8_slice(&mut caller, &mut buf)?;
251
+ eprintln!("crash: {}", String::from_utf8_lossy(&buf));
252
+ Ok(())
253
+ },
254
+ )))
255
+ }
236
256
  ("plum", "rawRandomInt") => {
237
257
  let func_ty = imp.ty().func().cloned().ok_or_else(|| {
238
258
  anyhow::anyhow!("plum::rawRandomInt import must be a function")
plum-cli/tests/crash_test.rs ADDED
@@ -0,0 +1,29 @@
1
+ #![allow(non_snake_case)]
2
+ use std::process::Command;
3
+
4
+ fn plumBin() -> std::path::PathBuf {
5
+ let mut path = std::env::current_exe().unwrap();
6
+ path.pop();
7
+ if path.ends_with("deps") {
8
+ path.pop();
9
+ }
10
+ path.push("plum");
11
+ path
12
+ }
13
+
14
+ #[test]
15
+ fn crash_prints_message() {
16
+ let dir = std::env::temp_dir().join(format!("plum_crash_{}", std::process::id()));
17
+ std::fs::create_dir_all(&dir).unwrap();
18
+ let src = dir.join("crash.plum");
19
+ std::fs::write(&src, "import std/Str\n\nfun main() =\n crash \"boom\"\n").unwrap();
20
+ let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
21
+ let out = Command::new(plumBin())
22
+ .current_dir(root)
23
+ .args(["run", src.to_str().unwrap()])
24
+ .output()
25
+ .expect("failed to run plum");
26
+ let stderr = String::from_utf8_lossy(&out.stderr);
27
+ assert!(!out.status.success(), "expected non-zero exit; stderr: {}", stderr);
28
+ assert!(stderr.contains("crash: boom"), "stderr was: {}", stderr);
29
+ }
plum-runtime/src/main.rs CHANGED
@@ -31,6 +31,20 @@ fn hostImports(store: &mut wasmtime::Store<()>, module: &wasmtime::Module) -> Ve
31
31
  Ok(())
32
32
  }))
33
33
  }
34
+ ("plum", "crash") => {
35
+ let func_ty = imp.ty().func().cloned().expect("plum::crash import should be a function");
36
+ wasmtime::Extern::Func(wasmtime::Func::new(&mut *store, func_ty, |mut caller, params, _results| {
37
+ let wasmtime::Val::AnyRef(Some(s)) = &params[0] else {
38
+ return Err(wasmtime::Error::msg("crash expects a Str argument"));
39
+ };
40
+ let arr = unwrapStrToArray(&mut caller, s)?;
41
+ let len = arr.len(&caller)?;
42
+ let mut buf = vec![0u8; len as usize];
43
+ arr.copy_to_i8_slice(&mut caller, &mut buf)?;
44
+ eprintln!("crash: {}", String::from_utf8_lossy(&buf));
45
+ Ok(())
46
+ }))
47
+ }
34
48
  ("plum", "rawRandomInt") => {
35
49
  let func_ty = imp.ty().func().cloned().expect("plum::rawRandomInt import should be a function");
36
50
  wasmtime::Extern::Func(wasmtime::Func::new(&mut *store, func_ty, |_caller, _params, results| {