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}