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