seq_runtime/combinators.rs
1//! Dataflow combinators for Seq
2//!
3//! Higher-order words that manage value flow on the stack,
4//! reducing the need for explicit stack shuffling (swap/rot/pick)
5//! or auxiliary stack usage (>aux / aux>).
6//!
7//! These follow the concatenative tradition from Factor/Joy:
8//! - `dip` — hide top value, run quotation, restore value
9//! - `keep` — run quotation on top value, but preserve the original
10//! - `bi` — apply two quotations to the same value
11
12use crate::quotations::invoke_callable;
13use crate::stack::{Stack, pop, push};
14
15/// `dip`: Hide top value, run quotation on the rest, restore value.
16///
17/// Stack effect: ( ..a x quot -- ..b x )
18/// where quot : ( ..a -- ..b )
19///
20/// Equivalent to: `swap >aux call aux>`
21///
22/// # Safety
23/// - Stack must have at least 2 values (quotation on top, preserved value below)
24/// - Top of stack must be a Quotation or Closure
25#[unsafe(no_mangle)]
26pub unsafe extern "C" fn patch_seq_dip(stack: Stack) -> Stack {
27 // SAFETY: Caller guarantees stack has quotation on top and a value below.
28 // invoke_callable's safety is documented in quotations.rs.
29 unsafe {
30 let (stack, quot) = pop(stack); // pop quotation
31 let (stack, x) = pop(stack); // pop preserved value
32 let stack = invoke_callable(stack, "); // run quotation on remaining stack
33 push(stack, x) // restore preserved value
34 }
35}
36
37/// `keep`: Run quotation on top value, but preserve the original.
38///
39/// Stack effect: ( ..a x quot -- ..b x )
40/// where quot : ( ..a x -- ..b )
41///
42/// Like `dip`, but the quotation also receives the preserved value.
43/// Equivalent to: `over >aux call aux>`
44///
45/// # Safety
46/// - Stack must have at least 2 values (quotation on top, value below)
47/// - Top of stack must be a Quotation or Closure
48#[unsafe(no_mangle)]
49pub unsafe extern "C" fn patch_seq_keep(stack: Stack) -> Stack {
50 // SAFETY: Caller guarantees stack has quotation on top and a value below.
51 // x is cloned so both the quotation and the restore get valid values.
52 unsafe {
53 let (stack, quot) = pop(stack); // pop quotation
54 let (stack, x) = pop(stack); // pop value to preserve
55 let stack = push(stack, x.clone()); // push copy for quotation to consume
56 let stack = invoke_callable(stack, "); // run quotation (consumes the copy)
57 push(stack, x) // restore original value
58 }
59}
60
61/// `bi`: Apply two quotations to the same value.
62///
63/// Stack effect: ( ..a x quot1 quot2 -- ..c )
64/// where quot1 : ( ..a x -- ..b )
65/// quot2 : ( ..b x -- ..c )
66///
67/// Equivalent to: `>aux keep aux> call`
68///
69/// # Safety
70/// - Stack must have at least 3 values (quot2 on top, quot1 below, value below that)
71/// - Top two stack values must be Quotations or Closures
72#[unsafe(no_mangle)]
73pub unsafe extern "C" fn patch_seq_bi(stack: Stack) -> Stack {
74 // SAFETY: Caller guarantees stack layout. x is cloned so both
75 // quotations receive a valid copy.
76 unsafe {
77 let (stack, quot2) = pop(stack); // pop second quotation
78 let (stack, quot1) = pop(stack); // pop first quotation
79 let (stack, x) = pop(stack); // pop value
80 let stack = push(stack, x.clone()); // push copy for quot1
81 let stack = invoke_callable(stack, "1); // run first quotation
82 let stack = push(stack, x); // push original for quot2
83 invoke_callable(stack, "2) // run second quotation
84 }
85}
86
87// Public re-exports with short names for internal use
88pub use patch_seq_bi as bi;
89pub use patch_seq_dip as dip;
90pub use patch_seq_keep as keep;