nom_gcode/
parse_gcode.rs

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    // GCode,
16    GCodeLine,
17    M,
18    GCodeParseError::*,
19};
20
21const STRING_ARG_MCODES: [u32; 7] = [
22    23,
23    28,
24    30,
25    // 32, Unsupported: Marlin M32 is a whole different kind of weird
26    36,
27    // M37 Unsupported: RepRap M37 is a whole different kind of weird
28    38,
29    117,
30    118,
31];
32
33// #[inline(always)]
34pub 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    // Strip leading whitespace
42    let (input, _) = space0::<_, VerboseError<&str>>(input)
43        .map_err(|_|
44            InvalidGCode(original_input.to_string())
45        )?;
46
47
48    // empty lines without a newline character
49    if input.is_empty() {
50        return Ok((input, None));
51    };
52
53    // Parse and return a non-gcode (empty line, demarcator or comment)
54    let mut non_gcode_line= alt((
55        // Empty line
56        map(newline, |_| None),
57        map(
58            alt((
59                // Demarcator (eg. "%")
60                demarcator,
61                // Doc Comment Line (eg. ";TIME:3600")
62                map(doc_comment, |doc| GCodeLine::DocComment(doc)),
63                // Comment Line (eg. "; Comment")
64                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    /*
75     * Parse the GCode command (eg. this would parse "G1" out of "G1 X10")
76     */
77
78    let (input, mut gcode) = parse_command(input)
79        .map_err(|_|
80            GCodeParseError::InvalidGCode(original_input.to_string())
81        )?;
82
83    /*
84     * Parse the GCode args (eg. this would parse "X10" out of "G1 X10")
85     */
86
87    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}