pushr/push/
boolean.rs

1use crate::push::instructions::Instruction;
2use crate::push::instructions::InstructionCache;
3use crate::push::item::{Item, PushType};
4use crate::push::state::PushState;
5use crate::push::state::*;
6use rand::Rng;
7use std::collections::HashMap;
8
9pub fn load_boolean_instructions(map: &mut HashMap<String, Instruction>) {
10    map.insert(String::from("BOOLEAN.="), Instruction::new(boolean_eq));
11    map.insert(String::from("BOOLEAN.AND"), Instruction::new(boolean_and));
12    map.insert(
13        String::from("BOOLEAN.DEFINE"),
14        Instruction::new(boolean_def),
15    );
16    map.insert(String::from("BOOLEAN.DUP"), Instruction::new(boolean_dup));
17    map.insert(
18        String::from("BOOLEAN.FLUSH"),
19        Instruction::new(boolean_flush),
20    );
21    map.insert(
22        String::from("BOOLEAN.FROMFLOAT"),
23        Instruction::new(boolean_from_float),
24    );
25    map.insert(
26        String::from("BOOLEAN.FROMINTEGER"),
27        Instruction::new(boolean_from_integer),
28    );
29    map.insert(String::from("BOOLEAN.ID"), Instruction::new(boolean_id));
30    map.insert(String::from("BOOLEAN.NOT"), Instruction::new(boolean_not));
31    map.insert(String::from("BOOLEAN.OR"), Instruction::new(boolean_or));
32    map.insert(String::from("BOOLEAN.POP"), Instruction::new(boolean_pop));
33    map.insert(String::from("BOOLEAN.RAND"), Instruction::new(boolean_rand));
34    map.insert(String::from("BOOLEAN.ROT"), Instruction::new(boolean_rot));
35    map.insert(
36        String::from("BOOLEAN.SHOVE"),
37        Instruction::new(boolean_shove),
38    );
39    map.insert(
40        String::from("BOOLEAN.STACKDEPTH"),
41        Instruction::new(boolean_stack_depth),
42    );
43    map.insert(String::from("BOOLEAN.SWAP"), Instruction::new(boolean_swap));
44    map.insert(String::from("BOOLEAN.YANK"), Instruction::new(boolean_yank));
45    map.insert(
46        String::from("BOOLEAN.YANKDUP"),
47        Instruction::new(boolean_yank_dup),
48    );
49}
50
51/// BOOLEAN.ID: Pushes the ID of the BOOLEAN stack to the INTEGER stack.
52pub fn boolean_id(push_state: &mut PushState, _instruction_set: &InstructionCache) {
53    push_state.int_stack.push(BOOL_STACK_ID);
54}
55
56/// BOOLEAN.=: Pushes TRUE if the top two BOOLEANs are equal, or FALSE otherwise.
57pub fn boolean_eq(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
58    if let Some(pv) = push_state.bool_stack.pop_vec(2) {
59        push_state.bool_stack.push(pv[0] == pv[1]);
60    }
61}
62
63/// BOOLEAN.AND: Pushes the logical AND of the top two BOOLEANs.
64pub fn boolean_and(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
65    if let Some(pv) = push_state.bool_stack.pop_vec(2) {
66        push_state.bool_stack.push(pv[0] && pv[1]);
67    }
68}
69
70/// BOOLEAN.OR: Pushes the logical OR of the top two BOOLEANs.
71pub fn boolean_or(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
72    if let Some(pv) = push_state.bool_stack.pop_vec(2) {
73        push_state.bool_stack.push(pv[0] || pv[1]);
74    }
75}
76
77/// BOOLEAN.DEFINE: Defines the name on top of the NAME stack as an instruction that will push the
78/// top item of the BOOLEAN stack onto the EXEC stack.
79pub fn boolean_def(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
80    if let Some(name) = push_state.name_stack.pop() {
81        if let Some(bval) = push_state.bool_stack.pop() {
82            push_state.name_bindings.insert(
83                name,
84                Item::Literal {
85                    push_type: PushType::Bool { val: bval },
86                },
87            );
88        }
89    }
90}
91
92/// BOOLEAN.DUP: Duplicates the top item on the BOOLEAN stack. Does not pop its argument (which, if
93/// it did, would negate the effect of the duplication!).
94pub fn boolean_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
95    if let Some(pv) = push_state.bool_stack.copy_vec(1) {
96        push_state.bool_stack.push(pv[0]);
97    }
98}
99
100/// BOOLEAN.FLUSH: Empties the BOOLEAN stack.
101pub fn boolean_flush(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
102    push_state.bool_stack.flush();
103}
104
105/// BOOLEAN.FROMFLOAT: Pushes FALSE if the top FLOAT is 0.0, or TRUE otherwise.
106pub fn boolean_from_float(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
107    if let Some(pv) = push_state.float_stack.copy_vec(1) {
108        let x = pv[0] == 0.0;
109        push_state.bool_stack.push(x);
110    }
111}
112
113/// BOOLEAN.FROMINTEGER: Pushes FALSE if the top INTEGER is 0, or TRUE otherwise.
114pub fn boolean_from_integer(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
115    if let Some(pv) = push_state.int_stack.copy_vec(1) {
116        let x = pv[0] == 0;
117        push_state.bool_stack.push(x);
118    }
119}
120
121/// BOOLEAN.NOT: Pushes the logical NOT of the top BOOLEAN.
122pub fn boolean_not(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
123    if let Some(pv) = push_state.bool_stack.pop() {
124        push_state.bool_stack.push(!pv);
125    }
126}
127
128/// BOOLEAN.OR: Pushes the logical OR of the top two BOOLEANs.
129pub fn boolean_pop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
130    push_state.bool_stack.pop();
131}
132
133/// BOOLEAN.POP: Pops the BOOLEAN stack.
134pub fn boolean_rand(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
135    let mut rng = rand::thread_rng();
136    let bval = rng.gen_range(0..2) == 1;
137    push_state.bool_stack.push(bval);
138}
139
140/// BOOLEAN.ROT: Rotates the top three items on the BOOLEAN stack, pulling the third item out and
141/// pushing it on top. This is equivalent to "2 BOOLEAN.YANK".
142pub fn boolean_rot(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
143    push_state.bool_stack.yank(2);
144}
145
146/// BOOLEAN.SHOVE: Inserts the top BOOLEAN "deep" in the stack, at the position indexed by the top
147/// INTEGER.
148pub fn boolean_shove(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
149    if let Some(shove_index) = push_state.int_stack.pop() {
150        let corr_index = i32::max(
151            i32::min((push_state.bool_stack.size() as i32) - 1, shove_index),
152            0,
153        ) as usize;
154        push_state.bool_stack.shove(corr_index as usize);
155    }
156}
157
158/// BOOLEAN.STACKDEPTH: Pushes the stack depth onto the INTEGER stack.
159pub fn boolean_stack_depth(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
160    push_state
161        .int_stack
162        .push(push_state.bool_stack.size() as i32);
163}
164
165/// BOOLEAN.SWAP: Swaps the top two BOOLEANs.
166pub fn boolean_swap(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
167    push_state.bool_stack.shove(1);
168}
169
170/// BOOLEAN.YANK: Removes an indexed item from "deep" in the stack and pushes it on top of the
171/// stack. The index is taken from the INTEGER stack.
172pub fn boolean_yank(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
173    if let Some(index) = push_state.int_stack.pop() {
174        let corr_index = i32::max(
175            i32::min((push_state.bool_stack.size() as i32) - 1, index),
176            0,
177        ) as usize;
178        push_state.bool_stack.yank(corr_index as usize);
179    }
180}
181
182/// BOOLEAN.YANKDUP: Pushes a copy of an indexed item "deep" in the stack onto the top of the
183/// stack, without removing the deep item. The index is taken from the INTEGER stack.
184pub fn boolean_yank_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
185    if let Some(idx) = push_state.int_stack.pop() {
186        if let Some(deep_item) = push_state.bool_stack.copy(idx as usize) {
187            push_state.bool_stack.push(deep_item);
188        }
189    }
190}
191#[cfg(test)]
192mod tests {
193    use super::*;
194
195    pub fn icache() -> InstructionCache {
196        InstructionCache::new(vec![])
197    }
198
199    #[test]
200    fn boolean_equal_pushes_result() {
201        let mut test_state = PushState::new();
202        test_state.bool_stack.push(true);
203        test_state.bool_stack.push(true);
204        boolean_eq(&mut test_state, &icache());
205        assert_eq!(test_state.bool_stack.pop().unwrap(), true);
206    }
207
208    #[test]
209    fn boolean_and_pushes_result() {
210        let mut test_state = PushState::new();
211        test_state.bool_stack.push(true);
212        test_state.bool_stack.push(false);
213        boolean_and(&mut test_state, &icache());
214        assert_eq!(test_state.bool_stack.pop().unwrap(), false);
215        test_state.bool_stack.push(true);
216        test_state.bool_stack.push(true);
217        boolean_and(&mut test_state, &icache());
218        assert_eq!(test_state.bool_stack.pop().unwrap(), true);
219    }
220
221    #[test]
222    fn boolean_define_creates_name_binding() {
223        let mut test_state = PushState::new();
224        test_state.bool_stack.push(true);
225        test_state.name_stack.push(String::from("TEST"));
226        boolean_def(&mut test_state, &icache());
227        assert_eq!(
228            *test_state.name_bindings.get("TEST").unwrap().to_string(),
229            Item::bool(true).to_string()
230        );
231    }
232
233    #[test]
234    fn boolean_dup_copies_top_element() {
235        let mut test_state = PushState::new();
236        test_state.bool_stack.push(false);
237        boolean_dup(&mut test_state, &icache());
238        assert_eq!(test_state.bool_stack.to_string(), "FALSE FALSE");
239    }
240
241    #[test]
242    fn boolean_flush_empties_stack() {
243        let mut test_state = PushState::new();
244        test_state.bool_stack.push(true);
245        test_state.bool_stack.push(false);
246        boolean_flush(&mut test_state, &icache());
247        assert_eq!(test_state.bool_stack.to_string(), "");
248    }
249
250    #[test]
251    fn boolean_from_float_compares_to_zero() {
252        let mut test_state = PushState::new();
253        test_state.float_stack.push(0.0);
254        boolean_from_float(&mut test_state, &icache());
255        assert_eq!(test_state.bool_stack.to_string(), "TRUE");
256        test_state.float_stack.push(0.01);
257        boolean_from_float(&mut test_state, &icache());
258        assert_eq!(test_state.bool_stack.to_string(), "FALSE TRUE");
259    }
260
261    #[test]
262    fn boolean_from_integer_compares_to_zero() {
263        let mut test_state = PushState::new();
264        test_state.int_stack.push(0);
265        boolean_from_integer(&mut test_state, &icache());
266        assert_eq!(test_state.bool_stack.to_string(), "TRUE");
267        test_state.int_stack.push(1);
268        boolean_from_integer(&mut test_state, &icache());
269        assert_eq!(test_state.bool_stack.to_string(), "FALSE TRUE");
270    }
271
272    #[test]
273    fn boolean_or_pushes_result() {
274        let mut test_state = PushState::new();
275        test_state.bool_stack.push(true);
276        test_state.bool_stack.push(false);
277        boolean_or(&mut test_state, &icache());
278        assert_eq!(test_state.bool_stack.pop().unwrap(), true);
279        test_state.bool_stack.push(false);
280        test_state.bool_stack.push(false);
281        boolean_or(&mut test_state, &icache());
282        assert_eq!(test_state.bool_stack.pop().unwrap(), false);
283    }
284
285    #[test]
286    fn boolean_not_pushes_result() {
287        let mut test_state = PushState::new();
288        test_state.bool_stack.push(true);
289        boolean_not(&mut test_state, &icache());
290        assert_eq!(test_state.bool_stack.pop().unwrap(), false);
291        test_state.bool_stack.push(false);
292        boolean_not(&mut test_state, &icache());
293        assert_eq!(test_state.bool_stack.pop().unwrap(), true);
294    }
295
296    #[test]
297    fn boolean_pop_removes_top_element() {
298        let mut test_state = PushState::new();
299        test_state.bool_stack.push(true);
300        test_state.bool_stack.push(false);
301        boolean_pop(&mut test_state, &icache());
302        assert_eq!(test_state.bool_stack.to_string(), "TRUE");
303    }
304
305    #[test]
306    fn boolean_rand_generates_value() {
307        let mut test_state = PushState::new();
308        boolean_rand(&mut test_state, &icache());
309        assert_eq!(test_state.bool_stack.size(), 1);
310    }
311
312    #[test]
313    fn boolean_rot_shuffles_elements() {
314        let mut test_state = PushState::new();
315        test_state.bool_stack.push(true);
316        test_state.bool_stack.push(true);
317        test_state.bool_stack.push(false);
318        assert_eq!(
319            test_state.bool_stack.to_string(),
320            "FALSE TRUE TRUE"
321        );
322        boolean_rot(&mut test_state, &icache());
323        assert_eq!(
324            test_state.bool_stack.to_string(),
325            "TRUE FALSE TRUE"
326        );
327    }
328
329    #[test]
330    fn boolean_shove_inserts_at_right_position() {
331        let mut test_state = PushState::new();
332        test_state.bool_stack.push(true);
333        test_state.bool_stack.push(true);
334        test_state.bool_stack.push(true);
335        test_state.bool_stack.push(false);
336        assert_eq!(
337            test_state.bool_stack.to_string(),
338            "FALSE TRUE TRUE TRUE"
339        );
340        test_state.int_stack.push(2);
341        boolean_shove(&mut test_state, &icache());
342        assert_eq!(
343            test_state.bool_stack.to_string(),
344            "TRUE TRUE FALSE TRUE"
345        );
346    }
347
348    #[test]
349    fn boolean_stack_depth_returns_size() {
350        let mut test_state = PushState::new();
351        test_state.bool_stack.push(true);
352        test_state.bool_stack.push(true);
353        test_state.bool_stack.push(true);
354        test_state.bool_stack.push(false);
355        boolean_stack_depth(&mut test_state, &icache());
356        assert_eq!(test_state.int_stack.to_string(), "4");
357    }
358
359    #[test]
360    fn boolean_swaps_top_elements() {
361        let mut test_state = PushState::new();
362        test_state.bool_stack.push(true);
363        test_state.bool_stack.push(false);
364        assert_eq!(test_state.bool_stack.to_string(), "FALSE TRUE");
365        boolean_swap(&mut test_state, &icache());
366        assert_eq!(test_state.bool_stack.to_string(), "TRUE FALSE");
367    }
368
369    #[test]
370    fn boolean_yank_brings_item_to_top() {
371        let mut test_state = PushState::new();
372        test_state.bool_stack.push(true);
373        test_state.bool_stack.push(false);
374        test_state.bool_stack.push(true);
375        test_state.bool_stack.push(true);
376        assert_eq!(
377            test_state.bool_stack.to_string(),
378            "TRUE TRUE FALSE TRUE"
379        );
380        test_state.int_stack.push(2);
381        boolean_yank(&mut test_state, &icache());
382        assert_eq!(
383            test_state.bool_stack.to_string(),
384            "FALSE TRUE TRUE TRUE"
385        );
386    }
387
388    #[test]
389    fn boolean_yank_dup_copies_item_to_top() {
390        let mut test_state = PushState::new();
391        test_state.bool_stack.push(true);
392        test_state.bool_stack.push(false);
393        test_state.bool_stack.push(true);
394        test_state.bool_stack.push(true);
395        assert_eq!(
396            test_state.bool_stack.to_string(),
397            "TRUE TRUE FALSE TRUE"
398        );
399        test_state.int_stack.push(2);
400        boolean_yank_dup(&mut test_state, &icache());
401        assert_eq!(
402            test_state.bool_stack.to_string(),
403            "FALSE TRUE TRUE FALSE TRUE"
404        );
405    }
406}