Skip to main content

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, &quot); // 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, &quot); // 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, &quot1); // run first quotation
82        let stack = push(stack, x); // push original for quot2
83        invoke_callable(stack, &quot2) // 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;