1use crate::stack::{Stack, pop};
7use crate::value::Value;
8
9#[unsafe(no_mangle)]
68pub unsafe extern "C" fn patch_seq_cond(mut stack: Stack) -> Stack {
69 unsafe {
70 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 let mut pairs = Vec::with_capacity(count);
86 stack = stack_temp;
87
88 for _ in 0..count {
89 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 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 pairs.reverse();
110
111 for (pred_ptr, body_ptr) in pairs {
114 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 stack = pred_fn(stack);
120
121 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 stack = body_fn(stack_after_pred);
132 return stack;
133 }
134
135 stack = stack_after_pred;
137 }
138
139 panic!("cond: no predicate matched");
141 }
142}
143
144pub use patch_seq_cond as cond;
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use crate::stack::push;
151
152 unsafe extern "C" fn pred_always_true(stack: Stack) -> Stack {
155 unsafe { push(stack, Value::Bool(true)) }
156 }
157
158 unsafe extern "C" fn pred_always_false(stack: Stack) -> Stack {
161 unsafe { push(stack, Value::Bool(false)) }
162 }
163
164 unsafe extern "C" fn pred_is_zero(stack: Stack) -> Stack {
167 unsafe {
168 let val = crate::stack::peek(stack);
169 match val {
170 Value::Int(n) => push(stack, Value::Bool(n == 0)),
171 _ => panic!("pred_is_zero: expected Int"),
172 }
173 }
174 }
175
176 unsafe extern "C" fn pred_is_negative(stack: Stack) -> Stack {
179 unsafe {
180 let val = crate::stack::peek(stack);
181 match val {
182 Value::Int(n) => push(stack, Value::Bool(n < 0)),
183 _ => panic!("pred_is_negative: expected Int"),
184 }
185 }
186 }
187
188 unsafe extern "C" fn body_matched(stack: Stack) -> Stack {
191 unsafe {
192 let (stack, _) = pop(stack);
193 push(
194 stack,
195 Value::String(crate::seqstring::global_string("matched".to_string())),
196 )
197 }
198 }
199
200 unsafe extern "C" fn body_zero(stack: Stack) -> Stack {
203 unsafe {
204 let (stack, _) = pop(stack);
205 push(
206 stack,
207 Value::String(crate::seqstring::global_string("zero".to_string())),
208 )
209 }
210 }
211
212 unsafe extern "C" fn body_positive(stack: Stack) -> Stack {
215 unsafe {
216 let (stack, _) = pop(stack);
217 push(
218 stack,
219 Value::String(crate::seqstring::global_string("positive".to_string())),
220 )
221 }
222 }
223
224 unsafe extern "C" fn body_negative(stack: Stack) -> Stack {
227 unsafe {
228 let (stack, _) = pop(stack);
229 push(
230 stack,
231 Value::String(crate::seqstring::global_string("negative".to_string())),
232 )
233 }
234 }
235
236 unsafe extern "C" fn body_default(stack: Stack) -> Stack {
239 unsafe {
240 let (stack, _) = pop(stack);
241 push(
242 stack,
243 Value::String(crate::seqstring::global_string("default".to_string())),
244 )
245 }
246 }
247
248 fn make_quotation(f: unsafe extern "C" fn(Stack) -> Stack) -> Value {
250 let ptr = f as *const () as usize;
251 Value::Quotation {
252 wrapper: ptr,
253 impl_: ptr,
254 }
255 }
256
257 #[test]
258 fn test_cond_single_match() {
259 unsafe {
262 let stack = crate::stack::alloc_test_stack();
263 let stack = push(stack, Value::Int(42)); let stack = push(stack, make_quotation(pred_always_true));
265 let stack = push(stack, make_quotation(body_matched));
266 let stack = push(stack, Value::Int(1)); let stack = cond(stack);
269
270 let (_, result) = pop(stack);
271 match result {
272 Value::String(s) => assert_eq!(s.as_str(), "matched"),
273 _ => panic!("Expected String, got {:?}", result),
274 }
275 }
276 }
277
278 #[test]
279 fn test_cond_first_match_wins() {
280 unsafe {
284 let stack = crate::stack::alloc_test_stack();
285 let stack = push(stack, Value::Int(42)); let stack = push(stack, make_quotation(pred_always_true));
287 let stack = push(stack, make_quotation(body_matched)); let stack = push(stack, make_quotation(pred_always_true));
289 let stack = push(stack, make_quotation(body_default)); let stack = push(stack, Value::Int(2)); let stack = cond(stack);
293
294 let (_, result) = pop(stack);
295 match result {
296 Value::String(s) => assert_eq!(s.as_str(), "matched"), _ => panic!("Expected String, got {:?}", result),
298 }
299 }
300 }
301
302 #[test]
303 fn test_cond_second_match() {
304 unsafe {
307 let stack = crate::stack::alloc_test_stack();
308 let stack = push(stack, Value::Int(42)); let stack = push(stack, make_quotation(pred_always_false));
310 let stack = push(stack, make_quotation(body_matched)); let stack = push(stack, make_quotation(pred_always_true));
312 let stack = push(stack, make_quotation(body_default)); let stack = push(stack, Value::Int(2)); let stack = cond(stack);
316
317 let (_, result) = pop(stack);
318 match result {
319 Value::String(s) => assert_eq!(s.as_str(), "default"), _ => panic!("Expected String, got {:?}", result),
321 }
322 }
323 }
324
325 #[test]
326 fn test_cond_classify_number() {
327 unsafe {
330 let stack = crate::stack::alloc_test_stack();
332 let stack = push(stack, Value::Int(-5)); let stack = push(stack, make_quotation(pred_is_negative));
334 let stack = push(stack, make_quotation(body_negative));
335 let stack = push(stack, make_quotation(pred_is_zero));
336 let stack = push(stack, make_quotation(body_zero));
337 let stack = push(stack, make_quotation(pred_always_true)); let stack = push(stack, make_quotation(body_positive));
339 let stack = push(stack, Value::Int(3)); let stack = cond(stack);
342 let (_, result) = pop(stack);
343 match result {
344 Value::String(s) => assert_eq!(s.as_str(), "negative"),
345 _ => panic!("Expected String"),
346 }
347
348 let stack = crate::stack::alloc_test_stack();
350 let stack = push(stack, Value::Int(0)); let stack = push(stack, make_quotation(pred_is_negative));
352 let stack = push(stack, make_quotation(body_negative));
353 let stack = push(stack, make_quotation(pred_is_zero));
354 let stack = push(stack, make_quotation(body_zero));
355 let stack = push(stack, make_quotation(pred_always_true)); let stack = push(stack, make_quotation(body_positive));
357 let stack = push(stack, Value::Int(3)); let stack = cond(stack);
360 let (_, result) = pop(stack);
361 match result {
362 Value::String(s) => assert_eq!(s.as_str(), "zero"),
363 _ => panic!("Expected String"),
364 }
365
366 let stack = crate::stack::alloc_test_stack();
368 let stack = push(stack, Value::Int(42)); let stack = push(stack, make_quotation(pred_is_negative));
370 let stack = push(stack, make_quotation(body_negative));
371 let stack = push(stack, make_quotation(pred_is_zero));
372 let stack = push(stack, make_quotation(body_zero));
373 let stack = push(stack, make_quotation(pred_always_true)); let stack = push(stack, make_quotation(body_positive));
375 let stack = push(stack, Value::Int(3)); let stack = cond(stack);
378 let (_, result) = pop(stack);
379 match result {
380 Value::String(s) => assert_eq!(s.as_str(), "positive"),
381 _ => panic!("Expected String"),
382 }
383 }
384 }
385
386 }