Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31dc6c6397 | |||
| 35b78193fd |
@@ -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
|
||||
\ ---------------------------------------------------------------
|
||||
|
||||
@@ -1088,12 +1088,10 @@ fn emit_do_loop(f: &mut Function, body: &[IrOp], is_plus_loop: bool, ctx: &mut E
|
||||
.instruction(&Instruction::End);
|
||||
}
|
||||
|
||||
// Forth 2012: LOOP exits when the index crosses the boundary between
|
||||
// limit-1 and limit. With step +1 that is exactly new_index == limit
|
||||
// in wraparound arithmetic — start >= limit must wrap, not exit early.
|
||||
// if index >= limit, exit
|
||||
f.instruction(&Instruction::LocalGet(index_local))
|
||||
.instruction(&Instruction::LocalGet(limit_local))
|
||||
.instruction(&Instruction::I32Eq)
|
||||
.instruction(&Instruction::I32GeS)
|
||||
.instruction(&Instruction::BrIf(1)) // break to $exit
|
||||
.instruction(&Instruction::Br(0)) // continue loop
|
||||
.instruction(&Instruction::End) // end loop
|
||||
@@ -1899,8 +1897,7 @@ fn emit_promoted_op(f: &mut Function, op: &IrOp, sim: &mut StackSim) {
|
||||
// Fix up stack for next iteration (LOOP body is stack-neutral)
|
||||
emit_promoted_loop_fixup(f, sim, &loop_top_stack);
|
||||
|
||||
// LOOP: increment by 1, exit when new_index == limit
|
||||
// (Forth 2012 boundary crossing; start >= limit wraps around)
|
||||
// LOOP: increment by 1, check >= limit
|
||||
f.instruction(&Instruction::LocalGet(index_local));
|
||||
f.instruction(&Instruction::I32Const(1));
|
||||
f.instruction(&Instruction::I32Add);
|
||||
@@ -1908,7 +1905,7 @@ fn emit_promoted_op(f: &mut Function, op: &IrOp, sim: &mut StackSim) {
|
||||
|
||||
f.instruction(&Instruction::LocalGet(index_local));
|
||||
f.instruction(&Instruction::LocalGet(limit_local));
|
||||
f.instruction(&Instruction::I32Eq);
|
||||
f.instruction(&Instruction::I32GeS);
|
||||
f.instruction(&Instruction::BrIf(1)); // break to $exit
|
||||
}
|
||||
|
||||
@@ -2832,11 +2829,9 @@ fn emit_consolidated_do_loop(
|
||||
.instruction(&Instruction::End);
|
||||
}
|
||||
|
||||
// Forth 2012 boundary crossing: exit when new_index == limit
|
||||
// (start >= limit wraps around instead of exiting early).
|
||||
f.instruction(&Instruction::LocalGet(index_local))
|
||||
.instruction(&Instruction::LocalGet(limit_local))
|
||||
.instruction(&Instruction::I32Eq)
|
||||
.instruction(&Instruction::I32GeS)
|
||||
.instruction(&Instruction::BrIf(1))
|
||||
.instruction(&Instruction::Br(0))
|
||||
.instruction(&Instruction::End)
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user