pushr/push/
index.rs

1use crate::push::instructions::Instruction;
2use crate::push::instructions::InstructionCache;
3use crate::push::state::PushState;
4use crate::push::stack::PushPrint;
5use std::collections::HashMap;
6use std::fmt;
7
8#[derive(Clone, Debug)]
9pub struct Index {
10    pub current: usize,
11    pub destination: usize,
12}
13
14impl Index {
15    pub fn new(dest_arg: usize) -> Self {
16        Self {
17            current: 0,
18            destination: dest_arg,
19        }
20    }
21}
22
23impl PushPrint for Index {
24   fn to_pstring(&self) -> String {
25       format!("{}", self.to_string())
26   }
27}
28
29impl fmt::Display for Index {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        write!(f, "{}/{}", self.current, self.destination)
32    }
33}
34
35impl PartialEq for Index {
36    fn eq(&self, other: &Self) -> bool {
37        self.current == other.current && self.destination == other.destination
38    }
39}
40
41/// Integer numbers (that is, numbers without decimal points).
42pub fn load_index_instructions(map: &mut HashMap<String, Instruction>) {
43    map.insert(
44        String::from("INDEX.CURRENT"),
45        Instruction::new(index_current),
46    );
47    map.insert(String::from("INDEX.DEFINE"), Instruction::new(index_define));
48    map.insert(
49        String::from("INDEX.DESTINATION"),
50        Instruction::new(index_destination),
51    );
52    map.insert(String::from("INDEX.FLUSH"), Instruction::new(index_flush));
53    map.insert(
54        String::from("INDEX.INCREASE"),
55        Instruction::new(index_increase),
56    );
57    map.insert(String::from("INDEX.POP"), Instruction::new(index_pop));
58}
59
60/// INDEX.CURRENT: Pushes the current field of the top INDEX to the INTEGER stack.
61pub fn index_current(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
62    if let Some(index) = push_state.index_stack.copy(0) {
63        push_state.int_stack.push(index.current as i32);
64    }
65}
66
67/// INDEX.DEFINE: Pushes the top INTEGER as destination of a new index.
68pub fn index_define(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
69    if let Some(index) = push_state.int_stack.pop() {
70        let corr_index = i32::max(0, index);
71        push_state.index_stack.push(Index::new(corr_index as usize));
72    }
73}
74
75/// INDEX.DESTINATION: Pushes the destination field of the top INDEX to the INTEGER stack.
76pub fn index_destination(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
77    if let Some(index) = push_state.index_stack.copy(0) {
78        push_state
79            .index_stack
80            .push(Index::new(index.destination as usize));
81    }
82}
83
84/// INDEX.FLUSH: Flushes the INDEX stack.
85pub fn index_flush(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
86    push_state.index_stack.flush();
87}
88
89/// INDEX.INCREASE: Increases the current value by one if current < destination. Otherwise
90/// this instruction acts as a NOOP.
91pub fn index_increase(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
92    if let Some(index) = push_state.index_stack.get_mut(0) {
93        if index.current < index.destination {
94            index.current += 1;
95        }
96    }
97}
98
99/// INDEX.POP: Pops the INDEX stack.
100pub fn index_pop(push_state: &mut PushState, _instruction_cache: &InstructionCache) {
101    push_state.index_stack.pop();
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    pub fn icache() -> InstructionCache {
109        InstructionCache::new(vec![])
110    }
111
112
113    #[test]
114    fn integer_modulus_pushes_result() {
115        let mut test_state = PushState::new();
116        test_state.index_stack.push(Index::new(3));
117        index_increase(&mut test_state, &icache());
118        let mut test_index = Index::new(3);
119        test_index.current += 1;
120        assert_eq!(test_state.index_stack.pop().unwrap(), test_index);
121    }
122}