1use std::collections::{HashMap, HashSet};
2use crate::core::*;
3
4lazy_static! {
5 static ref TERMINATOR_CHARS: HashSet<char>
6 = HashSet::from(['[', ']', '(', ')', '*', '/', '=']);
7}
8
9fn is_terminator_char(ch: char) -> bool {
10 return ch.is_whitespace() || TERMINATOR_CHARS.contains(&ch);
11}
12
13pub fn parse(source: &str) -> Result<Vec<LogoValue>, String> {
14 #[derive(PartialEq)]
15 enum Mode {
16 None,
17 Word,
18 DoubleQuoteString,
19 SingleQuoteString,
20 }
21 let mut mode = Mode::None;
22 let mut pending_word = String::new();
23
24 let mut list_stack: Vec<Vec<LogoValue>> = Vec::new();
25 list_stack.push(Vec::new());
26 for ch in source.chars() {
27 if (mode == Mode::Word || mode == Mode::DoubleQuoteString) && is_terminator_char(ch) {
28 if mode == Mode::Word {
29 list_stack.last_mut().unwrap().push(LogoValue::Word(Word(pending_word)));
30 }
31 else {
32 list_stack.last_mut().unwrap().push(LogoValue::String(pending_word));
33 }
34 pending_word = String::new();
35 mode = Mode::None;
36 }
37 if mode == Mode::SingleQuoteString && ch == '\'' {
38 list_stack.last_mut().unwrap().push(LogoValue::String(pending_word));
39 pending_word = String::new();
40 mode = Mode::None;
41 continue;
42 }
43
44 if mode != Mode::None {
45 pending_word.push(ch);
46 continue;
47 }
48
49 if ch.is_whitespace() {}
50 else if ch == '[' {
51 list_stack.push(Vec::new());
52 }
53 else if ch == ']' {
54 let last_list = list_stack.pop().unwrap();
55 match list_stack.last_mut() {
56 Some(stack) => stack.push(LogoValue::List(last_list)),
57 None => return Err("Not matched closing bracket".to_string())
58 }
59 }
60 else if ch == '"' {
61 mode = Mode::DoubleQuoteString;
62 }
63 else if ch == '\'' {
64 mode = Mode::SingleQuoteString;
65 }
66 else if TERMINATOR_CHARS.contains(&ch) {
67 list_stack.last_mut().unwrap().push(LogoValue::Word(Word(ch.to_string())));
68 }
69 else {
70 mode = Mode::Word;
71 pending_word = String::from(ch);
72 }
73 }
74 match mode {
75 Mode::None => {},
76 Mode::Word => list_stack.last_mut().unwrap().push(LogoValue::Word(Word(pending_word))),
77 Mode::DoubleQuoteString => list_stack.last_mut().unwrap().push(LogoValue::String(pending_word)),
78 Mode::SingleQuoteString => {
79 return Err(String::from("Missing closing quote"))
80 }
81 }
82 if list_stack.len() > 1 {
83 return Err(String::from("Missing closing bracket"));
84 }
85 return Ok(process_plus_minus(list_stack.pop().unwrap()));
86}
87
88fn process_plus_minus(list: Vec<LogoValue>) -> Vec<LogoValue> {
89 let mut result = Vec::with_capacity(list.len());
90 for val in list {
91 match val {
92 LogoValue::String(s) => result.push(LogoValue::String(s)),
93 LogoValue::List(sublist) => result.push(LogoValue::List(process_plus_minus(sublist))),
94 LogoValue::Word(word) => {
95 let mut cur = String::new();
96 for ch in word.0.chars() {
97 if ch != '+' && ch != '-' {
98 cur.push(ch);
99 continue;
100 }
101 if cur.is_empty() {
102 cur.push(ch);
103 }
104 else {
105 result.push(LogoValue::Word(Word(cur)));
106 result.push(LogoValue::Word(Word(ch.to_string())));
107 cur = String::new();
108 }
109 }
110 if !cur.is_empty() {
111 result.push(LogoValue::Word(Word(cur)));
112 }
113 }
114 }
115 }
116 result
117}
118
119pub fn parse_procedures(source: &str) -> Result<HashMap<String, LogoProcedure>, String> {
120 let mut result = HashMap::new();
121 let mut name = String::new();
122 let mut arg_names = Vec::new();
123 let mut code = Vec::new();
124 let values = parse(source)?;
125 #[derive(PartialEq)]
126 enum Mode {
127 None,
128 Name,
129 Params,
130 Body
131 }
132 let mut mode = Mode::None;
133
134 for value in values {
135 if mode == Mode::None {
136 if let LogoValue::Word(word) = &value {
137 if word.0.to_lowercase() == "to" {
138 mode = Mode::Name;
139 continue;
140 }
141 }
142 }
143 if mode == Mode::Name {
144 if let LogoValue::Word(word) = &value {
145 name = word.0.to_lowercase();
146 mode = Mode::Params;
147 continue;
148 }
149 }
150 if mode == Mode::Params {
151 if let LogoValue::Word(word) = &value {
152 if let Some(arg_name) = word.0.strip_prefix(":") {
153 arg_names.push(arg_name.to_lowercase());
154 continue;
155 }
156 }
157 mode = Mode::Body;
158 }
159 if mode == Mode::Body {
160 if let LogoValue::Word(word) = &value {
161 if word.0.to_lowercase() == "end" {
162 mode = Mode::None;
163 result.insert(name, LogoProcedure {arg_names, code});
164 name = String::new();
165 arg_names = Vec::new();
166 code = Vec::new();
167 continue;
168 }
169 }
170 code.push(value);
171 continue;
172 }
173 return Err("Invalid procedure syntax".to_string());
174 }
175
176 if mode != Mode::None {
177 return Err("Invalid procedure syntax".to_string());
178 }
179
180 Ok(result)
181}
182
183#[test]
184fn test_loop_parsing() {
185 let result = parse("repeat 12 [rt 30 repeat 4 [fd 50 rt 90]]");
186 let expected = vec![
187 LogoValue::Word(Word("repeat".to_string())),
188 LogoValue::Word(Word("12".to_string())),
189 LogoValue::List(vec![
190 LogoValue::Word(Word("rt".to_string())),
191 LogoValue::Word(Word("30".to_string())),
192 LogoValue::Word(Word("repeat".to_string())),
193 LogoValue::Word(Word("4".to_string())),
194 LogoValue::List(vec![
195 LogoValue::Word(Word("fd".to_string())),
196 LogoValue::Word(Word("50".to_string())),
197 LogoValue::Word(Word("rt".to_string())),
198 LogoValue::Word(Word("90".to_string())),
199 ])
200 ])
201 ];
202 assert_eq!(result, Ok(expected));
203}
204
205#[test]
206fn test_strings() {
207 let result = parse("\"hello world 'long string' blah");
208 let expected = vec![
209 LogoValue::String("hello".to_string()),
210 LogoValue::Word(Word("world".to_string())),
211 LogoValue::String("long string".to_string()),
212 LogoValue::Word(Word("blah".to_string())),
213 ];
214 assert_eq!(result, Ok(expected))
215}
216
217#[test]
218fn test_errors() {
219 let result = parse("[[]");
220 assert_eq!(result, Err("Missing closing bracket".to_string()));
221 let result = parse("[]]");
222 assert_eq!(result, Err("Not matched closing bracket".to_string()));
223 let result = parse("blah 'long string");
224 assert_eq!(result, Err("Missing closing quote".to_string()));
225}
226
227#[test]
228fn test_math() {
229 let result = parse("2+2");
230 let expected = Ok(vec![
231 LogoValue::Word(Word("2".to_string())),
232 LogoValue::Word(Word("+".to_string())),
233 LogoValue::Word(Word("2".to_string())),
234 ]);
235 assert_eq!(result, expected);
236 let result = parse("2 + 2");
237 assert_eq!(result, expected);
238
239 let result = parse("2 +2");
240 let expected = Ok(vec![
241 LogoValue::Word(Word("2".to_string())),
242 LogoValue::Word(Word("+2".to_string())),
243 ]);
244 assert_eq!(result, expected);
245
246 let result = parse("2 -2");
247 let expected = Ok(vec![
248 LogoValue::Word(Word("2".to_string())),
249 LogoValue::Word(Word("-2".to_string())),
250 ]);
251 assert_eq!(result, expected);
252}