pushr/push/
parser.rs

1use crate::push::instructions::InstructionSet;
2use crate::push::item::Item;
3use crate::push::stack::PushStack;
4use crate::push::state::PushState;
5use crate::push::vector::{BoolVector, FloatVector, IntVector};
6
7pub struct PushParser {}
8
9#[derive(Debug)]
10pub enum VectorType {
11    Bool,
12    Int,
13    Float,
14}
15
16impl PushParser {
17    /// Recursivley performs a front push to the stack. It keeps track of the open sublist by a depth
18    /// parameter. Returns true if the operation was sucessful
19    pub fn rec_push(stack: &mut PushStack<Item>, item: Item, depth: usize) -> bool {
20        if depth == 0 {
21            // Push at this level
22            stack.push_front(item);
23            return true;
24        }
25        if let Some(mut bottom_item) = stack.bottom_mut() {
26            match &mut bottom_item {
27                Item::List { items } => {
28                    // If the bottm element is a List push to its stack
29                    return PushParser::rec_push(items, item, depth - 1);
30                }
31                _ => {
32                    // Error: No more list found but depth > 0
33                    false
34                }
35            }
36        } else {
37            // Empty stack -> just push
38            stack.push(item);
39            true
40        }
41    }
42
43    /// Pushes the items on the execution stack to the code stack 
44    /// such that the order is preserved.
45    pub fn copy_to_code_stack(push_state: &mut PushState) {
46        let n = push_state.exec_stack.size();
47        if let Some(exec_code) = push_state.exec_stack.copy_vec(n) {
48            push_state.code_stack.push_vec(exec_code);
49        }
50    }
51
52    /// Determines vector type and pushes corresponding item to stack. Ignores
53    /// token if elements are not consistent.
54    pub fn parse_vector(
55        push_state: &mut PushState,
56        depth: usize,
57        vector_type: &VectorType,
58        vector_token: &str,
59    ) {
60        match vector_type {
61            VectorType::Bool => {
62                let mut bv = vec![];
63                for el in vector_token.split(",") {
64                    if "1" == el || "true" == el {
65                        bv.push(true);
66                    } else if "0" == el || "false" == el {
67                        bv.push(false);
68                    } else {
69                        return;
70                    }
71                }
72                PushParser::rec_push(
73                    &mut push_state.exec_stack,
74                    Item::boolvec(BoolVector::new(bv)),
75                    depth,
76                );
77            }
78            VectorType::Int => {
79                let mut iv = vec![];
80                for el in vector_token.split(",") {
81                    match el.to_string().parse::<i32>() {
82                        Ok(ival) => iv.push(ival),
83                        Err(_) => return,
84                    }
85                }
86                PushParser::rec_push(
87                    &mut push_state.exec_stack,
88                    Item::intvec(IntVector::new(iv)),
89                    depth,
90                );
91            }
92            VectorType::Float => {
93                let mut fv = vec![];
94                for el in vector_token.split(",") {
95                    match el.to_string().parse::<f32>() {
96                        Ok(fval) => fv.push(fval),
97                        Err(_) => return,
98                    }
99                }
100                PushParser::rec_push(
101                    &mut push_state.exec_stack,
102                    Item::floatvec(FloatVector::new(fv)),
103                    depth,
104                );
105            }
106        }
107    }
108
109    /// Splits a string into tokens and front pushes it to the stack s.t. the
110    /// end of the string ends up at the top of the stack.
111    pub fn parse_program(push_state: &mut PushState, instruction_set: &InstructionSet, code: &str) {
112        let mut depth = 0;
113        for token in code.split_whitespace() {
114            if token.starts_with("INT[") {
115                PushParser::parse_vector(
116                    push_state,
117                    depth,
118                    &VectorType::Int,
119                    &token[4..token.len() - 1],
120                );
121                continue;
122            }
123            if token.starts_with("FLOAT[") {
124                PushParser::parse_vector(
125                    push_state,
126                    depth,
127                    &VectorType::Float,
128                    &token[6..token.len() - 1],
129                );
130                continue;
131            }
132            if token.starts_with("BOOL[") {
133                PushParser::parse_vector(
134                    push_state,
135                    depth,
136                    &VectorType::Bool,
137                    &token[5..token.len() - 1],
138                );
139                continue;
140            }
141            if "(" == token {
142                PushParser::rec_push(
143                    &mut push_state.exec_stack,
144                    Item::List {
145                        items: PushStack::new(),
146                    },
147                    depth,
148                );
149                // Start of (sub) list
150                depth += 1;
151                continue;
152            }
153            if ")" == token {
154                // End of (sub) list
155                depth -= 1;
156                continue;
157            }
158
159            // Check for instruction
160            if instruction_set.is_instruction(token) {
161                PushParser::rec_push(
162                    &mut push_state.exec_stack,
163                    Item::instruction(token.to_string()),
164                    depth,
165                );
166                continue;
167            }
168            // Check for Literal
169            match token.to_string().parse::<i32>() {
170                Ok(ival) => {
171                    PushParser::rec_push(&mut push_state.exec_stack, Item::int(ival), depth);
172                    continue;
173                }
174                Err(_) => (),
175            }
176            match token.to_string().parse::<f32>() {
177                Ok(fval) => {
178                    PushParser::rec_push(&mut push_state.exec_stack, Item::float(fval), depth);
179                    continue;
180                }
181                Err(_) => (),
182            }
183
184            match token {
185                "TRUE" => {
186                    PushParser::rec_push(&mut push_state.exec_stack, Item::bool(true), depth);
187                    continue;
188                }
189                "FALSE" => {
190                    PushParser::rec_push(&mut push_state.exec_stack, Item::bool(false), depth);
191                    continue;
192                }
193                &_ => {
194                    PushParser::rec_push(
195                        &mut push_state.exec_stack,
196                        Item::name(token.to_string()),
197                        depth,
198                    );
199                }
200            }
201        }
202    }
203}
204#[cfg(test)]
205mod tests {
206    use super::*;
207
208    #[test]
209    pub fn parse_simple_program() {
210        let input = "( 2 3 INTEGER.* 4.1 5.2 FLOAT.+ TRUE FALSE BOOLEAN.OR )";
211        let mut push_state = PushState::new();
212        let mut instruction_set = InstructionSet::new();
213        instruction_set.load();
214        PushParser::parse_program(&mut push_state, &instruction_set, &input);
215        assert_eq!(push_state.exec_stack.to_string(), "( 2 3 INTEGER.* 4.100 5.200 FLOAT.+ TRUE FALSE BOOLEAN.OR )");
216    }
217
218    #[test]
219    pub fn parse_potentiation_program() {
220        let input = "( ARG FLOAT.DEFINE EXEC.Y ( ARG FLOAT.* 1 INTEGER.- INTEGER.DUP 0 INTEGER.> EXEC.IF ( ) EXEC.POP ) ) ";
221        let mut push_state = PushState::new();
222        let mut instruction_set = InstructionSet::new();
223        instruction_set.load();
224        PushParser::parse_program(&mut push_state, &instruction_set, &input);
225        assert_eq!(
226            push_state.exec_stack.to_string(),
227            "( ARG FLOAT.DEFINE EXEC.Y ( ARG FLOAT.* 1 INTEGER.- INTEGER.DUP 0 INTEGER.> EXEC.IF (  ) EXEC.POP ) )"
228        );
229    }
230
231    #[test]
232    pub fn parse_factorial_program() {
233        let input = "( CODE.QUOTE ( CODE.DUP INTEGER.DUP 1 INTEGER.- CODE.DO INTEGER.* )
234                       CODE.QUOTE ( INTEGER.POP 1 )
235                                      INTEGER.DUP 2 INTEGER.< CODE.IF )";
236        let mut push_state = PushState::new();
237        let mut instruction_set = InstructionSet::new();
238        instruction_set.load();
239        PushParser::parse_program(&mut push_state, &instruction_set, &input);
240        assert_eq!(
241            push_state.exec_stack.to_string(),
242            "( CODE.QUOTE ( CODE.DUP INTEGER.DUP 1 INTEGER.- CODE.DO INTEGER.* ) CODE.QUOTE ( INTEGER.POP 1 ) INTEGER.DUP 2 INTEGER.< CODE.IF )");
243    }
244
245    #[test]
246    pub fn parse_different_vector_types_with_correct_syntax() {
247        let input = "( BOOL[1,1,1,0,0] INT[2,345,-5] FLOAT[3.3,1.2,4.1] )";
248        let mut push_state = PushState::new();
249        let mut instruction_set = InstructionSet::new();
250        instruction_set.load();
251        PushParser::parse_program(&mut push_state, &instruction_set, &input);
252        assert_eq!(
253            push_state.exec_stack.to_string(),
254            "( [TRUE,TRUE,TRUE,FALSE,FALSE] [2,345,-5] [3.300,1.200,4.100] )"
255        );
256    }
257
258    #[test]
259    pub fn parse_different_vector_types_with_wrong_syntax() {
260        let input = "( BOOL[1,1,2,0,0] INT[2,345,-5.0] FLOAT[3.3,NANu,4.1] INT[1,2,3] )";
261        let mut push_state = PushState::new();
262        let mut instruction_set = InstructionSet::new();
263        instruction_set.load();
264        PushParser::parse_program(&mut push_state, &instruction_set, &input);
265        assert_eq!(
266            push_state.exec_stack.to_string(),
267            "( [1,2,3] )"
268        );
269    }
270}