Feat: prelude and new words

This commit is contained in:
2026-02-05 00:58:53 +01:00
parent b75b9562af
commit 53fb3eb759
21 changed files with 533 additions and 29 deletions

View File

@@ -19,6 +19,12 @@ pub enum Op {
Drop2,
Swap2,
Over2,
Rev,
Shuffle,
Sort,
RSort,
Sum,
Prod,
Forget,
Add,
Sub,
@@ -91,6 +97,7 @@ pub enum Op {
SetSpeed,
At,
IntRange,
StepRange,
Generate,
GeomRange,
Times,

View File

@@ -345,6 +345,77 @@ impl Forth {
stack.push(a);
stack.push(b);
}
Op::Rev => {
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
if count > stack.len() {
return Err("stack underflow".into());
}
let start = stack.len() - count;
stack[start..].reverse();
}
Op::Shuffle => {
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
if count > stack.len() {
return Err("stack underflow".into());
}
let start = stack.len() - count;
let slice = &mut stack[start..];
let mut rng = self.rng.lock();
for i in (1..slice.len()).rev() {
let j = rng.gen_range(0..=i);
slice.swap(i, j);
}
}
Op::Sort => {
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
if count > stack.len() {
return Err("stack underflow".into());
}
let start = stack.len() - count;
stack[start..].sort_by(|a, b| {
a.as_float()
.unwrap_or(0.0)
.partial_cmp(&b.as_float().unwrap_or(0.0))
.unwrap_or(std::cmp::Ordering::Equal)
});
}
Op::RSort => {
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
if count > stack.len() {
return Err("stack underflow".into());
}
let start = stack.len() - count;
stack[start..].sort_by(|a, b| {
b.as_float()
.unwrap_or(0.0)
.partial_cmp(&a.as_float().unwrap_or(0.0))
.unwrap_or(std::cmp::Ordering::Equal)
});
}
Op::Sum => {
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
if count > stack.len() {
return Err("stack underflow".into());
}
let start = stack.len() - count;
let total: f64 = stack
.drain(start..)
.map(|v| v.as_float().unwrap_or(0.0))
.sum();
stack.push(float_to_value(total));
}
Op::Prod => {
let count = stack.pop().ok_or("stack underflow")?.as_int()? as usize;
if count > stack.len() {
return Err("stack underflow".into());
}
let start = stack.len() - count;
let product: f64 = stack
.drain(start..)
.map(|v| v.as_float().unwrap_or(1.0))
.product();
stack.push(float_to_value(product));
}
Op::Add => binary_op(stack, |a, b| a + b)?,
Op::Sub => binary_op(stack, |a, b| a - b)?,
@@ -889,6 +960,25 @@ impl Forth {
}
}
Op::StepRange => {
let step = stack.pop().ok_or("stack underflow")?.as_float()?;
let end = stack.pop().ok_or("stack underflow")?.as_float()?;
let start = stack.pop().ok_or("stack underflow")?.as_float()?;
if step == 0.0 {
return Err("step cannot be zero".into());
}
let ascending = step > 0.0;
let mut val = start;
loop {
let done = if ascending { val > end } else { val < end };
if done {
break;
}
stack.push(float_to_value(val));
val += step;
}
}
Op::Generate => {
let count = stack.pop().ok_or("stack underflow")?.as_int()?;
let quot = stack.pop().ok_or("stack underflow")?;

View File

@@ -20,6 +20,12 @@ pub(super) fn simple_op(name: &str) -> Option<Op> {
"2drop" => Op::Drop2,
"2swap" => Op::Swap2,
"2over" => Op::Over2,
"rev" => Op::Rev,
"shuffle" => Op::Shuffle,
"sort" => Op::Sort,
"rsort" => Op::RSort,
"sum" => Op::Sum,
"prod" => Op::Prod,
"+" => Op::Add,
"-" => Op::Sub,
"*" => Op::Mul,
@@ -83,6 +89,7 @@ pub(super) fn simple_op(name: &str) -> Option<Op> {
"oct" => Op::Oct,
"clear" => Op::ClearCmd,
".." => Op::IntRange,
".," => Op::StepRange,
"gen" => Op::Generate,
"geom.." => Op::GeomRange,
"times" => Op::Times,

View File

@@ -123,6 +123,66 @@ pub(super) const WORDS: &[Word] = &[
compile: Simple,
varargs: false,
},
Word {
name: "rev",
aliases: &[],
category: "Stack",
stack: "(..n n -- ..n)",
desc: "Reverse top n items",
example: "1 2 3 3 rev => 3 2 1",
compile: Simple,
varargs: true,
},
Word {
name: "shuffle",
aliases: &[],
category: "Stack",
stack: "(..n n -- ..n)",
desc: "Randomly shuffle top n items",
example: "1 2 3 3 shuffle",
compile: Simple,
varargs: true,
},
Word {
name: "sort",
aliases: &[],
category: "Stack",
stack: "(..n n -- ..n)",
desc: "Sort top n items ascending",
example: "3 1 2 3 sort => 1 2 3",
compile: Simple,
varargs: true,
},
Word {
name: "rsort",
aliases: &[],
category: "Stack",
stack: "(..n n -- ..n)",
desc: "Sort top n items descending",
example: "1 2 3 3 rsort => 3 2 1",
compile: Simple,
varargs: true,
},
Word {
name: "sum",
aliases: &[],
category: "Stack",
stack: "(..n n -- total)",
desc: "Sum top n items",
example: "1 2 3 3 sum => 6",
compile: Simple,
varargs: true,
},
Word {
name: "prod",
aliases: &[],
category: "Stack",
stack: "(..n n -- product)",
desc: "Multiply top n items",
example: "2 3 4 3 prod => 24",
compile: Simple,
varargs: true,
},
// Arithmetic
Word {
name: "+",

View File

@@ -390,6 +390,16 @@ pub(super) const WORDS: &[Word] = &[
compile: Simple,
varargs: false,
},
Word {
name: ".,",
aliases: &[],
category: "Generator",
stack: "(start end step -- start start+step ...)",
desc: "Push arithmetic sequence with custom step",
example: "0 1 0.25 ., => 0 0.25 0.5 0.75 1",
compile: Simple,
varargs: false,
},
Word {
name: "gen",
aliases: &[],