pushr/push/
name.rs

1use crate::push::instructions::Instruction;
2use crate::push::instructions::InstructionCache;
3use crate::push::random::CodeGenerator;
4use crate::push::state::PushState;
5use crate::push::state::*;
6use std::collections::HashMap;
7
8/// For creating bindings between symbolic identifiers and values of various types; that is,
9/// for implementing (global) variables and defined instructions. Bindings are created with
10/// DEFINE instructions. Any identifier that is not a known Push instruction or a known literal
11/// of any other type is considered a NAME and will be pushed onto the NAME stack when
12/// encountered, unless it has a definition (in which case its associated value will be pushed
13/// on the EXEC stack when it is encountered. The NAME.QUOTE instruction can be used to get a
14/// name that already has a definition onto the NAME stack.
15pub fn load_name_instructions(map: &mut HashMap<String, Instruction>) {
16    map.insert(String::from("NAME.="), Instruction::new(name_equal));
17    map.insert(String::from("NAME.CAT"), Instruction::new(name_cat));
18    map.insert(String::from("NAME.DUP"), Instruction::new(name_dup));
19    map.insert(String::from("NAME.FLUSH"), Instruction::new(name_flush));
20    map.insert(String::from("NAME.ID"), Instruction::new(name_id));
21    map.insert(String::from("NAME.POP"), Instruction::new(name_pop));
22    map.insert(String::from("NAME.QUOTE"), Instruction::new(name_quote));
23    map.insert(String::from("NAME.RAND"), Instruction::new(name_rand));
24    map.insert(
25        String::from("NAME.RANDBOUNDNAME"),
26        Instruction::new(name_rand_bound),
27    );
28    map.insert(String::from("NAME.ROT"), Instruction::new(name_rot));
29    map.insert(String::from("NAME.SEND"), Instruction::new(name_send));
30    map.insert(String::from("NAME.SHOVE"), Instruction::new(name_shove));
31    map.insert(
32        String::from("NAME.STACKDEPTH"),
33        Instruction::new(name_stack_depth),
34    );
35    map.insert(String::from("NAME.SWAP"), Instruction::new(name_swap));
36    map.insert(String::from("NAME.YANK"), Instruction::new(name_yank));
37    map.insert(
38        String::from("NAME.YANKDUP"),
39        Instruction::new(name_yank_dup),
40    );
41}
42
43/// NAME.ID: Pushes the ID of the NAME stack to the INTEGER stack.
44pub fn name_id(push_state: &mut PushState, _instruction_set: &InstructionCache) {
45    push_state.int_stack.push(NAME_STACK_ID);
46}
47
48/// NAME.CAT: Pushes the concatenation of the two topmost items where top item
49/// will be appended.
50fn name_cat(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
51    if let Some(nvals) = push_state.name_stack.pop_vec(2) {
52        let mut catstr = nvals[0].clone();
53        catstr.push_str(&" ".to_string());
54        catstr.push_str(&nvals[1]);
55        push_state.name_stack.push(catstr);
56    }
57}
58
59/// NAME.=: Pushes TRUE if the top two NAMEs are equal, or FALSE otherwise.
60fn name_equal(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
61    if let Some(nvals) = push_state.name_stack.pop_vec(2) {
62        push_state.bool_stack.push(nvals[0] == nvals[1]);
63    }
64}
65
66/// NAME.DUP: Duplicates the top item on the NAME stack. Does not pop its argument (which, if it
67/// did, would negate the effect of the duplication!).
68pub fn name_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
69    if let Some(nval) = push_state.name_stack.copy(0) {
70        push_state.name_stack.push(nval);
71    }
72}
73
74/// NAME.FLUSH: Empties the NAME stack.
75pub fn name_flush(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
76    push_state.name_stack.flush();
77}
78
79/// NAME.POP: Pops the NAME stack.
80pub fn name_pop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
81    push_state.name_stack.pop();
82}
83
84/// NAME.QUOTE: Sets a flag indicating that the next name encountered will be pushed onto the NAME
85/// stack (and not have its associated value pushed onto the EXEC stack), regardless of whether or
86/// not it has a definition. Upon encountering such a name and pushing it onto the NAME stack the
87/// flag will be cleared (whether or not the pushed name had a definition).
88pub fn name_quote(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
89    push_state.quote_name = true;
90}
91
92/// NAME.RAND: Pushes a newly generated random NAME.
93pub fn name_rand(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
94    push_state.name_stack.push(CodeGenerator::new_random_name());
95}
96
97/// NAME.RANDBOUNDNAME: Pushes a randomly selected NAME that already has a definition.
98pub fn name_rand_bound(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
99    push_state
100        .name_stack
101        .push(CodeGenerator::existing_random_name(push_state));
102}
103
104/// NAME.ROT: Rotates the top three items on the NAME stack, pulling the third item out and pushing
105/// it on top. This is equivalent to "2 NAME.YANK".
106pub fn name_rot(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
107    push_state.name_stack.yank(2);
108}
109
110/// NAME.SEND: Flags the top NAME item to be sent via the com module. 
111pub fn name_send(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
112    push_state.send_name = true;
113}
114
115/// NAME.SHOVE: Inserts the top NAME "deep" in the stack, at the position indexed by the top
116/// INTEGER.
117pub fn name_shove(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
118    if let Some(shove_index) = push_state.int_stack.pop() {
119        let corr_index = i32::max(
120            i32::min((push_state.name_stack.size() as i32) - 1, shove_index),
121            0,
122        ) as usize;
123        push_state.name_stack.shove(corr_index as usize);
124    }
125}
126
127/// NAME.STACKDEPTH: Pushes the stack depth onto the INTEGER stack.
128pub fn name_stack_depth(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
129    push_state
130        .int_stack
131        .push(push_state.name_stack.size() as i32);
132}
133
134/// NAME.SWAP: Swaps the top two NAMEs.
135pub fn name_swap(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
136    push_state.name_stack.shove(1);
137}
138
139/// NAME.YANK: Removes an indexed item from "deep" in the stack and pushes it on top of the stack.
140/// The index is taken from the INTEGER stack.
141pub fn name_yank(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
142    if let Some(index) = push_state.int_stack.pop() {
143        let corr_index = i32::max(
144            i32::min((push_state.name_stack.size() as i32) - 1, index),
145            0,
146        ) as usize;
147        push_state.name_stack.yank(corr_index as usize);
148    }
149}
150
151/// NAME.YANKDUP: Pushes a copy of an indexed item "deep" in the stack onto the top of the stack,
152/// without removing the deep item. The index is taken from the INTEGER stack.
153pub fn name_yank_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
154    if let Some(index) = push_state.int_stack.pop() {
155        let corr_index = i32::max(
156            i32::min((push_state.name_stack.size() as i32) - 1, index),
157            0,
158        ) as usize;
159        if let Some(deep_item) = push_state.name_stack.copy(corr_index) {
160            push_state.name_stack.push(deep_item);
161        }
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use crate::push::item::Item;
169
170    pub fn icache() -> InstructionCache {
171        InstructionCache::new(vec![])
172    }
173
174    #[test]
175    fn name_cat_appends_second_item() {
176        let mut test_state = PushState::new();
177        test_state.name_stack.push(String::from("Test"));
178        test_state.name_stack.push(String::from("Test"));
179        name_cat(&mut test_state, &icache());
180        assert_eq!(test_state.name_stack.pop().unwrap(), "Test Test".to_string());
181    }
182
183    #[test]
184    fn name_equal_pushes_result() {
185        let mut test_state = PushState::new();
186        test_state.name_stack.push(String::from("Test"));
187        test_state.name_stack.push(String::from("Test"));
188        name_equal(&mut test_state, &icache());
189        assert_eq!(test_state.bool_stack.pop().unwrap(), true);
190    }
191
192    #[test]
193    fn name_dup_copies_top_element() {
194        let mut test_state = PushState::new();
195        test_state.name_stack.push(String::from("Test"));
196        name_dup(&mut test_state, &icache());
197        assert_eq!(test_state.name_stack.to_string(), "Test Test");
198    }
199
200    #[test]
201    fn name_flush_empties_stack() {
202        let mut test_state = PushState::new();
203        test_state.name_stack.push(String::from("I1"));
204        test_state.name_stack.push(String::from("I2"));
205        name_flush(&mut test_state, &icache());
206        assert_eq!(test_state.name_stack.to_string(), "");
207    }
208    #[test]
209    fn name_rand_generates_value() {
210        let mut test_state = PushState::new();
211        name_rand(&mut test_state, &icache());
212        assert_eq!(test_state.name_stack.size(), 1);
213    }
214    #[test]
215    fn name_rand_bound_generates_value() {
216        let mut test_state = PushState::new();
217        test_state
218            .name_bindings
219            .insert(CodeGenerator::new_random_name(), Item::int(1));
220        name_rand_bound(&mut test_state, &icache());
221        assert_eq!(test_state.name_stack.size(), 1);
222    }
223
224    #[test]
225    fn name_rot_shuffles_elements() {
226        let mut test_state = PushState::new();
227        test_state.name_stack.push(String::from("Test3"));
228        test_state.name_stack.push(String::from("Test2"));
229        test_state.name_stack.push(String::from("Test1"));
230        assert_eq!(
231            test_state.name_stack.to_string(),
232            "Test1 Test2 Test3"
233        );
234        name_rot(&mut test_state, &icache());
235        assert_eq!(
236            test_state.name_stack.to_string(),
237            "Test3 Test1 Test2"
238        );
239    }
240
241    #[test]
242    fn name_shove_inserts_at_right_position() {
243        let mut test_state = PushState::new();
244        test_state.name_stack.push(String::from("Test4"));
245        test_state.name_stack.push(String::from("Test3"));
246        test_state.name_stack.push(String::from("Test2"));
247        test_state.name_stack.push(String::from("Test1"));
248        assert_eq!(
249            test_state.name_stack.to_string(),
250            "Test1 Test2 Test3 Test4"
251        );
252        test_state.int_stack.push(2);
253        name_shove(&mut test_state, &icache());
254        assert_eq!(
255            test_state.name_stack.to_string(),
256            "Test2 Test3 Test1 Test4"
257        );
258    }
259
260    #[test]
261    fn name_stack_depth_returns_size() {
262        let mut test_state = PushState::new();
263        test_state.name_stack.push(String::from("Test4"));
264        test_state.name_stack.push(String::from("Test3"));
265        test_state.name_stack.push(String::from("Test2"));
266        test_state.name_stack.push(String::from("Test1"));
267        name_stack_depth(&mut test_state, &icache());
268        assert_eq!(test_state.int_stack.to_string(), "4");
269    }
270
271    #[test]
272    fn name_swaps_top_elements() {
273        let mut test_state = PushState::new();
274        test_state.name_stack.push(String::from("Test2"));
275        test_state.name_stack.push(String::from("Test1"));
276        assert_eq!(test_state.name_stack.to_string(), "Test1 Test2");
277        name_swap(&mut test_state, &icache());
278        assert_eq!(test_state.name_stack.to_string(), "Test2 Test1");
279    }
280
281    #[test]
282    fn name_yank_brings_item_to_top() {
283        let mut test_state = PushState::new();
284        test_state.name_stack.push(String::from("Test5"));
285        test_state.name_stack.push(String::from("Test4"));
286        test_state.name_stack.push(String::from("Test3"));
287        test_state.name_stack.push(String::from("Test2"));
288        test_state.name_stack.push(String::from("Test1"));
289        assert_eq!(
290            test_state.name_stack.to_string(),
291            "Test1 Test2 Test3 Test4 Test5"
292        );
293        test_state.int_stack.push(3);
294        name_yank(&mut test_state, &icache());
295        assert_eq!(
296            test_state.name_stack.to_string(),
297            "Test4 Test1 Test2 Test3 Test5"
298        );
299    }
300
301    #[test]
302    fn name_yank_dup_copies_item_to_top() {
303        let mut test_state = PushState::new();
304        test_state.name_stack.push(String::from("Test5"));
305        test_state.name_stack.push(String::from("Test4"));
306        test_state.name_stack.push(String::from("Test3"));
307        test_state.name_stack.push(String::from("Test2"));
308        test_state.name_stack.push(String::from("Test1"));
309        assert_eq!(
310            test_state.name_stack.to_string(),
311            "Test1 Test2 Test3 Test4 Test5"
312        );
313        test_state.int_stack.push(3);
314        name_yank_dup(&mut test_state, &icache());
315        assert_eq!(
316            test_state.name_stack.to_string(),
317            "Test4 Test1 Test2 Test3 Test4 Test5"
318        );
319    }
320}