2 Commits

Author SHA1 Message Date
Oleksandr Kozachuk 31dc6c6397 fix(core): no SystemTime on wasm32 — fixed boot seed + UTIME Forth error
CI / check (push) Waiting to run
2026-07-29 16:56:31 +02:00
Oleksandr Kozachuk 35b78193fd feat(boot): add -ROT <= >= gforth extensions
CI / check (push) Has been cancelled
Non-standard but ubiquitous words; absence aborted otherwise-valid
gforth programs with unknown-word errors.
2026-07-18 15:40:39 +02:00
2 changed files with 64 additions and 14 deletions
+13
View File
@@ -72,6 +72,19 @@
1-
REPEAT ;
\ ---------------------------------------------------------------
\ Common extensions (not in Forth 2012, gforth-compatible)
\ ---------------------------------------------------------------
\ -ROT ( x1 x2 x3 -- x3 x1 x2 ) rotate top item to third place
: -ROT ROT ROT ;
\ <= ( n1 n2 -- flag ) true if n1 <= n2 (signed)
: <= > 0= ;
\ >= ( n1 n2 -- flag ) true if n1 >= n2 (signed)
: >= < 0= ;
\ ---------------------------------------------------------------
\ Phase 2: Double-cell arithmetic
\ ---------------------------------------------------------------
+37
View File
@@ -393,6 +393,11 @@ impl<R: Runtime> ForthVM<R> {
substitutions: Arc::new(Mutex::new(HashMap::new())),
search_order: Arc::new(Mutex::new(vec![1])),
next_wid: Arc::new(Mutex::new(2)),
// SystemTime::now() PANICS on wasm32-unknown-unknown (no time
// source), which turned VM construction into an `unreachable`
// trap in the browser. Seed from the wall clock only where one
// exists; wasm hosts start deterministic and reseed via RND-SEED.
#[cfg(not(target_arch = "wasm32"))]
rng_state: {
use std::time::{SystemTime, UNIX_EPOCH};
let seed = SystemTime::now()
@@ -404,6 +409,8 @@ impl<R: Runtime> ForthVM<R> {
seed
}))
},
#[cfg(target_arch = "wasm32")]
rng_state: Arc::new(Mutex::new(0xDEAD_BEEF_CAFE_BABE)),
compile_frames: Vec::new(),
compiling_word_addr: 0,
};
@@ -5383,6 +5390,15 @@ impl<R: Runtime> ForthVM<R> {
/// UTIME ( -- ud ) push microseconds since epoch as a double-cell value.
fn register_utime(&mut self) -> anyhow::Result<()> {
let func: HostFn = Box::new(move |ctx: &mut dyn HostAccess| {
// SystemTime::now() panics on wasm32-unknown-unknown; fail as a
// catchable Forth error instead of poisoning the VM with a trap.
#[cfg(target_arch = "wasm32")]
{
let _ = ctx;
Err(anyhow::anyhow!("UTIME: no time source on this platform"))
}
#[cfg(not(target_arch = "wasm32"))]
{
use std::time::{SystemTime, UNIX_EPOCH};
let us = SystemTime::now()
.duration_since(UNIX_EPOCH)
@@ -5397,6 +5413,7 @@ impl<R: Runtime> ForthVM<R> {
ctx.mem_write_slice(new_sp as u32 + 4, &lo.to_le_bytes());
ctx.set_dsp((new_sp as i32) as u32);
Ok(())
}
});
self.register_host_primitive("UTIME", false, func)?;
@@ -7462,6 +7479,12 @@ mod tests {
assert_eq!(eval_stack("1 2 3 ROT"), vec![1, 3, 2]);
}
#[test]
fn test_minus_rot() {
// ( 1 2 3 -- 3 1 2 ) top-first: [2, 1, 3]
assert_eq!(eval_stack("1 2 3 -ROT"), vec![2, 1, 3]);
}
// -- Comparison --
#[test]
@@ -7482,6 +7505,20 @@ mod tests {
assert_eq!(eval_stack("3 5 >"), vec![0]);
}
#[test]
fn test_less_or_equal() {
assert_eq!(eval_stack("3 5 <="), vec![-1]);
assert_eq!(eval_stack("5 5 <="), vec![-1]);
assert_eq!(eval_stack("5 3 <="), vec![0]);
}
#[test]
fn test_greater_or_equal() {
assert_eq!(eval_stack("5 3 >="), vec![-1]);
assert_eq!(eval_stack("5 5 >="), vec![-1]);
assert_eq!(eval_stack("3 5 >="), vec![0]);
}
// -- Logic --
#[test]