pushr/push/
code.rs

1use crate::push::instructions::Instruction;
2use crate::push::instructions::InstructionCache;
3use crate::push::item::Item;
4use crate::push::random::CodeGenerator;
5use crate::push::stack::PushStack;
6use crate::push::state::PushState;
7use crate::push::state::*;
8use std::cmp;
9use std::collections::HashMap;
10
11/// For explicit code manipulation and execution. May also be used as a general list data type.
12/// This type must always be present, as the top level interpreter will push any code to be
13/// executed on the CODE stack prior to execution. However, one may turn off all CODE instructions
14/// if code manipulation is not needed.
15pub fn load_code_instructions(map: &mut HashMap<String, Instruction>) {
16    map.insert(String::from("CODE.="), Instruction::new(code_eq));
17    map.insert(String::from("CODE.APPEND"), Instruction::new(code_append));
18    map.insert(String::from("CODE.ATOM"), Instruction::new(code_item));
19    map.insert(String::from("CODE.CAR"), Instruction::new(code_first));
20    map.insert(String::from("CODE.CDR"), Instruction::new(code_rest));
21    map.insert(String::from("CODE.CONS"), Instruction::new(code_cons));
22    map.insert(
23        String::from("CODE.CONTAINER"),
24        Instruction::new(code_container),
25    );
26    map.insert(
27        String::from("CODE.CONTAINS"),
28        Instruction::new(code_contains),
29    );
30    map.insert(String::from("CODE.DEFINE"), Instruction::new(code_define));
31    map.insert(
32        String::from("CODE.DEFINITION"),
33        Instruction::new(code_definition),
34    );
35    map.insert(
36        String::from("CODE.DISCREPANCY"),
37        Instruction::new(code_discrepancy),
38    );
39    map.insert(String::from("CODE.DO"), Instruction::new(code_do));
40    map.insert(String::from("CODE.DO*"), Instruction::new(code_pop_and_do));
41    map.insert(String::from("CODE.LOOP"), Instruction::new(code_loop));
42    map.insert(String::from("CODE.DUP"), Instruction::new(code_dup));
43    map.insert(String::from("CODE.EXTRACT"), Instruction::new(code_extract));
44    map.insert(String::from("CODE.FLUSH"), Instruction::new(code_flush));
45    map.insert(
46        String::from("CODE.FROMBOOLEAN"),
47        Instruction::new(code_from_bool),
48    );
49    map.insert(
50        String::from("CODE.FROMFLOAT"),
51        Instruction::new(code_from_float),
52    );
53    map.insert(
54        String::from("CODE.FROMINTEGER"),
55        Instruction::new(code_from_int),
56    );
57    map.insert(
58        String::from("CODE.FROMNAME"),
59        Instruction::new(code_from_name),
60    );
61    map.insert(String::from("CODE.ID"), Instruction::new(code_id));
62    map.insert(String::from("CODE.IF"), Instruction::new(code_if));
63    map.insert(String::from("CODE.INSERT"), Instruction::new(code_insert));
64    map.insert(String::from("CODE.LENGTH"), Instruction::new(code_length));
65    map.insert(String::from("CODE.LIST"), Instruction::new(code_list));
66    map.insert(String::from("CODE.MEMBER"), Instruction::new(code_member));
67    map.insert(String::from("CODE.NOOP"), Instruction::new(code_noop));
68    map.insert(String::from("CODE.NTH"), Instruction::new(code_nth));
69    map.insert(String::from("CODE.NULL"), Instruction::new(code_null));
70    map.insert(String::from("CODE.POP"), Instruction::new(code_pop));
71    map.insert(
72        String::from("CODE.POSITION"),
73        Instruction::new(code_position),
74    );
75    map.insert(String::from("CODE.PRINT"), Instruction::new(code_print));
76    map.insert(String::from("CODE.QUOTE"), Instruction::new(code_quote));
77    map.insert(String::from("CODE.RAND"), Instruction::new(code_rand));
78    map.insert(String::from("CODE.ROT"), Instruction::new(code_rot));
79    map.insert(String::from("CODE.SHOVE"), Instruction::new(code_shove));
80    map.insert(String::from("CODE.SIZE"), Instruction::new(code_size));
81    map.insert(
82        String::from("CODE.STACKDEPTH"),
83        Instruction::new(code_stack_depth),
84    );
85    map.insert(String::from("CODE.SUBST"), Instruction::new(code_subst));
86    map.insert(String::from("CODE.SWAP"), Instruction::new(code_swap));
87    map.insert(String::from("CODE.YANK"), Instruction::new(code_yank));
88    map.insert(
89        String::from("CODE.YANKDUP"),
90        Instruction::new(code_yank_dup),
91    );
92}
93
94/// CODE.ID: Pushes the ID of the CODE stack to the INTEGER stack.
95pub fn code_id(push_state: &mut PushState, _instruction_set: &InstructionCache) {
96    push_state.int_stack.push(CODE_STACK_ID);
97}
98
99/// CODE.=: Pushes TRUE if the top two pieces of CODE are equal,
100/// or FALSE otherwise.
101pub fn code_eq(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
102    if let Some(pv) = push_state.code_stack.copy_vec(2) {
103        push_state
104            .bool_stack
105            .push(pv[0].to_string() == pv[1].to_string());
106    }
107}
108
109/// CODE.APPEND: Pushes the result of appending the top two pieces of code.
110/// If one of the pieces of code is a single instruction or literal (that is,
111/// something not surrounded by parentheses) then it is surrounded by
112/// parentheses first.
113pub fn code_append(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
114    if let Some(pv) = push_state.code_stack.pop_vec(2) {
115        push_state.code_stack.push(Item::List {
116            items: PushStack::from_vec(pv),
117        });
118    }
119}
120
121/// CODE.ATOM: Pushes TRUE onto the BOOLEAN stack if the top piece of code is a single instruction
122/// or a literal, and FALSE otherwise (that is, if it is something surrounded by parentheses).
123pub fn code_item(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
124    // Equality only checks type and ignores value
125    push_state.bool_stack.push(
126        push_state.code_stack.last_eq(&Item::int(0))
127            || push_state.code_stack.last_eq(&Item::noop()),
128    );
129}
130
131/// CODE.CAR: Pushes the first item of the list on top of the CODE stack. For example, if the top
132/// piece of code is "( A B )" then this pushes "A" (after popping the argument). If the code on
133/// top of the stack is not a list then this has no effect. The name derives from the similar Lisp
134/// function; a more generic name would be "FIRST".
135pub fn code_first(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
136    if push_state.code_stack.last_eq(&Item::empty_list()) {
137        match push_state.code_stack.pop() {
138            Some(Item::List { mut items }) => {
139                if let Some(item) = items.pop() {
140                    push_state.code_stack.push(item);
141                }
142            }
143            _ => (),
144        }
145    }
146}
147
148/// CODE.CDR: Pushes a version of the list from the top of the CODE stack without its first
149/// element. For example, if the top piece of code is "( A B )" then this pushes "( B )" (after
150/// popping the argument). If the code on top of the stack is not a list then this pushes the empty
151/// list ("( )"). The name derives from the similar Lisp function; a more generic name would be
152/// "REST".
153pub fn code_rest(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
154    match push_state.code_stack.pop() {
155        Some(Item::List { mut items }) => {
156            items.pop();
157            push_state.code_stack.push(Item::List { items: items });
158        }
159        _ => (),
160    }
161}
162
163/// CODE.CONS: Pushes the result of "consing" (in the Lisp sense) the second stack item onto the
164/// first stack item (which is coerced to a list if necessary). For example, if the top piece of
165/// code is "( A B )" and the second piece of code is "X" then this pushes "( X A B )" (after
166/// popping the argument).
167pub fn code_cons(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
168    if let Some(pv) = push_state.code_stack.pop_vec(2) {
169        let mut consblock = PushStack::new();
170        for i in (0..2).rev() {
171            match &pv[i] {
172                Item::Literal { push_type: _ } => {
173                    consblock.push(pv[i].clone());
174                }
175                Item::List { items: a } => {
176                    if let Some(vec) = a.copy_vec(a.size()) {
177                        consblock.push_vec(vec)
178                    }
179                }
180                _ => (),
181            }
182        }
183        push_state.code_stack.push(Item::List { items: consblock });
184    }
185}
186
187/// CODE.CONTAINER: Pushes the "container" of the second CODE stack item within the first CODE
188/// stack item onto the CODE stack. If second item contains the first anywhere (i.e. in any nested
189/// list) then the container is the smallest sub-list that contains but is not equal to the first
190/// instance. For example, if the top piece of code is "( B ( C ( A ) ) ( D ( A ) ) )" and the
191/// second piece of code is "( A )" then this pushes ( C ( A ) ). Pushes an empty list if there is
192/// no such container.
193pub fn code_container(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
194    if let Some(code) = push_state.code_stack.copy_vec(2) {
195        match Item::container(&code[1], &code[0]) {
196            Ok(container) => push_state.code_stack.push(container),
197            Err(_) => push_state.code_stack.push(Item::empty_list()),
198        }
199    }
200}
201
202/// CODE.CONTAINS: Pushes TRUE on the BOOLEAN stack if the second CODE stack item contains the
203/// first CODE stack item anywhere (e.g. in a sub-list).
204pub fn code_contains(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
205    if let Some(ov) = push_state.code_stack.copy_vec(2) {
206        let first_el = ov[1].to_string();
207        let code_str = ov[0].to_string();
208        if first_el.contains(&code_str) {
209            push_state.bool_stack.push(true);
210        } else {
211            push_state.bool_stack.push(false);
212        }
213    }
214}
215
216/// CODE.DEFINE: Defines the name on top of the NAME stack as an instruction that will push the top
217/// item of the CODE stack onto the EXEC stack.
218pub fn code_define(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
219    if let Some(name) = push_state.name_stack.pop() {
220        if let Some(instruction) = push_state.code_stack.pop() {
221            push_state.name_bindings.insert(name, instruction);
222        }
223    }
224}
225
226/// CODE.DEFINITION: Pushes the definition associated with the top NAME on the NAME stack (if any)
227/// onto the CODE stack. This extracts the definition for inspection/manipulation, rather than for
228/// immediate execution (although it may then be executed with a call to CODE.DO or a similar
229/// instruction).
230pub fn code_definition(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
231    if let Some(name) = push_state.name_stack.pop() {
232        if let Some(instruction) = push_state.name_bindings.get(&*name) {
233            push_state.code_stack.push(instruction.clone());
234        }
235    }
236}
237/// CODE.DISCREPANCY: Pushes a measure of the discrepancy between the top two CODE stack items onto
238/// the INTEGER stack. This will be zero if the top two items are equivalent, and will be higher
239/// the 'more different' the items are from one another. The calculation is as follows:
240/// 1. Construct a list of all of the unique items in both of the lists (where uniqueness is
241///    determined by equalp). Sub-lists and items all count as items.
242/// 2. Initialize the result to zero.
243/// 3. For each unique item increment the result by the difference between the number of
244///    occurrences of the item in the two pieces of code.
245/// 4. Push the result.
246pub fn code_discrepancy(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
247    let mut discrepancy = 0;
248    if let Some(ov) = push_state.code_stack.copy_vec(2) {
249        match &ov[0] {
250            Item::List { items: fstlist } => {
251                match &ov[1] {
252                    Item::List { items: scdlist } => {
253                        // Compare lists
254                        if let Some(fstvec) = fstlist.copy_vec(fstlist.size()) {
255                            for (i, x) in fstvec.iter().rev().enumerate() {
256                                if let Some(val) = scdlist.equal_at(i, x) {
257                                    if !val {
258                                        discrepancy += 1;
259                                    }
260                                }
261                            }
262                        }
263                        discrepancy =
264                            discrepancy + (fstlist.size() as i32 - scdlist.size() as i32).abs();
265                    }
266                    _ => {
267                        discrepancy = if ov[0].to_string() != ov[1].to_string() {
268                            1
269                        } else {
270                            0
271                        }
272                    }
273                }
274            }
275            _ => {
276                discrepancy = if ov[0].to_string() != ov[1].to_string() {
277                    1
278                } else {
279                    0
280                }
281            }
282        }
283        push_state.int_stack.push(discrepancy);
284    }
285}
286
287/// CODE.DO: Recursively invokes the interpreter on the program on top of the CODE stack. After
288/// evaluation the CODE stack is popped; normally this pops the program that was just executed, but
289/// if the expression itself manipulates the stack then this final pop may end up popping something
290/// else.
291pub fn code_do(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
292    if let Some(instruction) = push_state.code_stack.copy(0) {
293        push_state.exec_stack.push(Item::InstructionMeta {
294            name: "CODE.POP".to_string(),
295        });
296        push_state.exec_stack.push(instruction);
297    }
298}
299
300/// CODE.DO*: Like CODE.DO but pops the stack before, rather than after, the recursive execution.
301pub fn code_pop_and_do(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
302    if let Some(instruction) = push_state.code_stack.copy(0) {
303        push_state.exec_stack.push(instruction);
304        push_state.exec_stack.push(Item::InstructionMeta {
305            name: "CODE.POP".to_string(),
306        });
307    }
308}
309
310/// CODE.LOOP: An iteration instruction that executes the top item on the EXEC stack a number
311/// of times that depends on the top two INDEX items, while also pushing the loop counter onto the
312/// INDEX stack for possible access during the execution of the body of the loop.
313/// First the code and the index arguments are saved locally and popped. Then the current and the
314/// destination field of the index are compared. If they are equal nothing happens, i.e. the
315/// index pair is just removed and the loop is terminated.
316/// If the integers are not equal then the current index will be
317/// pushed onto the INDEX stack but two items will be pushed onto the EXEC stack -- first a
318/// recursive call to EXEC.LOOP (with the same code and destination index, but with a current
319/// index that has been incremented by 1 to be closer to the destination
320/// index) and then the body code.
321pub fn code_loop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
322    if let Some(body) = push_state.code_stack.pop() {
323        if let Some(index) = push_state.index_stack.copy(0) {
324            if index.current < index.destination {
325                let updated_loop = Item::list(vec![
326                    body.clone(),
327                    Item::instruction("CODE.LOOP".to_string()),
328                    Item::instruction("INDEX.INCREASE".to_string()),
329                ]);
330                push_state.exec_stack.push(updated_loop);
331                push_state.exec_stack.push(body);
332            } else {
333                push_state.index_stack.pop();
334            }
335        }
336    }
337}
338
339/// CODE.DUP: Duplicates the top item on the CODE stack. Does not pop its argument (which, if it
340/// did, would negate the effect of the duplication!).
341pub fn code_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
342    if let Some(instruction) = push_state.code_stack.copy(0) {
343        push_state.code_stack.push(instruction);
344    }
345}
346
347/// CODE.EXTRACT: Pushes the sub-expression of the top item of the CODE stack that is indexed by
348/// the top item of the INTEGER stack. The indexing here counts "points," where each parenthesized
349/// expression and each literal/instruction is considered a point, and it proceeds in depth first
350/// order. The entire piece of code is at index 0; if it is a list then the first item in the list
351/// is at index 1, etc. The integer used as the index is taken modulo the number of points in the
352/// overall expression (and its absolute value is taken in case it is negative) to ensure that it
353/// is within the meaningful range.
354pub fn code_extract(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
355    if let Some(sub_idx) = push_state.int_stack.pop() {
356        if let Some(code) = push_state.code_stack.get(0) {
357            let total_size = Item::size(code);
358            let norm_idx = sub_idx.rem_euclid(total_size as i32);
359            match Item::traverse(&code, norm_idx as usize) {
360                Ok(el) => push_state.code_stack.push(el),
361                Err(_) => (),
362            };
363        }
364    }
365}
366
367/// CODE.FLUSH: Empties the CODE stack.
368pub fn code_flush(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
369    push_state.code_stack.flush();
370}
371
372/// CODE.FROMBOOLEAN: Pops the BOOLEAN stack and pushes the popped item (TRUE or FALSE) onto the
373/// CODE stack.
374pub fn code_from_bool(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
375    if let Some(bval) = push_state.bool_stack.pop() {
376        push_state.code_stack.push(Item::bool(bval));
377    }
378}
379/// CODE.FROMFLOAT: Pops the FLOAT stack and pushes the popped item onto the CODE stack.
380pub fn code_from_float(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
381    if let Some(fval) = push_state.float_stack.pop() {
382        push_state.code_stack.push(Item::float(fval));
383    }
384}
385
386/// CODE.FROMINTEGER: Pops the INTEGER stack and pushes the popped integer onto the CODE stack.
387pub fn code_from_int(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
388    if let Some(ival) = push_state.int_stack.pop() {
389        push_state.code_stack.push(Item::int(ival));
390    }
391}
392
393/// CODE.FROMNAME: Pops the NAME stack and pushes the popped item onto the CODE stack.
394pub fn code_from_name(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
395    if let Some(nval) = push_state.name_stack.pop() {
396        push_state.code_stack.push(Item::id(nval.to_string()));
397    }
398}
399
400/// CODE.IF: If the top item of the BOOLEAN stack is TRUE this recursively executes the second item
401/// of the CODE stack; otherwise it recursively executes the first item of the CODE stack. Either
402/// way both elements of the CODE stack (and the BOOLEAN value upon which the decision was made)
403/// are popped.
404pub fn code_if(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
405    if let Some(code) = push_state.code_stack.pop_vec(2) {
406        if let Some(exec_second) = push_state.bool_stack.pop() {
407            if exec_second {
408                // Push second element for execution
409                push_state.exec_stack.push(code[0].clone());
410            } else {
411                // Push first element for execution
412                push_state.exec_stack.push(code[1].clone());
413            }
414        }
415    }
416}
417
418/// CODE.INSERT: Pushes the result of inserting the second item of the CODE stack into the first
419/// item, at the position indexed by the top item of the INTEGER stack (and replacing whatever was
420/// there formerly). The indexing is computed as in CODE.EXTRACT.
421pub fn code_insert(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
422    if let Some(sub_idx) = push_state.int_stack.pop() {
423        if let Some(code_to_be_inserted) = push_state.code_stack.copy(1) {
424            let _ = Item::insert(
425                push_state.code_stack.get_mut(0).unwrap(),
426                &code_to_be_inserted,
427                sub_idx as usize,
428            );
429        }
430    }
431}
432
433/// CODE.LENGTH: Pushes the length of the top item on the CODE stack onto the INTEGER stack. If the
434/// top item is not a list then this pushes a 1. If the top item is a list then this pushes the
435/// number of items in the top level of the list; that is, nested lists contribute only 1 to this
436/// count, no matter what they contain.
437pub fn code_length(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
438    if let Some(top_item) = push_state.code_stack.get(0) {
439        match top_item {
440            Item::List { items } => push_state.int_stack.push(items.size() as i32),
441            _ => push_state.int_stack.push(1),
442        }
443    }
444}
445
446/// CODE.LIST: Pushes a list of the top two items of the CODE stack onto the CODE stack.
447pub fn code_list(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
448    if let Some(top_items) = push_state.code_stack.copy_vec(2) {
449        push_state
450            .code_stack
451            .push(Item::list(vec![top_items[0].clone(), top_items[1].clone()]));
452    }
453}
454
455/// CODE.CONTAINS: Pushes TRUE on the BOOLEAN stack if the second CODE stack item contains the
456/// first CODE stack item anywhere (e.g. in a sub-list).
457pub fn code_member(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
458    if let Some(ov) = push_state.code_stack.copy_vec(2) {
459        let top_el = ov[1].to_string();
460        let sec_el = ov[0].to_string();
461        if sec_el.contains(&top_el) {
462            push_state.bool_stack.push(true);
463        } else {
464            push_state.bool_stack.push(false);
465        }
466    }
467}
468
469/// CODE.NOOP: Does nothing.
470pub fn code_noop(_push_state: &mut PushState, _instruction_cache: &InstructionCache) {}
471
472/// CODE.NTH: Pushes the nth element of the expression on top of the CODE stack (which is coerced
473/// to a list first if necessary). If the expression is an empty list then the result is an empty
474/// list. N is taken from the INTEGER stack and is taken modulo the length of the expression into
475/// which it is indexing.
476pub fn code_nth(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
477    if let Some(sub_idx) = push_state.int_stack.pop() {
478        if let Some(code) = push_state.code_stack.get(0) {
479            let total_size = Item::shallow_size(code);
480            let idx = sub_idx.rem_euclid(total_size as i32);
481            let mut item_to_push = Item::empty_list();
482            if idx == 0 {
483                item_to_push = code.clone();
484            }
485            match code {
486                Item::List { items } => {
487                    if let Some(nth_item) = items.get(idx as usize - 1) {
488                        item_to_push = nth_item.clone();
489                    }
490                }
491                _ => (),
492            }
493            push_state.code_stack.push(item_to_push);
494        }
495    }
496}
497
498/// CODE.NULL: Pushes TRUE onto the BOOLEAN stack if the top item of the CODE stack is an empty
499/// list, or FALSE otherwise.
500pub fn code_null(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
501    if let Some(code) = push_state.code_stack.get(0) {
502        let mut is_null = false;
503        match code {
504            Item::List { items } => {
505                if items.size() == 0 {
506                    is_null = true;
507                }
508            }
509            _ => (),
510        }
511        push_state.bool_stack.push(is_null);
512    }
513}
514
515/// CODE.POP: Pops the CODE stack.
516pub fn code_pop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
517    push_state.code_stack.pop();
518}
519
520/// CODE.POSITION: Pushes onto the INTEGER stack the position of the second item on the CODE stack
521/// within the first item (which is coerced to a list if necessary). Pushes -1 if no match is found.
522pub fn code_position(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
523    if let Some(code) = push_state.code_stack.copy_vec(2) {
524        match Item::contains(&code[1], &code[0], 0) {
525            Ok(pos) => push_state.int_stack.push(pos as i32),
526            Err(()) => push_state.int_stack.push(-1),
527        }
528    }
529}
530
531/// CODE.PRINT: Pushes the string representation of the code stack to the name stack. 
532pub fn code_print(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
533    if push_state.code_stack.size() > 0 {
534        let code_str = push_state.code_stack.to_string();
535        push_state.name_stack.push(code_str);
536
537    }
538}
539
540/// CODE.QUOTE: Specifies that the next expression submitted for execution will instead be pushed
541/// literally onto the CODE stack. This can be implemented by moving the top item on the EXEC stack
542/// onto the CODE stack.
543pub fn code_quote(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
544    if let Some(exec_code) = push_state.exec_stack.pop() {
545        push_state.code_stack.push(exec_code);
546    }
547}
548
549/// CODE.RAND: Pushes a newly-generated random program onto the CODE stack. The limit for the size
550/// of the expression is taken from the INTEGER stack; to ensure that it is in the appropriate
551/// range this is taken modulo the value of the MAX-POINTS-IN-RANDOM-EXPRESSIONS parameter and the
552/// absolute value of the result is used.
553pub fn code_rand(push_state: &mut PushState, instruction_cache: &InstructionCache) {
554    if let Some(size_limit) = push_state.int_stack.pop() {
555        let limit = cmp::min(
556            i32::abs(size_limit),
557            i32::abs(push_state.configuration.max_points_in_random_expressions),
558        );
559        if let Some(rand_item) =
560            CodeGenerator::random_code(&push_state, &instruction_cache, limit as usize)
561        {
562            push_state.code_stack.push(rand_item);
563        }
564    }
565}
566
567/// CODE.ROT: Rotates the top three items on the CODE stack, pulling the third item out and pushing
568/// it on top. This is equivalent to "2 CODE.YANK".
569pub fn code_rot(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
570    push_state.code_stack.yank(2);
571}
572
573/// CODE.SHOVE: Inserts the top piece of CODE "deep" in the stack, at the position indexed by the
574/// top INTEGER.
575pub fn code_shove(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
576    if let Some(shove_index) = push_state.int_stack.pop() {
577        let corr_index = i32::max(
578            i32::min((push_state.code_stack.size() as i32) - 1, shove_index),
579            0,
580        ) as usize;
581        push_state.code_stack.shove(corr_index as usize);
582    }
583}
584
585/// CODE.SIZE: Pushes the number of "points" in the top piece of CODE onto the INTEGER stack. Each
586/// instruction, literal, and pair of parentheses counts as a point.
587pub fn code_size(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
588    if let Some(code) = push_state.code_stack.get(0) {
589        push_state.int_stack.push(Item::size(&code) as i32);
590    }
591}
592
593/// CODE.STACKDEPTH: Pushes the stack depth onto the INTEGER stack.
594pub fn code_stack_depth(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
595    push_state
596        .int_stack
597        .push(push_state.code_stack.size() as i32);
598}
599
600/// CODE.SUBST: Pushes the result of substituting the third item on the code stack for the second
601/// item in the first item. As of this writing this is implemented only in the Lisp implementation,
602/// within which it relies on the Lisp "subst" function. As such, there are several problematic
603/// possibilities; for example "dotted-lists" can result in certain cases with empty-list
604/// arguments. If any of these problematic possibilities occurs the stack is left unchanged.
605pub fn code_subst(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
606    if let Some(code) = push_state.code_stack.pop_vec(3) {
607        // code[2]: first item => item to be modified (target)
608        // code[1]: second item => substitute
609        // code[0]: third item => replace pattern
610        let mut target = code[2].clone();
611        if Item::substitute(&mut target, &code[0], &code[1]) {
612            // Target and pattern are the same => push substitute
613            push_state.code_stack.push(code[1].clone());
614        } else {
615            // Push target with substitute
616            push_state.code_stack.push(target);
617        }
618    }
619}
620
621/// CODE.SWAP: Swaps the top two pieces of CODE.
622pub fn code_swap(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
623    push_state.code_stack.shove(1);
624}
625
626/// CODE.YANK: Removes an indexed item from "deep" in the stack and pushes it on top of the stack.
627/// The index is taken from the INTEGER stack.
628pub fn code_yank(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
629    if let Some(index) = push_state.int_stack.pop() {
630        let corr_index = i32::max(
631            i32::min((push_state.code_stack.size() as i32) - 1, index),
632            0,
633        ) as usize;
634        push_state.code_stack.yank(corr_index as usize);
635    }
636}
637
638/// CODE.YANKDUP: Pushes a copy of an indexed item "deep" in the stack onto the top of the stack,
639/// without removing the deep item. The index is taken from the INTEGER stack.
640pub fn code_yank_dup(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
641    if let Some(index) = push_state.int_stack.pop() {
642        let corr_index = i32::max(
643            i32::min((push_state.code_stack.size() as i32) - 1, index),
644            0,
645        ) as usize;
646        if let Some(deep_item) = push_state.code_stack.copy(corr_index as usize) {
647            push_state.code_stack.push(deep_item);
648        }
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use super::*;
655    use crate::push::index::Index;
656    use crate::push::parser::PushParser;
657    use crate::push::instructions::InstructionSet;
658
659    pub fn icache() -> InstructionCache {
660        InstructionCache::new(vec![])
661    }
662
663    #[test]
664    fn code_eq_pushes_true_when_elements_equal() {
665        let mut test_state = PushState::new();
666        test_state.code_stack.push(Item::int(1));
667        test_state.code_stack.push(Item::int(1));
668        code_eq(&mut test_state, &icache());
669        assert_eq!(test_state.code_stack.size(), 2);
670        if let Some(val) = test_state.bool_stack.pop() {
671            assert_eq!(val, true, "Must be true in case of equality");
672        } else {
673            assert!(false, "Expected bool value");
674        }
675    }
676
677    #[test]
678    fn code_eq_pushes_false_when_elements_unequal() {
679        let mut test_state = PushState::new();
680        test_state.code_stack.push(Item::int(1));
681        test_state.code_stack.push(Item::int(2));
682        code_eq(&mut test_state, &icache());
683        assert_eq!(test_state.code_stack.size(), 2);
684        if let Some(val) = test_state.bool_stack.pop() {
685            assert_eq!(val, false, "Must be false in case of inequality");
686        } else {
687            assert!(false, "Expected bool value");
688        }
689    }
690
691    #[test]
692    fn code_append_pushes_block_when_finding_literals() {
693        let mut test_state = PushState::new();
694        test_state.code_stack.push(Item::int(1));
695        test_state.code_stack.push(Item::int(2));
696        code_append(&mut test_state, &icache());
697        assert_eq!(test_state.code_stack.size(), 1, "Excpected single element");
698        assert!(
699            test_state.code_stack.last_eq(&Item::empty_list()),
700            "Expected Code Block"
701        );
702    }
703
704    #[test]
705    fn code_item_pushes_true_when_no_list_found() {
706        let mut test_state = PushState::new();
707        test_state.code_stack.push(Item::int(0));
708        code_item(&mut test_state, &icache());
709        assert!(
710            test_state.bool_stack.last_eq(&true),
711            "Should push true for Literal"
712        );
713        test_state = PushState::new();
714        test_state.code_stack.push(Item::noop());
715        code_item(&mut test_state, &icache());
716        assert!(
717            test_state.bool_stack.last_eq(&true),
718            "Should push true for Instruction"
719        );
720        test_state = PushState::new();
721        test_state.code_stack.push(Item::empty_list());
722        code_item(&mut test_state, &icache());
723        assert!(
724            test_state.bool_stack.last_eq(&false),
725            "Should push false for Code Block"
726        );
727    }
728
729    #[test]
730    fn code_first_pushes_first_element_when_cb_is_found() {
731        let mut test_state = PushState::new();
732        test_state
733            .code_stack
734            .push(Item::list(vec![Item::int(1), Item::int(2), Item::int(3)]));
735        code_first(&mut test_state, &icache());
736        assert_eq!(test_state.code_stack.to_string(), "3");
737    }
738
739    #[test]
740    fn code_rest_pushes_all_except_first_element() {
741        let mut test_state = PushState::new();
742        test_state
743            .code_stack
744            .push(Item::list(vec![Item::int(1), Item::int(2), Item::int(3)]));
745        assert_eq!(
746            test_state.code_stack.to_string(),
747            "( 3 2 1 )"
748        );
749        code_rest(&mut test_state, &icache());
750        assert_eq!(
751            test_state.code_stack.to_string(),
752            "( 2 1 )"
753        );
754    }
755
756    #[test]
757    fn code_cons_appends_in_reverse_order() {
758        let mut test_state = PushState::new();
759        test_state.code_stack.push(Item::int(1));
760        test_state.code_stack.push(Item::int(2));
761        assert_eq!(
762            test_state.code_stack.to_string(),
763            "2 1"
764        );
765        code_cons(&mut test_state, &icache());
766        assert_eq!(
767            test_state.code_stack.to_string(),
768            "( 1 2 )"
769        );
770    }
771
772    #[test]
773    fn code_container_finds_subelement() {
774        let mut test_state = PushState::new();
775        // Test element is (1 2)'
776        test_state
777            .code_stack
778            .push(Item::list(vec![Item::int(1), Item::int(2)]));
779        test_state.code_stack.push(Item::list(vec![
780            Item::list(vec![
781                Item::int(3),
782                Item::list(vec![Item::int(1), Item::int(2)]),
783                Item::list(vec![Item::int(3), Item::int(3)]),
784                Item::int(3),
785            ]),
786            Item::int(4),
787            Item::int(5),
788        ]));
789        code_container(&mut test_state, &icache());
790        // The top element is expected to be the smallest container of (1 2)' => (3 (1 2)' (3 3)' 3)'
791        assert!(test_state
792            .code_stack
793            .to_string()
794            .starts_with("( 3 ( 3 3 ) ( 2 1 ) 3"));
795    }
796
797    #[test]
798    fn code_contains_pushes_true_if_second_contains_first() {
799        let mut test_state = PushState::new();
800        // Test element is (1 2)'
801        test_state
802            .code_stack
803            .push(Item::list(vec![Item::int(1), Item::int(2)]));
804        test_state.code_stack.push(Item::list(vec![
805            Item::list(vec![
806                Item::int(3),
807                Item::list(vec![Item::int(1), Item::int(2)]),
808                Item::list(vec![Item::int(3), Item::int(3)]),
809                Item::int(3),
810            ]),
811            Item::int(4),
812            Item::int(5),
813        ]));
814        code_contains(&mut test_state, &icache());
815        assert_eq!(test_state.bool_stack.to_string(), "TRUE");
816    }
817
818    #[test]
819    fn code_define_creates_name_binding() {
820        let mut test_state = PushState::new();
821        test_state.code_stack.push(Item::int(2));
822        test_state.name_stack.push(String::from("TEST"));
823        code_define(&mut test_state, &icache());
824        assert_eq!(
825            *test_state.name_bindings.get("TEST").unwrap().to_string(),
826            Item::int(2).to_string()
827        );
828    }
829
830    #[test]
831    fn code_definition_pushes_to_code_stack() {
832        let mut test_state = PushState::new();
833        test_state
834            .name_bindings
835            .insert(String::from("TEST"), Item::int(2));
836        test_state.name_stack.push(String::from("TEST"));
837        code_definition(&mut test_state, &icache());
838        assert_eq!(
839            test_state.code_stack.pop().unwrap().to_string(),
840            Item::int(2).to_string()
841        );
842    }
843
844    #[test]
845    fn code_discrepancy_calculates_zero_discrepancy_correctly() {
846        let mut test_state = PushState::new();
847        // Test element is (1 2)'
848        test_state
849            .code_stack
850            .push(Item::list(vec![Item::int(1), Item::int(2)]));
851        test_state
852            .code_stack
853            .push(Item::list(vec![Item::int(1), Item::int(2)]));
854        code_discrepancy(&mut test_state, &icache());
855        assert_eq!(test_state.int_stack.to_string(), "0");
856    }
857
858    #[test]
859    fn code_discrepancy_calculates_discrepancy_correctly() {
860        let mut test_state = PushState::new();
861        // Test element is (1 2)'
862        test_state
863            .code_stack
864            .push(Item::list(vec![Item::int(0), Item::int(2)]));
865        test_state
866            .code_stack
867            .push(Item::list(vec![Item::int(1), Item::int(2)]));
868        code_discrepancy(&mut test_state, &icache());
869        assert_eq!(test_state.int_stack.to_string(), "1");
870    }
871
872    #[test]
873    fn code_do_adds_instruction_to_excecution_stack() {
874        let mut test_state = PushState::new();
875        test_state.code_stack.push(Item::int(9));
876        code_do(&mut test_state, &icache());
877        assert_eq!(
878            test_state.exec_stack.to_string(),
879            "9 CODE.POP"
880        );
881    }
882
883    #[test]
884    fn code_pop_and_do_adds_instruction_to_excecution_stack() {
885        let mut test_state = PushState::new();
886        test_state.code_stack.push(Item::int(9));
887        code_pop_and_do(&mut test_state, &icache());
888        assert_eq!(
889            test_state.exec_stack.to_string(),
890            "CODE.POP 9"
891        );
892    }
893
894    #[test]
895    fn code_loop_pushes_body_and_updated_loop() {
896        let mut test_state = PushState::new();
897        test_state.code_stack.push(Item::noop());
898        test_state.index_stack.push(Index::new(3));
899        code_loop(&mut test_state, &icache());
900        assert_eq!(test_state.exec_stack.to_string(), "NOOP ( INDEX.INCREASE CODE.LOOP NOOP )");
901    }
902
903    #[test]
904    fn code_loop_removes_index_when_terminated() {
905        let mut test_state = PushState::new();
906        test_state.code_stack.push(Item::noop());
907        let mut test_index = Index::new(3);
908        test_index.current = 3;
909        test_state.index_stack.push(test_index);
910        code_loop(&mut test_state, &icache());
911        assert_eq!(test_state.index_stack.to_string(), "");
912        assert_eq!(test_state.exec_stack.to_string(), "");
913    }
914
915    #[test]
916    fn code_dup_duplicates_top_element() {
917        let mut test_state = PushState::new();
918        test_state.code_stack.push(Item::noop());
919        code_dup(&mut test_state, &icache());
920        assert_eq!(
921            test_state.code_stack.to_string(),
922            "NOOP NOOP"
923        );
924    }
925
926    #[test]
927    fn code_flush_empties_stack() {
928        let mut test_state = PushState::new();
929        // Test element is (1 2)'
930        test_state
931            .code_stack
932            .push(Item::list(vec![Item::int(0), Item::int(2)]));
933        test_state
934            .code_stack
935            .push(Item::list(vec![Item::int(1), Item::int(2)]));
936        code_flush(&mut test_state, &icache());
937        assert_eq!(test_state.code_stack.to_string(), "");
938    }
939
940    #[test]
941    fn code_from_bool_pushes_literal() {
942        let mut test_state = PushState::new();
943        test_state.bool_stack.push(true);
944        code_from_bool(&mut test_state, &icache());
945        assert_eq!(test_state.code_stack.to_string(), "TRUE");
946    }
947
948    #[test]
949    fn code_if_pushes_second_item_when_true() {
950        let mut test_state = PushState::new();
951        test_state.bool_stack.push(true);
952        test_state.code_stack.push(Item::int(2));
953        test_state.code_stack.push(Item::int(1));
954        code_if(&mut test_state, &icache());
955        assert_eq!(test_state.exec_stack.to_string(), "2");
956        assert_eq!(test_state.code_stack.to_string(), "");
957        assert_eq!(test_state.bool_stack.to_string(), "");
958    }
959
960    #[test]
961    fn code_if_pushes_first_item_when_false() {
962        let mut test_state = PushState::new();
963        test_state.bool_stack.push(false);
964        test_state.code_stack.push(Item::int(2));
965        test_state.code_stack.push(Item::int(1));
966        code_if(&mut test_state, &icache());
967        assert_eq!(test_state.exec_stack.to_string(), "1");
968        assert_eq!(test_state.code_stack.to_string(), "");
969        assert_eq!(test_state.bool_stack.to_string(), "");
970    }
971
972    #[test]
973    fn code_extract_finds_correct_subelement() {
974        let mut test_state = PushState::new();
975        let test_item = Item::list(vec![
976            Item::int(4),
977            Item::list(vec![Item::int(3)]),
978            Item::int(2),
979            Item::int(1),
980        ]);
981        // Total Size = 6 => 10 % 6 = 4
982        // Expected 4th element - Literal(3) - to be extracted
983        test_state.int_stack.push(10);
984        test_state.code_stack.push(test_item);
985        code_extract(&mut test_state, &icache());
986        assert_eq!(test_state.int_stack.to_string(), "");
987        assert_eq!(
988            test_state.code_stack.to_string(),
989            "3 ( 1 2 ( 3 ) 4 )"
990        );
991    }
992
993    #[test]
994    fn code_insert_replaces_element() {
995        let mut test_state = PushState::new();
996        test_state.int_stack.push(4);
997        let test_container = Item::list(vec![
998            Item::int(4),
999            Item::list(vec![Item::int(3)]),
1000            Item::int(2),
1001            Item::int(1),
1002        ]);
1003        let test_item = Item::int(5);
1004        test_state.code_stack.push(test_item);
1005        test_state.code_stack.push(test_container);
1006        code_insert(&mut test_state, &icache());
1007        assert_eq!(test_state.int_stack.to_string(), "");
1008        assert_eq!(
1009            test_state.code_stack.to_string(),
1010            "( 1 2 ( 5 ) 4 ) 5"
1011        );
1012    }
1013
1014    #[test]
1015    fn code_insert_does_nothing_when_index_too_big() {
1016        let mut test_state = PushState::new();
1017        test_state.int_stack.push(4);
1018        let test_container = Item::list(vec![Item::int(2), Item::int(1)]);
1019        let test_item = Item::int(5);
1020        test_state.code_stack.push(test_item);
1021        test_state.code_stack.push(test_container);
1022        code_insert(&mut test_state, &icache());
1023        assert_eq!(test_state.int_stack.to_string(), "");
1024        assert_eq!(
1025            test_state.code_stack.to_string(),
1026            "( 1 2 ) 5"
1027        );
1028    }
1029
1030    #[test]
1031    fn code_length_pushes_top_list_size() {
1032        let mut test_state = PushState::new();
1033        test_state.code_stack.push(Item::list(vec![
1034            Item::int(2),
1035            Item::int(1),
1036            Item::list(vec![Item::int(0), Item::float(2.3)]),
1037        ]));
1038        code_length(&mut test_state, &icache());
1039        assert_eq!(test_state.int_stack.to_string(), "3");
1040    }
1041
1042    #[test]
1043    fn code_list_pushes_lists_including_top_items() {
1044        let mut test_state = PushState::new();
1045        test_state
1046            .code_stack
1047            .push(Item::list(vec![Item::int(0), Item::float(2.3)]));
1048        test_state.code_stack.push(Item::int(2));
1049        code_list(&mut test_state, &icache());
1050        assert_eq!(test_state.code_stack.to_string(), "( 2 ( 2.300 0 ) ) 2 ( 2.300 0 )");
1051    }
1052
1053    #[test]
1054    fn code_nth_ignores_nested_lists() {
1055        let mut test_state = PushState::new();
1056        let test_item = Item::list(vec![
1057            Item::int(4),
1058            Item::list(vec![Item::int(3)]),
1059            Item::int(2),
1060            Item::int(1),
1061        ]);
1062        // Shallow Size = 5 => 9 % 5 = 4
1063        // Expected 4th element - Literal(4) - to be extracted
1064        test_state.int_stack.push(9);
1065        test_state.code_stack.push(test_item);
1066        code_nth(&mut test_state, &icache());
1067        assert_eq!(test_state.int_stack.to_string(), "");
1068        assert_eq!(
1069        test_state.code_stack.to_string(),
1070        "4 ( 1 2 ( 3 ) 4 )"
1071    );
1072    }
1073
1074    #[test]
1075    fn code_null_pushes_true_for_empty_list() {
1076        let mut test_state = PushState::new();
1077        test_state.code_stack.push(Item::empty_list());
1078        code_null(&mut test_state, &icache());
1079        assert_eq!(*test_state.bool_stack.get(0).unwrap(), true);
1080    }
1081
1082    #[test]
1083    fn code_pop_removes_top_element() {
1084        let mut test_state = PushState::new();
1085        test_state.code_stack.push(Item::int(1));
1086        test_state.code_stack.push(Item::int(2));
1087        test_state.code_stack.push(Item::int(3));
1088        code_pop(&mut test_state, &icache());
1089        assert_eq!(
1090            test_state.code_stack.to_string(),
1091            "2 1"
1092        );
1093    }
1094
1095    #[test]
1096    fn code_position_pushes_value_when_contained() {
1097        let mut test_state = PushState::new();
1098        test_state.code_stack.push(Item::int(3));
1099        test_state.code_stack.push(Item::list(vec![
1100            Item::int(4),
1101            Item::list(vec![Item::int(3)]),
1102            Item::int(2),
1103            Item::int(1),
1104        ]));
1105        code_position(&mut test_state, &icache());
1106        assert_eq!(test_state.int_stack.get(0).unwrap(), &4);
1107    }
1108
1109    #[test]
1110    fn code_print_creates_parseable_output() {
1111        let mut test_state = PushState::new();
1112        let mut instruction_set = InstructionSet::new();
1113        instruction_set.load();
1114        test_state.code_stack.push(Item::int(3));
1115        test_state.code_stack.push(Item::list(vec![
1116            Item::int(4),
1117            Item::list(vec![Item::int(3)]),
1118            Item::int(2),
1119            Item::int(1),
1120        ]));
1121        code_print(&mut test_state, &icache());
1122        assert_eq!(test_state.name_stack.size(), 1);
1123        let printed_code = test_state.name_stack.copy(0).unwrap();
1124        PushParser::parse_program(&mut test_state, &instruction_set, &printed_code);
1125        assert_eq!(
1126            test_state.exec_stack.to_string(), test_state.code_stack.to_string());
1127    }
1128
1129    #[test]
1130    fn code_quote_moves_item_from_exec_to_code_stack() {
1131        let mut test_state = PushState::new();
1132        test_state.exec_stack.push(Item::int(2));
1133        code_quote(&mut test_state, &icache());
1134        assert_eq!(test_state.code_stack.to_string(), "2")
1135    }
1136
1137    #[test]
1138    fn code_rand_pushes_random_code() {
1139        let mut test_state = PushState::new();
1140        test_state.int_stack.push(100);
1141        code_rand(&mut test_state, &icache());
1142        assert_eq!(test_state.code_stack.size(), 1);
1143    }
1144
1145    #[test]
1146    fn code_rot_shuffles_elements() {
1147        let mut test_state = PushState::new();
1148        test_state.code_stack.push(Item::int(3));
1149        test_state.code_stack.push(Item::int(2));
1150        test_state.code_stack.push(Item::int(1));
1151        assert_eq!(
1152            test_state.code_stack.to_string(),
1153            "1 2 3"
1154        );
1155        code_rot(&mut test_state, &icache());
1156        assert_eq!(
1157            test_state.code_stack.to_string(),
1158            "3 1 2"
1159        );
1160    }
1161
1162    #[test]
1163    fn code_shove_inserts_at_right_position() {
1164        let mut test_state = PushState::new();
1165        test_state.code_stack.push(Item::int(4));
1166        test_state.code_stack.push(Item::int(3));
1167        test_state.code_stack.push(Item::int(2));
1168        test_state.code_stack.push(Item::int(1));
1169        assert_eq!(
1170            test_state.code_stack.to_string(),
1171            "1 2 3 4"
1172        );
1173        test_state.int_stack.push(2);
1174        code_shove(&mut test_state, &icache());
1175        assert_eq!(
1176            test_state.code_stack.to_string(),
1177            "2 3 1 4"
1178        );
1179    }
1180
1181    #[test]
1182    fn code_size_calculates_top_element() {
1183        let mut test_state = PushState::new();
1184        let test_item = Item::list(vec![
1185            Item::int(4),
1186            Item::list(vec![Item::int(3)]),
1187            Item::int(2),
1188            Item::int(1),
1189        ]);
1190        test_state.code_stack.push(test_item);
1191        code_size(&mut test_state, &icache());
1192        assert_eq!(test_state.int_stack.to_string(), "6");
1193    }
1194
1195    #[test]
1196    fn code_substitute_code_elements() {
1197        let mut test_state = PushState::new();
1198        let target_item = Item::list(vec![
1199            Item::list(vec![]),
1200            Item::list(vec![Item::int(3)]),
1201            Item::int(2),
1202            Item::int(1),
1203        ]);
1204        let substitute = Item::int(4);
1205        let pattern = Item::list(vec![]);
1206        test_state.code_stack.push(pattern);
1207        test_state.code_stack.push(substitute);
1208        test_state.code_stack.push(target_item);
1209        code_subst(&mut test_state, &icache());
1210        assert_eq!(
1211            test_state.code_stack.to_string(),
1212            "( 1 2 ( 3 ) 4 )"
1213        );
1214    }
1215
1216    #[test]
1217    fn code_swaps_top_elements() {
1218        let mut test_state = PushState::new();
1219        test_state.code_stack.push(Item::int(0));
1220        test_state.code_stack.push(Item::int(1));
1221        assert_eq!(
1222            test_state.code_stack.to_string(),
1223            "1 0"
1224        );
1225        code_swap(&mut test_state, &icache());
1226        assert_eq!(
1227            test_state.code_stack.to_string(),
1228            "0 1"
1229        );
1230    }
1231
1232    #[test]
1233    fn code_yank_brings_item_to_top() {
1234        let mut test_state = PushState::new();
1235        test_state.code_stack.push(Item::int(5));
1236        test_state.code_stack.push(Item::int(4));
1237        test_state.code_stack.push(Item::int(3));
1238        test_state.code_stack.push(Item::int(2));
1239        test_state.code_stack.push(Item::int(1));
1240        assert_eq!(
1241            test_state.code_stack.to_string(),
1242            "1 2 3 4 5"
1243        );
1244        test_state.int_stack.push(3);
1245        code_yank(&mut test_state, &icache());
1246        assert_eq!(
1247            test_state.code_stack.to_string(),
1248            "4 1 2 3 5"
1249        );
1250    }
1251
1252    #[test]
1253    fn code_yank_dup_copies_item_to_top() {
1254        let mut test_state = PushState::new();
1255        test_state.code_stack.push(Item::int(5));
1256        test_state.code_stack.push(Item::int(4));
1257        test_state.code_stack.push(Item::int(3));
1258        test_state.code_stack.push(Item::int(2));
1259        test_state.code_stack.push(Item::int(1));
1260        assert_eq!(
1261            test_state.code_stack.to_string(),
1262            "1 2 3 4 5"
1263        );
1264        test_state.int_stack.push(3);
1265        code_yank_dup(&mut test_state, &icache());
1266        assert_eq!(
1267            test_state.code_stack.to_string(),
1268            "4 1 2 3 4 5"
1269        );
1270    }
1271}