seq_runtime/
cond.rs

1//! Conditional combinator for multi-way branching
2//!
3//! Provides `cond` - a concatenative alternative to match/case statements.
4//! Uses quotation pairs (predicate + body) evaluated in order until one matches.
5
6use crate::stack::{Stack, pop};
7use crate::value::Value;
8
9/// Multi-way conditional combinator
10///
11/// # Stack Effect
12///
13/// `( value [pred1] [body1] ... [predN] [bodyN] N -- result )`
14///
15/// # How It Works
16///
17/// 1. Takes a value and N predicate/body quotation pairs from the stack
18/// 2. Tries each predicate in order (first pair = first tried)
19/// 3. When a predicate returns true, executes its body and returns
20/// 4. Panics if no predicate matches (always include a default case)
21///
22/// # Quotation Contracts
23///
24/// - **Predicate**: `( value -- value Bool )` - keeps value on stack, pushes true or false
25/// - **Body**: `( value -- result )` - consumes value, produces result
26///
27/// # Default Case Pattern
28///
29/// Use `[ true ]` as the last predicate to create an "otherwise" case that always matches:
30///
31/// ```text
32/// [ true ] [ drop "default result" ]
33/// ```
34///
35/// # Example: Classify a Number
36///
37/// ```text
38/// : classify ( Int -- String )
39///   [ dup 0 i.< ]  [ drop "negative" ]
40///   [ dup 0 i.= ]  [ drop "zero" ]
41///   [ true ]       [ drop "positive" ]
42///   3 cond
43/// ;
44///
45/// -5 classify   # "negative"
46/// 0 classify    # "zero"
47/// 42 classify   # "positive"
48/// ```
49///
50/// # Example: FizzBuzz Logic
51///
52/// ```text
53/// : fizzbuzz ( Int -- String )
54///   [ dup 15 i.% 0 i.= ]  [ drop "FizzBuzz" ]
55///   [ dup 3 i.% 0 i.= ]   [ drop "Fizz" ]
56///   [ dup 5 i.% 0 i.= ]   [ drop "Buzz" ]
57///   [ true ]              [ int->string ]
58///   4 cond
59/// ;
60/// ```
61///
62/// # Safety
63///
64/// - Stack must have at least (2*N + 1) values (value + N pairs)
65/// - All predicate/body values must be Quotations
66/// - Predicates must return Bool
67#[unsafe(no_mangle)]
68pub unsafe extern "C" fn patch_seq_cond(mut stack: Stack) -> Stack {
69    unsafe {
70        // Pop count
71        let (stack_temp, count_val) = pop(stack);
72        let count = match count_val {
73            Value::Int(n) if n >= 0 => n as usize,
74            Value::Int(n) => panic!("cond: count must be non-negative, got {}", n),
75            _ => panic!("cond: expected Int count, got {:?}", count_val),
76        };
77
78        if count == 0 {
79            panic!("cond: need at least one predicate/body pair");
80        }
81
82        // Pop all predicate/body pairs into a vector
83        // Stack is [ value pred1 body1 pred2 body2 ... predN bodyN ]
84        // We pop from top (bodyN) down to bottom (pred1)
85        let mut pairs = Vec::with_capacity(count);
86        stack = stack_temp;
87
88        for _ in 0..count {
89            // Pop body quotation
90            let (stack_temp, body_val) = pop(stack);
91            let body_wrapper = match body_val {
92                Value::Quotation { wrapper, .. } => wrapper,
93                _ => panic!("cond: expected body Quotation, got {:?}", body_val),
94            };
95
96            // Pop predicate quotation
97            let (stack_temp2, pred_val) = pop(stack_temp);
98            let pred_wrapper = match pred_val {
99                Value::Quotation { wrapper, .. } => wrapper,
100                _ => panic!("cond: expected predicate Quotation, got {:?}", pred_val),
101            };
102
103            stack = stack_temp2;
104            pairs.push((pred_wrapper, body_wrapper));
105        }
106
107        // Now pairs is in reverse order (last pair at index 0)
108        // Reverse it so we try first pair first
109        pairs.reverse();
110
111        // Value is now on top of stack
112        // For each pair, dup value, run predicate, check result
113        for (pred_ptr, body_ptr) in pairs {
114            // Cast function pointers
115            let pred_fn: unsafe extern "C" fn(Stack) -> Stack = std::mem::transmute(pred_ptr);
116            let body_fn: unsafe extern "C" fn(Stack) -> Stack = std::mem::transmute(body_ptr);
117
118            // Execute predicate (keeps value on stack, adds boolean result)
119            stack = pred_fn(stack);
120
121            // Pop predicate result
122            let (stack_after_pred, pred_result) = pop(stack);
123
124            let matches = match pred_result {
125                Value::Bool(b) => b,
126                _ => panic!("cond: predicate must return Bool, got {:?}", pred_result),
127            };
128
129            if matches {
130                // Predicate matched! Execute body and return
131                stack = body_fn(stack_after_pred);
132                return stack;
133            }
134
135            // Predicate didn't match, try next pair
136            stack = stack_after_pred;
137        }
138
139        // No predicate matched!
140        panic!("cond: no predicate matched");
141    }
142}
143
144// Public re-export with short name for internal use
145pub use patch_seq_cond as cond;