1use nom::{
2 character::complete::*,
3 error::VerboseError,
4};
5use nom::branch::*;
6use nom::combinator::*;
7use nom::sequence::*;
8
9use super::{
10 parse_command,
11 parse_args,
12 comment,
13 doc_comment,
14 GCodeParseError,
15 GCodeLine,
17 M,
18 GCodeParseError::*,
19};
20
21const STRING_ARG_MCODES: [u32; 7] = [
22 23,
23 28,
24 30,
25 36,
27 38,
29 117,
30 118,
31];
32
33pub fn parse_gcode(input: &str) -> Result<(&str, Option<GCodeLine>), GCodeParseError> {
35 let original_input = input;
36 let demarcator = map(
37 pair(char('%'), not_line_ending),
38 |_: (char, &str)| GCodeLine::FileDemarcator,
39 );
40
41 let (input, _) = space0::<_, VerboseError<&str>>(input)
43 .map_err(|_|
44 InvalidGCode(original_input.to_string())
45 )?;
46
47
48 if input.is_empty() {
50 return Ok((input, None));
51 };
52
53 let mut non_gcode_line= alt((
55 map(newline, |_| None),
57 map(
58 alt((
59 demarcator,
61 map(doc_comment, |doc| GCodeLine::DocComment(doc)),
63 map(comment, |comment| GCodeLine::Comment(comment))
65 )),
66 |line| Some(line),
67 ),
68 ));
69
70 if let Ok((input, gcode_line)) = non_gcode_line(input) {
71 return Ok((input, gcode_line));
72 };
73
74 let (input, mut gcode) = parse_command(input)
79 .map_err(|_|
80 GCodeParseError::InvalidGCode(original_input.to_string())
81 )?;
82
83 let string_arg_mcode =
88 gcode.mnemonic == M
89 && gcode.minor == 0
90 && STRING_ARG_MCODES.contains(&gcode.major);
91
92 let (input, args_or_comments) = parse_args(
93 string_arg_mcode,
94 input,
95 )
96 .map_err(|_| InvalidArguments(original_input.to_string()))?;
97
98 gcode.args_or_comments = args_or_comments;
99 Ok((input, Some(GCodeLine::GCode(gcode))))
100}