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;