1use crate::{
2 buffers::{Buffers, DefaultBuffers},
3 lexer::{Lexer, Token, TokenType},
4 words::{Atom, Word, WordsOrComments},
5 Callbacks, Comment, GCode, Line, Mnemonic, Nop,
6};
7use core::{iter::Peekable, marker::PhantomData};
8
9pub fn parse<'input>(src: &'input str) -> impl Iterator<Item = GCode> + 'input {
16 full_parse_with_callbacks(src, Nop).flat_map(|line| line.into_gcodes())
17}
18
19pub fn full_parse_with_callbacks<'input, C: Callbacks + 'input>(
26 src: &'input str,
27 callbacks: C,
28) -> impl Iterator<Item = Line<'input>> + 'input {
29 let tokens = Lexer::new(src);
30 let atoms = WordsOrComments::new(tokens);
31 Lines::new(atoms, callbacks)
32}
33
34#[derive(Debug)]
36pub struct Parser<'input, C, B = DefaultBuffers> {
37 lines: Lines<'input, WordsOrComments<'input, Lexer<'input>>, C, B>,
40}
41
42impl<'input, C, B> Parser<'input, C, B> {
43 pub fn new(src: &'input str, callbacks: C) -> Self {
46 let tokens = Lexer::new(src);
47 let atoms = WordsOrComments::new(tokens);
48 let lines = Lines::new(atoms, callbacks);
49 Parser { lines }
50 }
51}
52
53impl<'input, B> From<&'input str> for Parser<'input, Nop, B> {
54 fn from(src: &'input str) -> Self { Parser::new(src, Nop) }
55}
56
57impl<'input, C: Callbacks, B: Buffers<'input>> Iterator
58 for Parser<'input, C, B>
59{
60 type Item = Line<'input, B>;
61
62 fn next(&mut self) -> Option<Self::Item> { self.lines.next() }
63}
64
65#[derive(Debug)]
66struct Lines<'input, I, C, B>
67where
68 I: Iterator<Item = Atom<'input>>,
69{
70 atoms: Peekable<I>,
71 callbacks: C,
72 last_gcode_type: Option<Word>,
73 _buffers: PhantomData<B>,
74}
75
76impl<'input, I, C, B> Lines<'input, I, C, B>
77where
78 I: Iterator<Item = Atom<'input>>,
79{
80 fn new(atoms: I, callbacks: C) -> Self {
81 Lines {
82 atoms: atoms.peekable(),
83 callbacks,
84 last_gcode_type: None,
85 _buffers: PhantomData,
86 }
87 }
88}
89
90impl<'input, I, C, B> Lines<'input, I, C, B>
91where
92 I: Iterator<Item = Atom<'input>>,
93 C: Callbacks,
94 B: Buffers<'input>,
95{
96 fn handle_line_number(
97 &mut self,
98 word: Word,
99 line: &mut Line<'input, B>,
100 has_temp_gcode: bool,
101 ) {
102 if line.gcodes().is_empty()
103 && line.line_number().is_none()
104 && !has_temp_gcode
105 {
106 line.set_line_number(word);
107 } else {
108 self.callbacks.unexpected_line_number(word.value, word.span);
109 }
110 }
111
112 fn handle_arg(
113 &mut self,
114 word: Word,
115 line: &mut Line<'input, B>,
116 temp_gcode: &mut Option<GCode<B::Arguments>>,
117 ) {
118 if let Some(mnemonic) = Mnemonic::for_letter(word.letter) {
119 self.last_gcode_type = Some(word);
122 if let Some(completed) = temp_gcode.take() {
123 if let Err(e) = line.push_gcode(completed) {
124 self.on_gcode_push_error(e.0);
125 }
126 }
127 *temp_gcode = Some(GCode::new_with_argument_buffer(
128 mnemonic,
129 word.value,
130 word.span,
131 B::Arguments::default(),
132 ));
133 return;
134 }
135
136 if let Some(temp) = temp_gcode {
138 if let Err(e) = temp.push_argument(word) {
139 self.on_arg_push_error(&temp, e.0);
140 }
141 return;
142 }
143
144 match self.last_gcode_type {
147 Some(ty) => {
148 let mut new_gcode = GCode::new_with_argument_buffer(
149 Mnemonic::for_letter(ty.letter).unwrap(),
150 ty.value,
151 ty.span,
152 B::Arguments::default(),
153 );
154 if let Err(e) = new_gcode.push_argument(word) {
155 self.on_arg_push_error(&new_gcode, e.0);
156 }
157 *temp_gcode = Some(new_gcode);
158 },
159 None => {
161 self.callbacks.argument_without_a_command(
162 word.letter,
163 word.value,
164 word.span,
165 );
166 },
167 }
168 }
169
170 fn handle_broken_word(&mut self, token: Token<'_>) {
171 if token.kind == TokenType::Letter {
172 self.callbacks
173 .letter_without_a_number(token.value, token.span);
174 } else {
175 self.callbacks
176 .number_without_a_letter(token.value, token.span);
177 }
178 }
179
180 fn on_arg_push_error(&mut self, gcode: &GCode<B::Arguments>, arg: Word) {
181 self.callbacks.gcode_argument_buffer_overflowed(
182 gcode.mnemonic(),
183 gcode.major_number(),
184 gcode.minor_number(),
185 arg,
186 );
187 }
188
189 fn on_comment_push_error(&mut self, comment: Comment<'_>) {
190 self.callbacks.comment_buffer_overflow(comment);
191 }
192
193 fn on_gcode_push_error(&mut self, gcode: GCode<B::Arguments>) {
194 self.callbacks.gcode_buffer_overflowed(
195 gcode.mnemonic(),
196 gcode.major_number(),
197 gcode.minor_number(),
198 gcode.arguments(),
199 gcode.span(),
200 );
201 }
202
203 fn next_line_number(&mut self) -> Option<usize> {
204 self.atoms.peek().map(|a| a.span().line)
205 }
206}
207
208impl<'input, I, C, B> Iterator for Lines<'input, I, C, B>
209where
210 I: Iterator<Item = Atom<'input>> + 'input,
211 C: Callbacks,
212 B: Buffers<'input>,
213{
214 type Item = Line<'input, B>;
215
216 fn next(&mut self) -> Option<Self::Item> {
217 let mut line = Line::default();
218 let mut temp_gcode = None;
221
222 while let Some(next_line) = self.next_line_number() {
223 if !line.is_empty() && next_line != line.span().line {
224 break;
226 }
227
228 match self.atoms.next().expect("unreachable") {
229 Atom::Unknown(token) => {
230 self.callbacks.unknown_content(token.value, token.span)
231 },
232 Atom::Comment(comment) => {
233 if let Err(e) = line.push_comment(comment) {
234 self.on_comment_push_error(e.0);
235 }
236 },
237 Atom::Word(word) if word.letter.to_ascii_lowercase() == 'n' => {
239 self.handle_line_number(
240 word,
241 &mut line,
242 temp_gcode.is_some(),
243 );
244 },
245 Atom::Word(word) => {
246 self.handle_arg(word, &mut line, &mut temp_gcode)
247 },
248 Atom::BrokenWord(token) => self.handle_broken_word(token),
249 }
250 }
251
252 if let Some(gcode) = temp_gcode.take() {
253 if let Err(e) = line.push_gcode(gcode) {
254 self.on_gcode_push_error(e.0);
255 }
256 }
257
258 if line.is_empty() {
259 None
260 } else {
261 Some(line)
262 }
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269 use crate::Span;
270 use arrayvec::ArrayVec;
271 use std::{prelude::v1::*, sync::Mutex};
272
273 #[derive(Debug)]
274 struct MockCallbacks<'a> {
275 unexpected_line_number: &'a Mutex<Vec<(f32, Span)>>,
276 }
277
278 impl<'a> Callbacks for MockCallbacks<'a> {
279 fn unexpected_line_number(&mut self, line_number: f32, span: Span) {
280 self.unexpected_line_number
281 .lock()
282 .unwrap()
283 .push((line_number, span));
284 }
285 }
286
287 #[derive(Debug, Copy, Clone, PartialEq)]
288 enum BigBuffers {}
289
290 impl<'input> Buffers<'input> for BigBuffers {
291 type Arguments = ArrayVec<[Word; 16]>;
292 type Commands = ArrayVec<[GCode<Self::Arguments>; 16]>;
293 type Comments = ArrayVec<[Comment<'input>; 16]>;
294 }
295
296 fn parse(
297 src: &str,
298 ) -> Lines<'_, impl Iterator<Item = Atom<'_>>, Nop, BigBuffers> {
299 let tokens = Lexer::new(src);
300 let atoms = WordsOrComments::new(tokens);
301 Lines::new(atoms, Nop)
302 }
303
304 #[test]
305 fn we_can_parse_a_comment() {
306 let src = "(this is a comment)";
307 let got: Vec<_> = parse(src).collect();
308
309 assert_eq!(got.len(), 1);
310 let line = &got[0];
311 assert_eq!(line.comments().len(), 1);
312 assert_eq!(line.gcodes().len(), 0);
313 assert_eq!(line.span(), Span::new(0, src.len(), 0));
314 }
315
316 #[test]
317 fn line_numbers() {
318 let src = "N42";
319 let got: Vec<_> = parse(src).collect();
320
321 assert_eq!(got.len(), 1);
322 let line = &got[0];
323 assert_eq!(line.comments().len(), 0);
324 assert_eq!(line.gcodes().len(), 0);
325 let span = Span::new(0, src.len(), 0);
326 assert_eq!(
327 line.line_number(),
328 Some(Word {
329 letter: 'N',
330 value: 42.0,
331 span
332 })
333 );
334 assert_eq!(line.span(), span);
335 }
336
337 #[test]
338 fn line_numbers_after_the_start_are_an_error() {
339 let src = "G90 N42";
340 let unexpected_line_number = Default::default();
341 let got: Vec<_> = full_parse_with_callbacks(
342 src,
343 MockCallbacks {
344 unexpected_line_number: &unexpected_line_number,
345 },
346 )
347 .collect();
348
349 assert_eq!(got.len(), 1);
350 assert!(got[0].line_number().is_none());
351 let unexpected_line_number = unexpected_line_number.lock().unwrap();
352 assert_eq!(unexpected_line_number.len(), 1);
353 assert_eq!(unexpected_line_number[0].0, 42.0);
354 }
355
356 #[test]
357 fn parse_g90() {
358 let src = "G90";
359 let got: Vec<_> = parse(src).collect();
360
361 assert_eq!(got.len(), 1);
362 let line = &got[0];
363 assert_eq!(line.gcodes().len(), 1);
364 let g90 = &line.gcodes()[0];
365 assert_eq!(g90.major_number(), 90);
366 assert_eq!(g90.minor_number(), 0);
367 assert_eq!(g90.arguments().len(), 0);
368 }
369
370 #[test]
371 fn parse_command_with_arguments() {
372 let src = "G01X5 Y-20";
373 let should_be =
374 GCode::new(Mnemonic::General, 1.0, Span::new(0, src.len(), 0))
375 .with_argument(Word {
376 letter: 'X',
377 value: 5.0,
378 span: Span::new(3, 5, 0),
379 })
380 .with_argument(Word {
381 letter: 'Y',
382 value: -20.0,
383 span: Span::new(6, 10, 0),
384 });
385
386 let got: Vec<_> = parse(src).collect();
387
388 assert_eq!(got.len(), 1);
389 let line = &got[0];
390 assert_eq!(line.gcodes().len(), 1);
391 let g01 = &line.gcodes()[0];
392 assert_eq!(g01, &should_be);
393 }
394
395 #[test]
396 fn multiple_commands_on_the_same_line() {
397 let src = "G01 X5 G90 (comment) G91 M10\nG01";
398
399 let got: Vec<_> = parse(src).collect();
400
401 assert_eq!(got.len(), 2);
402 assert_eq!(got[0].gcodes().len(), 4);
403 assert_eq!(got[0].comments().len(), 1);
404 assert_eq!(got[1].gcodes().len(), 1);
405 }
406
407 #[test]
410 #[cfg(feature = "serde-1")]
411 fn you_can_actually_serialize_lines() {
412 let src = "G01 X5 G90 (comment) G91 M10\nG01\n";
413 let line = parse(src).next().unwrap();
414
415 fn assert_serializable<S: serde::Serialize>(_: &S) {}
416 fn assert_deserializable<'de, D: serde::Deserialize<'de>>() {}
417
418 assert_serializable(&line);
419 assert_deserializable::<Line<'_>>();
420 }
421
422 #[test]
425 #[ignore]
426 fn funny_bug_in_crate_example() {
427 let src = "G90 \n G01 X50.0 Y-10";
428 let expected = vec![
429 GCode::new(Mnemonic::General, 90.0, Span::PLACEHOLDER),
430 GCode::new(Mnemonic::General, 1.0, Span::PLACEHOLDER)
431 .with_argument(Word::new('X', 50.0, Span::PLACEHOLDER))
432 .with_argument(Word::new('Y', -10.0, Span::PLACEHOLDER)),
433 ];
434
435 let got: Vec<_> = crate::parse(src).collect();
436
437 assert_eq!(got, expected);
438 }
439}