seq_runtime/
cond.rs

1//! Conditional combinator for multi-way branching
2//!
3//! Provides `cond` - a concatenative alternative to match/case statements.
4
5use crate::stack::{Stack, pop};
6use crate::value::Value;
7
8/// Multi-way conditional combinator
9///
10/// Takes N predicate/body quotation pairs from the stack, plus a value to test.
11/// Tries each predicate in order (last to first on stack). When a predicate
12/// returns non-zero, executes its corresponding body and returns.
13///
14/// Stack effect: ( value [pred1] [body1] [pred2] [body2] ... [predN] [bodyN] count -- result )
15///
16/// Each predicate quotation has effect: ( value -- value bool )
17/// Each body quotation has effect: ( value -- result )
18///
19/// Example:
20/// ```cem
21/// : route ( request -- response )
22///   [ dup "GET /" = ]           [ drop "Hello" ]
23///   [ dup "/api" starts-with ]  [ get-users ]
24///   [ drop 1 ]                  [ drop "Not Found" ]
25///   3 cond ;
26/// ```
27///
28/// # Safety
29/// - Stack must have at least (2*count + 2) values
30/// - All predicate/body values must be Quotations
31/// - Predicates must return Int (0 or non-zero)
32#[unsafe(no_mangle)]
33pub unsafe extern "C" fn patch_seq_cond(mut stack: Stack) -> Stack {
34    unsafe {
35        // Pop count
36        let (stack_temp, count_val) = pop(stack);
37        let count = match count_val {
38            Value::Int(n) if n >= 0 => n as usize,
39            Value::Int(n) => panic!("cond: count must be non-negative, got {}", n),
40            _ => panic!("cond: expected Int count, got {:?}", count_val),
41        };
42
43        if count == 0 {
44            panic!("cond: need at least one predicate/body pair");
45        }
46
47        // Pop all predicate/body pairs into a vector
48        // Stack is [ value pred1 body1 pred2 body2 ... predN bodyN ]
49        // We pop from top (bodyN) down to bottom (pred1)
50        let mut pairs = Vec::with_capacity(count);
51        stack = stack_temp;
52
53        for _ in 0..count {
54            // Pop body quotation
55            let (stack_temp, body_val) = pop(stack);
56            let body_wrapper = match body_val {
57                Value::Quotation { wrapper, .. } => wrapper,
58                _ => panic!("cond: expected body Quotation, got {:?}", body_val),
59            };
60
61            // Pop predicate quotation
62            let (stack_temp2, pred_val) = pop(stack_temp);
63            let pred_wrapper = match pred_val {
64                Value::Quotation { wrapper, .. } => wrapper,
65                _ => panic!("cond: expected predicate Quotation, got {:?}", pred_val),
66            };
67
68            stack = stack_temp2;
69            pairs.push((pred_wrapper, body_wrapper));
70        }
71
72        // Now pairs is in reverse order (last pair at index 0)
73        // Reverse it so we try first pair first
74        pairs.reverse();
75
76        // Value is now on top of stack
77        // For each pair, dup value, run predicate, check result
78        for (pred_ptr, body_ptr) in pairs {
79            // Cast function pointers
80            let pred_fn: unsafe extern "C" fn(Stack) -> Stack = std::mem::transmute(pred_ptr);
81            let body_fn: unsafe extern "C" fn(Stack) -> Stack = std::mem::transmute(body_ptr);
82
83            // Execute predicate (keeps value on stack, adds boolean result)
84            stack = pred_fn(stack);
85
86            // Pop predicate result
87            let (stack_after_pred, pred_result) = pop(stack);
88
89            let matches = match pred_result {
90                Value::Int(0) => false,
91                Value::Int(_) => true,
92                _ => panic!("cond: predicate must return Int, got {:?}", pred_result),
93            };
94
95            if matches {
96                // Predicate matched! Execute body and return
97                stack = body_fn(stack_after_pred);
98                return stack;
99            }
100
101            // Predicate didn't match, try next pair
102            stack = stack_after_pred;
103        }
104
105        // No predicate matched!
106        panic!("cond: no predicate matched");
107    }
108}
109
110// Public re-export with short name for internal use
111pub use patch_seq_cond as cond;