gcode/lib.rs
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
//! A parser for g-code programs, with both a convenient AST API and a
//! zero-allocation visitor-based core designed for embedded environments.
//!
//! Some explicit design goals of this crate are:
//!
//! - **embedded-friendly:** users should be able to use this crate without
//! requiring access to an operating system (e.g. `#[no_std]` environments or
//! WebAssembly)
//! - **deterministic memory usage:** the [`crate::core`] parser can operate
//! without dynamic allocation
//! - **error-resistant:** the parser attempts to recover from erroneous input,
//! reporting diagnostics and continuing where possible
//! - **performance:** parsing should be reasonably fast, guaranteeing `O(n)`
//! time complexity with no backtracking
//!
//! # Getting Started
//!
//! With the [`alloc`] feature (enabled by default), use [`parse`] to parse
//! g-code into a [`Program`]. If any parse errors are emitted, [`parse`]
//! returns `Err` with the collected [`Diagnostics`].
//!
//! ```rust
//! # #[cfg(feature = "alloc")]
//! # fn main() -> Result<(), gcode::Diagnostics> {
//! use gcode::{Code, Value};
//!
//! let src = "G90 (absolute)\nG00 X50.0 Y-10";
//! let program = gcode::parse(src)?;
//!
//! assert!(program.blocks.len() >= 1);
//!
//! for block in &program.blocks {
//! for code in &block.codes {
//! if let Code::General(g) = code {
//! for arg in &g.args {
//! match (arg.letter, &arg.value) {
//! ('X', Value::Literal(x)) => assert_eq!(*x, 50.0),
//! ('Y', Value::Literal(y)) => assert_eq!(*y, -10.0),
//! _ => {}
//! }
//! }
//! }
//! }
//! }
//! # Ok(())
//! # }
//! # #[cfg(not(feature = "alloc"))]
//! # fn main() {}
//! ```
//!
//! Parse errors are reported as [`Diagnostic`]s and collected in [`Diagnostics`].
//!
//! For more complex use cases, including zero-allocation or streaming parsing,
//! refer to the [`core`] module.
//!
//! # Document model
//!
//! G-code is modelled as a sequence of *blocks*. A [`Program`] (from [`parse`])
//! is the root: it has `blocks`. Each [`Block`] corresponds roughly to one line
//! of source and contains an optional line number (N), comments, G/M/T
//! commands ([`Code`] with [`Argument`]s), and bare word addresses
//! ([`Block::word_addresses`]—e.g. `X10.5` without a preceding G/M/T).
//!
//! For example, this source:
//!
//! ```text
//! G1 X10.5 Y20.0 F1500
//! M3 S1000
//! ; start cutting
//! ```
//!
//! is a document of three blocks: the first has one G-code (G1) with arguments
//! X, Y, F; the second has one M-code (M3) with S; the third has a comment.
//!
//! Unlike JSON or XML, g-code has no single universal grammar; controllers and
//! dialects differ, and the meaning of a block often depends on machine state
//! or dialect rules. This crate therefore models g-code at the *syntactic*
//! level: the parser represents what was written, not what the machine would
//! do. Higher-level interpretation (e.g. whether X/Y are coordinates for a
//! move) is left to downstream code.
//!
//! # Zero allocation
//!
//! To avoid dynamic allocation, do not enable the `alloc` feature and do not
//! use the [`parse`] function (which builds an AST). Implement
//! [`ProgramVisitor`](crate::core::ProgramVisitor),
//! [`BlockVisitor`](crate::core::BlockVisitor), and
//! [`CommandVisitor`](crate::core::CommandVisitor) and pass your visitor to
//! [`core::parse`]; the parser drives your visitor and does not allocate.
//!
//! # Spans
//!
//! Each element's original location in the source is retained as a
//! [`Span`](crate::core::Span).
//!
//! This supports:
//!
//! - Showing where a parsing or semantic error occurred
//! - Highlighting the current command when stepping through a program
//! - Reporting progress (e.g. line/column) to the user or machine
//!
//! In the core API, visitor methods receive a `Span` (e.g.
//! [`BlockVisitor::line_number`](crate::core::BlockVisitor::line_number) and
//! [`BlockVisitor::comment`](crate::core::BlockVisitor::comment)). AST types
//! (with `alloc`) have a `span` field (e.g. [`Block::span`], [`Comment::span`],
//! [`GeneralCode::span`], [`Argument::span`]).
//!
//! # Feature Flags
//!
#![doc = document_features::document_features!()]
#![deny(
bare_trait_objects,
elided_lifetimes_in_paths,
missing_copy_implementations,
missing_debug_implementations,
rust_2018_idioms,
unreachable_pub,
unsafe_code,
unused_qualifications,
unused_results,
variant_size_differences,
rustdoc::broken_intra_doc_links,
missing_docs
)]
#![cfg_attr(not(test), no_std)]
// Make sure docs indicate when something is hidden behind a feature flag
#![cfg_attr(feature = "unstable-doc-cfg", feature(doc_cfg))]
#![cfg_attr(feature = "unstable-doc-cfg", doc(auto_cfg))]
pub mod core;
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
mod diags;
#[cfg(feature = "alloc")]
mod types;
#[cfg(feature = "alloc")]
mod visitor;
#[cfg(feature = "alloc")]
pub use crate::{
diags::{Diagnostic, DiagnosticKind, Diagnostics},
types::*,
visitor::AstBuilder,
};
/// Parse G-code source into a [`Program`] or return [`Diagnostics`] on error.
///
/// Requires the `alloc` feature. For zero-allocation or streaming parsing, use
/// [`core::parse`] with a custom [`ProgramVisitor`](crate::core::ProgramVisitor).
#[cfg(feature = "alloc")]
pub fn parse(src: &str) -> Result<Program, Diagnostics> {
let mut visitor = AstBuilder::new();
core::parse(src, &mut visitor);
visitor.finish()
}
#[doc = include_str!("../README.md")]
#[cfg(feature = "alloc")]
#[doc(hidden)]
pub fn _assert_readme_code_examples_compile() {}