async_gcode/
lib.rs

1//! This crate implements a GCode (RS-274) parser.
2//!
3//! The default dialect is taken from NIST's [RS274/NGC interpreter version 3].
4//! Some expensive part of that dialect such as parameters and expressions are gated behind feature
5//! – respectively `parse-parameters` and `parse-expressions` – in order to avoid dependency on
6//! dynamic allocation.
7//!
8//! Some extension to this dialect such as checksum, trailing comments, optional values are also
9//! supported and gated behind features.
10//!
11//! ## 🔩 Example
12//!
13//! ```
14//! use futures::stream;
15//! use futures_executor::block_on;
16//! use async_gcode::{Parser, Error};
17//! let input = r"
18//!    G21 H21. I21.098
19//!    J-21 K-21. L-21.098
20//!    M+21 N+21. P+21.098
21//!    Q.098 R-.098 S+.098
22//!    t - 21 . 33 "
23//!    // mapping to `Result<u8, Error>` to render error conversion transparent.
24//!    .bytes().map(Result::<_, Error>::Ok);
25//!
26//! block_on(async {
27//!     let mut parser = Parser::new(stream::iter(input));
28//!
29//!     loop {
30//!         if let Some(res) = parser.next().await {
31//!             println!("{:?}", res);
32//!         } else {
33//!             break;
34//!         }
35//!     }
36//! });
37//! ```
38//!
39//! ## Error management
40//!
41//! On parsing error the `Parser` can no longer trust its input and enters an error recovery state.
42//! No `GCode` or `Error` will be emitted until a new line character (`\n`) is received. Then a
43//! `GCode::Execute` is emitted and the parser restarts in a reliable known state.
44//!
45//! ## ⚙ Features
46//! - `std` : Enabled by default. Allows for the use of dynamic allocation.
47//! - `parse-comments` : enables the parser to return `GCode::Comment(String)`; requires an allocator.
48//! - `parse-trailing-comment`: allows line to end with a `; comment`.
49//! - `parse-checksum` : Enables the use of xorsum.
50//! - `parse-parameters` : Enables the use of `#` parameters ; requires an allocator.
51//!   If `string-value` is enabled then parameters may use string index.
52//!   If `optional-value` is enabled then parameters value may be omitted but **NOT** the indices.
53//! - `parse-expressions` : Enables parsing infix expressions ; requires an allocator.
54//! - `optional-value` : Allows to omit in `RealValue` in word and parameter value positions.
55//!   Parameter indices cannot be omitted nor can be literals in expressions.
56//! - `string-value` : Allows `RealValue` to be a string. Any character preceded with `\` will be
57//!   used as is (useful for `"`, `)` or new line).
58//!
59//! ## ⚠ Warning
60//!
61//! Dev-dependencies currently leak features to dependencies.
62//! This crate requires rust-nightly to build with no_std until `-Z features=dev_dep` makes it to
63//! stable.
64//!
65//! ## Note for future development
66//! It might be interesting to have a look at ISO 6983 and/or ISO 14649.
67//!
68//! During development fixed arithmetics was considered to be made available as an alternative to
69//! the floating point arithmetics especially for small target. After investigation, it does not
70//! seem to be a significant gain for the precision required by the gcode standard.
71//!
72//! Ideally all arithmetics triggered by a theoritically valid input should be caught and not
73//! trigger a panic. For an excessive  number of digit in a number may exceed the capacity of the
74//! variable used internally.
75//!
76//! [RS274/NGC interpreter version 3]: https://www.nist.gov/publications/nist-rs274ngc-interpreter-version-3?pub_id=823374
77#![cfg_attr(not(feature = "std"), no_std)]
78
79#[cfg(all(
80    not(feature = "std"),
81    any(
82        feature = "parse-expressions",
83        feature = "parse-parameters",
84        feature = "parse-comments",
85        feature = "string-value"
86    )
87))]
88extern crate alloc;
89
90#[cfg(all(not(feature = "std"), feature = "parse-comments"))]
91use alloc::string::String;
92
93#[macro_use]
94mod utils;
95
96mod stream;
97mod types;
98
99mod parser;
100
101pub use parser::Parser;
102pub use types::Literal;
103pub use types::RealValue;
104
105#[cfg(any(feature = "parse-expressions", feature = "parse-parameters"))]
106pub use types::expressions::Expression;
107
108#[derive(Debug, PartialEq, Eq, Clone, Copy)]
109pub enum Error {
110    /// Error no the gcode syntax
111    UnexpectedByte(u8),
112
113    /// The parsed number excedded the expected range.
114    NumberOverflow,
115
116    /// Format error during number parsing. Typically a dot without digits (at least one is
117    /// required).
118    BadNumberFormat,
119
120    #[cfg(any(feature = "parse-comments", feature = "string-value"))]
121    /// The string or comment received contained an invalid UTF-8 character sequence.
122    InvalidUTF8String,
123
124    #[cfg(feature = "parse-checksum")]
125    /// Checksum verification failed. The error contains the computed value.
126    BadChecksum(u8),
127
128    #[cfg(feature = "parse-expressions")]
129    /// The expressions received was invalid.
130    InvalidExpression,
131}
132
133#[derive(Debug, PartialEq, Clone)]
134pub enum GCode {
135    BlockDelete,
136    LineNumber(u32),
137    #[cfg(feature = "parse-comments")]
138    Comment(String),
139    Word(char, RealValue),
140    #[cfg(feature = "parse-parameters")]
141    /// When `optional-value` is enabled, the index cannot be `RealValue::None`.
142    ParameterSet(RealValue, RealValue),
143    Execute,
144}