logo_interp/
executor.rs

1use std::collections::HashMap;
2use std::mem::swap;
3use crate::core::{LogoValue, Word};
4use crate::executor_state::*;
5use crate::parser;
6
7pub fn execute_str<S>(state: &mut EState<S>, proc_source: &str, source: &str) -> Result<(), String> {
8    state.logo_procedures = parser::parse_procedures(proc_source)?;
9    execute(state, parser::parse(source)?)
10}
11
12pub fn execute<S>(state: &mut EState<S>, source: Vec<LogoValue>) -> Result<(), String> {
13    let transformed_source = math_transform(source)?;
14    let mut it = transformed_source.iter();
15    while it.len() > 0 {
16        match execute_expr(state, &mut it)? {
17            Some(val) => return Err(format!("Don't know what to do with {}", val)),
18            None => {}
19        }
20    }
21    Ok(())
22}
23
24fn execute_expr<'a, S>(state: &mut EState<S>, it: &mut impl Iterator<Item = &'a LogoValue>) -> Result<Option<LogoValue>, String>
25{
26    let cmd = it.next();
27    if cmd.is_none() {
28        return Ok(None)
29    }
30    let cmd = cmd.unwrap();
31    if let LogoValue::Word(word) = cmd {
32        let word = &word.0;
33        if let Ok(_) = word.parse::<f64>() {
34            return Ok(Some(LogoValue::Word(Word(word.clone()))))
35        }
36
37        let word = word.to_lowercase();
38        if let Some(var_name) = word.strip_prefix(":") {
39            if !state.vars.contains_key(var_name) {
40                return Err("No such variable".to_string());
41            }
42            return Ok(Some(state.vars[var_name].clone()));
43        }
44
45        let fun = state.functions.get(word.as_str());
46        if let Some(fun) = fun {
47            let f = fun.f.clone();
48            let mut args = Vec::with_capacity(fun.args as usize);
49            for _ in 0..fun.args {
50                let arg = execute_expr(state, it)?;
51                match arg {
52                    Some(arg) => args.push(arg),
53                    None => return Err(format!("Missing argument for {}", word))
54                }
55            }
56            return (f)(state, args);
57        }
58
59        if let Some(logo_proc) = state.logo_procedures.get(word.as_str()) {
60            let logo_proc = logo_proc.clone();
61            let backup = backup_vars(state, &logo_proc.arg_names);
62            for arg_name in &logo_proc.arg_names {
63                let expr_result = execute_expr(state, it);
64                if let Err(_) = &expr_result {
65                    restore_vars(state, backup);
66                    return Err(expr_result.err().unwrap());
67                }
68                let expr_result = expr_result.unwrap();
69                if expr_result.is_none() {
70                    restore_vars(state, backup);
71                    return Err(format!("Missing argument for {}", word));
72                }
73                state.vars.insert(arg_name.clone(), expr_result.unwrap());
74            }
75            let proc_result = execute(state, logo_proc.code);
76            restore_vars(state, backup);
77            return match proc_result {
78                Err(err) => {
79                    if err == "Output" {
80                        let output = state.output.clone();
81                        state.output = None;
82                        return Ok(output);
83                    }
84                    Err(err)
85                },
86                Ok(()) => Ok(None)
87            }
88        }
89        return Err(format!("Don't know what to do with {}", cmd))
90    }
91
92    return Ok(Some(cmd.clone()));
93}
94
95fn backup_vars<S>(state: &EState<S>, var_names: &Vec<String>) -> Vec<(String, Option<LogoValue>)> {
96    let mut result = Vec::with_capacity(var_names.len());
97    for var_name in var_names {
98        let val = state.vars.get(var_name);
99        match val {
100            Some(val) => result.push((var_name.clone(), Some(val.clone()))),
101            None => result.push((var_name.clone(), None))
102        }
103    }
104    result
105}
106
107fn restore_vars<S>(state: &mut EState<S>, backup: Vec<(String, Option<LogoValue>)>) {
108    for (var_name, val) in backup {
109        match val {
110            Some(val) => {
111                state.vars.insert(var_name, val);
112            },
113            None => {
114                state.vars.remove(var_name.as_str());
115            }
116        }
117    }
118}
119
120fn math_transform(source: Vec<LogoValue>) -> Result<Vec<LogoValue>, String> {
121    let mut tree = BracketTree::parse(source)?;
122    process_math_signs(&mut tree, &HashMap::from([
123        ("*".to_string(), "product".to_string()),
124        ("/".to_string(), "quotient".to_string()),
125    ]))?;
126    process_math_signs(&mut tree, &HashMap::from([
127        ("+".to_string(), "sum".to_string()),
128        ("-".to_string(), "difference".to_string()),
129    ]))?;
130    process_math_signs(&mut tree, &HashMap::from([
131        (">".to_string(), "greater?".to_string()),
132        ("<".to_string(), "less?".to_string()),
133        ("=".to_string(), "equal?".to_string()),
134    ]))?;
135    Ok(tree.to_list())
136}
137
138fn process_math_signs(tree: &mut BracketTree, signs: &HashMap<String, String>) -> Result<(), String> {
139    tree.process(&|nodes: Vec<BracketTreeChild>| -> Result<Vec<BracketTreeChild>, String> {
140        let mut result = Vec::new();
141        let mut it = nodes.into_iter();
142        while let Some(node) = it.next() {
143            if let BracketTreeChild::Value(val) = &node {
144                if let LogoValue::Word(word) = val {
145                    if let Some(sing_op) = signs.get(word.0.as_str()) {
146                        let prev = match result.pop() {
147                            Some(val) => val,
148                            None => return Err(format!("Missing first argument for {}", word.0))
149                        };
150                        let next = match it.next() {
151                            Some(val) => val,
152                            None => return Err(format!("Missing second argument for {}", word.0))
153                        };
154                        let subtree = BracketTree{
155                            children: vec![
156                                BracketTreeChild::Value(LogoValue::Word(Word(sing_op.clone()))),
157                                prev,
158                                next
159                            ]
160                        };
161                        result.push(BracketTreeChild::Tree(Box::new(subtree)));
162                        continue;
163                    }
164                }
165            }
166            result.push(node);
167        }
168        Ok(result)
169    })
170}
171
172struct BracketTree {
173    children: Vec<BracketTreeChild>
174}
175
176enum BracketTreeChild {
177    Value(LogoValue),
178    Tree(Box<BracketTree>)
179}
180
181impl BracketTree {
182    fn new() -> Self {
183        BracketTree {children: Vec::new()}
184    }
185
186    fn parse(list: Vec<LogoValue>) -> Result<Self, String> {
187        let mut stack = Vec::new();
188        stack.push(BracketTree::new());
189        for el in list {
190            if let LogoValue::Word(word) = &el {
191                if word.0 == "(" {
192                    stack.push(BracketTree::new());
193                    continue;
194                }
195                else if word.0 == ")" {
196                    if stack.len() == 1 {
197                        return Err("Missing corresponding opening bracket for ')'".to_string());
198                    }
199                    let last_stack = stack.pop().unwrap();
200                    stack.last_mut().unwrap().children.push(BracketTreeChild::Tree(Box::new(last_stack)));
201                    continue;
202                }
203            }
204            stack.last_mut().unwrap().children.push(BracketTreeChild::Value(el));
205        }
206        if stack.len() > 1 {
207            return Err("Missing corresponding closing bracket for '('".to_string());
208        }
209        Ok(stack.pop().unwrap())
210    }
211
212    fn into_list(self, list: &mut Vec<LogoValue>) {
213        for child in self.children {
214            match child {
215                BracketTreeChild::Value(val) => {
216                    list.push(val)
217                },
218                BracketTreeChild::Tree(tree) => {
219                    tree.into_list(list);
220                }
221            }
222        }
223    }
224
225    fn to_list(self) -> Vec<LogoValue> {
226        let mut result = Vec::new();
227        self.into_list(&mut result);
228        result
229    }
230
231    fn process(&mut self, f: &impl Fn(Vec<BracketTreeChild>) -> Result<Vec<BracketTreeChild>, String>) -> Result<(), String> {
232        let mut tmp = Vec::new();
233        swap(&mut tmp, &mut self.children);
234        self.children = f(tmp)?;
235        for child in &mut self.children {
236            if let BracketTreeChild::Tree(tree) = child {
237                tree.process(f)?;
238            }
239        }
240        Ok(())
241    }
242}
243
244#[test]
245fn test_execution() {
246    use crate::stdlib::*;
247
248    struct S {
249        total: i32
250    }
251    let mut state = EState::new(S{total: 0});
252    add_stdlib(&mut state);
253    state.functions.insert("add".to_string(), Function::from_proc1(|s: &mut EState<S>, x: i32| -> Result<(), String> {
254        s.state.total += x;
255        Ok(())
256    }));
257
258    execute_str(&mut state, "", "add 5 repeat 4 [add 1] add 10").unwrap();
259    assert_eq!(state.state.total, 19);
260
261    state.state.total = 0;
262    execute_str(&mut state, "", "make 'hi' 5 add :hi add thing 'hi'").unwrap();
263    assert_eq!(state.state.total, 10);
264
265    state.state.total = 0;
266    execute_str(&mut state, "to add4 add 4 end \
267        to add_double :x add :x add :x end \
268        to double :x output sum :x :x end",
269 "add4 add_double 6 add double 3").unwrap();
270    assert_eq!(state.state.total, 22);
271}
272
273#[test]
274fn test_execution_math() {
275    use crate::stdlib::*;
276
277    struct S {
278        result: i32
279    }
280    let mut state = EState::new(S{result: 0});
281    add_stdlib(&mut state);
282    state.functions.insert("return".to_string(), Function::from_proc1(|s: &mut EState<S>, x: i32| -> Result<(), String> {
283        s.state.result = x;
284        Ok(())
285    }));
286
287    execute_str(&mut state, "", "return 2 + 3").unwrap();
288    assert_eq!(state.state.result, 5);
289
290    execute_str(&mut state, "", "return 2 + +4").unwrap();
291    assert_eq!(state.state.result, 6);
292
293    execute_str(&mut state, "", "return 2 + -3").unwrap();
294    assert_eq!(state.state.result, -1);
295
296    execute_str(&mut state, "", "return product 2 3 + sum 4 5").unwrap();
297    assert_eq!(state.state.result, 24);
298
299    execute_str(&mut state, "", "return (product 2 3) + (sum 4 5)").unwrap();
300    assert_eq!(state.state.result, 15);
301
302    execute_str(&mut state, "", "return 3 + 4 * 5 + 2").unwrap();
303    assert_eq!(state.state.result, 25);
304
305    execute_str(&mut state, "", "return (3 + 4) * (5 + 2)").unwrap();
306    assert_eq!(state.state.result, 49);
307
308    execute_str(&mut state, "", "return (1 + (3 + 4)) * ((5 + 2) + 2)").unwrap();
309    assert_eq!(state.state.result, 72);
310}
311
312#[test]
313fn test_execution_comparison() {
314    use crate::stdlib::*;
315
316    struct S {
317        result: bool
318    }
319    let mut state = EState::new(S{result: false});
320    add_stdlib(&mut state);
321    state.functions.insert("return".to_string(), Function::from_proc1(|s: &mut EState<S>, x: bool| -> Result<(), String> {
322        s.state.result = x;
323        Ok(())
324    }));
325
326    execute_str(&mut state, "", "return 2 = 2").unwrap();
327    assert_eq!(state.state.result, true);
328
329    execute_str(&mut state, "", "return 2 = 4 / 2").unwrap();
330    assert_eq!(state.state.result, true);
331
332    execute_str(&mut state, "", "return 1 = pi / pi").unwrap();
333    assert_eq!(state.state.result, true);
334
335    execute_str(&mut state, "", "return 1/3 = 2/6").unwrap();
336    assert_eq!(state.state.result, true);
337
338    execute_str(&mut state, "", "return 1/4 < 1/5").unwrap();
339    assert_eq!(state.state.result, false);
340
341    execute_str(&mut state, "", "return (ln 1) > 0").unwrap();
342    assert_eq!(state.state.result, false);
343}