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;