1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! A zero-allocation, push-based parser for g-code.
//!
//! `gcode::core` is the low-level engine of this crate. Instead of building an
//! abstract syntax tree (AST) for you, it drives your own visitor
//! implementations and never allocates on your behalf. Use this module when
//! you need deterministic memory usage (e.g. embedded or `no_std`), when you
//! want to stream g-code and act on it as it arrives, or when you want full
//! control over how commands, numbers, and diagnostics are represented. If you
//! prefer a ready-made AST and default diagnostics, use [`crate::parse`]
//! and the [`crate::Program`] type instead; they are thin layers built on top
//! of [`crate::core`].
//!
//! ## How the API works
//!
//! The parser is built around a **visitor pattern** that mirrors the g-code
//! grammar: the parser drives the visitor, and the visitor trait hierarchy
//! matches the structure of the language. At the top level you implement
//! [`ProgramVisitor`], which creates a [`BlockVisitor`] for each block (line);
//! each block can then create a [`CommandVisitor`] for each command on that
//! line.
//!
//! ### Terminals vs non-terminals
//!
//! The grammar is reflected in the visitor API:
//!
//! - **Terminals** (leaf tokens) are reported by a single method call with the
//! value and its [`Span`]. No new visitor is created. Examples:
//! [`BlockVisitor::line_number`], [`BlockVisitor::comment`],
//! [`BlockVisitor::program_number`], [`CommandVisitor::argument`].
//!
//! - **Non-terminals** (sub-structures) are entered by a method that returns a
//! **child visitor** inside [`ControlFlow::Continue`]. The parser then drives
//! that visitor until the sub-structure ends, at which point it calls a
//! consuming method (`end_line` or `end_command`) and returns to the parent.
//! Examples: [`ProgramVisitor::start_block`] returns a [`BlockVisitor`] for
//! that line; [`BlockVisitor::start_general_code`] (and the other
//! `start_*_code` methods) return a [`CommandVisitor`] for that command.
//!
//! In other words, the call flow is:
//!
//! - parser calls `start_block()` and gets a [`BlockVisitor`]
//! - block visitor receives `line_number`, `comment`, and `start_*_code(…)`
//! - each `start_*_code(…)` returns a [`CommandVisitor`]
//! - command visitor receives one or more `argument(…)` calls and then
//! `end_command`
//! - control returns to the block visitor for more calls or `end_line`
//! - control returns to the program visitor
//!
//! The type of each level is fixed by the trait; the parser never allocates
//! intermediate nodes.
//!
//! ## Control flow and allocation
//!
//! Because each non-terminal is “enter by returning a visitor, exit by
//! consuming it”, the **caller** can implement visitors as values that only
//! borrow from their parent (for example a struct holding `&mut Vec<Block>`,
//! `&mut Diagnostics`, or other state). The parser only stores and invokes
//! whatever visitor type you supply; it does not build its own trees or
//! buffers. The entire parse can therefore be zero-allocation: no boxes, no
//! `Vec`s, no strings owned by the parser. Allocation happens only if your
//! visitor implementation chooses to allocate (for example to build an AST).
//!
//! If a visitor returns [`ControlFlow::Break`], the parser stops at a
//! well-defined pause point (for example when an output buffer is full). You
//! can resume later by calling [`resume`] with the returned [`ParserState`]
//! and the same visitor, and parsing will continue from where it left off.
//!
//! ## Diagnostics
//!
//! Recoverable diagnostics are reported via [`HasDiagnostics`]: the visitor
//! supplies a [`Diagnostics`] implementation, and the parser calls into it
//! (for example `emit_unknown_content`, `emit_unexpected`) when it encounters
//! bad input. The parser does not abort on these conditions; it reports and
//! continues, so callers can decide how to surface or aggregate errors.
//!
//! ## Relationship to the rest of the crate
//!
//! The higher-level [`crate`] module and the [`crate::parse`] convenience
//! function are implemented in terms of `gcode::core`: they provide visitors
//! that build an owned AST and collect diagnostics into a single value. As a
//! result, the behaviour of the entire crate is defined here; understanding
//! `gcode::core` gives you a precise mental model for how parsing, spans, and
//! diagnostics behave at every layer.
//!
//! Examples
//!
//! Implement [`ProgramVisitor`] to receive blocks as they are parsed. Each
//! block is handled by a [`BlockVisitor`], which in turn creates a
//! [`CommandVisitor`] for each G/M/T command.
//!
//! We don't care about errors in this example, so we use [`Noop`] as the
//! diagnostics implementation.
//!
//! ```rust
//! # #[allow(refining_impl_trait)]
//! use gcode::core::{
//! BlockVisitor, CommandVisitor, ControlFlow, HasDiagnostics, Noop,
//! Number, ProgramVisitor,
//! };
//!
//! #[derive(Default)]
//! struct Counter {
//! blocks: usize,
//! commands: usize,
//! diag: Noop,
//! }
//!
//! impl HasDiagnostics for Counter {
//! fn diagnostics(&mut self) -> &mut dyn gcode::core::Diagnostics {
//! &mut self.diag
//! }
//! }
//!
//! struct BlockCounter<'a>(&'a mut Counter);
//!
//! impl ProgramVisitor for Counter {
//! fn start_block(&mut self) -> ControlFlow<BlockCounter<'_>> {
//! self.blocks += 1;
//! ControlFlow::Continue(BlockCounter(&mut *self))
//! }
//! }
//!
//! impl HasDiagnostics for BlockCounter<'_> {
//! fn diagnostics(&mut self) -> &mut dyn gcode::core::Diagnostics {
//! &mut self.0.diag
//! }
//! }
//!
//! struct CommandCounter<'a>(&'a mut Counter);
//!
//! impl BlockVisitor for BlockCounter<'_> {
//! fn start_general_code(&mut self, _number: Number) -> ControlFlow<CommandCounter<'_>> {
//! self.0.commands += 1;
//! ControlFlow::Continue(CommandCounter(&mut *self.0))
//! }
//! fn start_miscellaneous_code(&mut self, _number: Number) -> ControlFlow<CommandCounter<'_>> {
//! self.0.commands += 1;
//! ControlFlow::Continue(CommandCounter(&mut *self.0))
//! }
//! fn start_tool_change_code(&mut self, _number: Number) -> ControlFlow<CommandCounter<'_>> {
//! self.0.commands += 1;
//! ControlFlow::Continue(CommandCounter(&mut *self.0))
//! }
//! }
//!
//! impl HasDiagnostics for CommandCounter<'_> {
//! fn diagnostics(&mut self) -> &mut dyn gcode::core::Diagnostics {
//! &mut self.0.diag
//! }
//! }
//!
//! impl CommandVisitor for CommandCounter<'_> {}
//!
//! let src = "G90\nG01 X5\nM3";
//! let mut counter = Counter::default();
//! gcode::core::parse(src, &mut counter);
//! assert_eq!(counter.blocks, 3);
//! assert_eq!(counter.commands, 3);
//! ```
pub use ;
pub use ;
/// Parses `src` from start to finish, driving `visitor` for each block.
///
/// For incremental or resumable parsing, use [`ParserState::empty`] and
/// [`resume`] instead; return [`ControlFlow::Break`] from your visitor when you
/// need to pause, then call [`resume`] with the returned state.