lrpar/mod.rs
1#![allow(clippy::cognitive_complexity)]
2#![allow(clippy::many_single_char_names)]
3#![allow(clippy::needless_doctest_main)]
4#![allow(clippy::new_without_default)]
5#![allow(clippy::range_plus_one)]
6#![allow(clippy::too_many_arguments)]
7#![allow(clippy::type_complexity)]
8#![allow(clippy::unnecessary_wraps)]
9#![allow(clippy::upper_case_acronyms)]
10#![forbid(unsafe_code)]
11#![deny(unreachable_pub)]
12
13//! `lrpar` provides a Yacc-compatible parser (where grammars can be generated at compile-time or
14//! run-time). It can take in traditional `.y` files and convert them into an idiomatic Rust
15//! parser.
16//!
17//! If you're new to `lrpar`, please read the "quick start guide". The "grmtools book" and API
18//! reference have more detailed information. You can find the appropriate documentation for the
19//! version of lrpar you are using here:
20//!
21//! | Latest release | master |
22//! |-----------------------------------------|--------|
23//! | [Quickstart guide](https://softdevteam.github.io/grmtools/latest_release/book/quickstart.html) | [Quickstart guide](https://softdevteam.github.io/grmtools/master/book/quickstart.html) |
24//! | [grmtools book](https://softdevteam.github.io/grmtools/latest_release/book/) | [grmtools book](https://softdevteam.github.io/grmtools/master/book) |
25//! | [lrpar API](https://docs.rs/lrpar/) | [lrpar API](https://softdevteam.github.io/grmtools/master/api/lrpar/) |
26//!
27//! [Documentation for all past and present releases](https://softdevteam.github.io/grmtools/)
28//!
29//!
30//! ## Example
31//!
32//! Let's assume we want to statically generate a parser for a simple calculator language (and
33//! let's also assume we are able to use [`lrlex`](https://crates.io/crates/lrlex) for the lexer).
34//! We need to add a `build.rs` file to our project which statically compiles both the lexer and
35//! parser. While we can perform both steps individually, it's easiest to use `lrlex` which does
36//! both jobs for us in one go. Our `build.rs` file thus looks as follows:
37//!
38//! ```text
39//! use cfgrammar::yacc::YaccKind;
40//! use lrlex::CTLexerBuilder;
41//!
42//! fn main() {
43//! CTLexerBuilder::new()
44//! .lrpar_config(|ctp| {
45//! ctp.yacckind(YaccKind::Grmtools)
46//! .grammar_in_src_dir("calc.y")
47//! .unwrap()
48//! })
49//! .lexer_in_src_dir("calc.l")
50//! .unwrap()
51//! .build()
52//! .unwrap();
53//! }
54//! ```
55//!
56//! where `src/calc.l` is as follows:
57//!
58//! ```text
59//! %%
60//! [0-9]+ "INT"
61//! \+ "+"
62//! \* "*"
63//! \( "("
64//! \) ")"
65//! [\t ]+ ;
66//! ```
67//!
68//! and `src/calc.y` is as follows:
69//!
70//! ```text
71//! %start Expr
72//! %avoid_insert "INT"
73//! %%
74//! Expr -> Result<u64, ()>:
75//! Expr '+' Term { Ok($1? + $3?) }
76//! | Term { $1 }
77//! ;
78//!
79//! Term -> Result<u64, ()>:
80//! Term '*' Factor { Ok($1? * $3?) }
81//! | Factor { $1 }
82//! ;
83//!
84//! Factor -> Result<u64, ()>:
85//! '(' Expr ')' { $2 }
86//! | 'INT'
87//! {
88//! let v = $1.map_err(|_| ())?;
89//! parse_int($lexer.span_str(v.span()))
90//! }
91//! ;
92//! %%
93//! // Any functions here are in scope for all the grammar actions above.
94//!
95//! fn parse_int(s: &str) -> Result<u64, ()> {
96//! match s.parse::<u64>() {
97//! Ok(val) => Ok(val),
98//! Err(_) => {
99//! eprintln!("{} cannot be represented as a u64", s);
100//! Err(())
101//! }
102//! }
103//! }
104//! ```
105//!
106//! Because we specified that our Yacc file is in `Grmtools` format, each rule has a
107//! separate Rust type to which all its functions conform (in this case, all the
108//! rules have the same type, but that's not a requirement).
109//!
110//! A simple `src/main.rs` is as follows:
111//!
112//! ```text
113//! use std::io::{self, BufRead, Write};
114//!
115//! use lrlex::lrlex_mod;
116//! use lrpar::lrpar_mod;
117//!
118//! // Using `lrlex_mod!` brings the lexer for `calc.l` into scope. By default the module name
119//! // will be `calc_l` (i.e. the file name, minus any extensions, with a suffix of `_l`).
120//! lrlex_mod!("calc.l");
121//! // Using `lrpar_mod!` brings the parser for `calc.y` into scope. By default the module name
122//! // will be `calc_y` (i.e. the file name, minus any extensions, with a suffix of `_y`).
123//! lrpar_mod!("calc.y");
124//!
125//! fn main() {
126//! // Get the `LexerDef` for the `calc` language.
127//! let lexerdef = calc_l::lexerdef();
128//! let stdin = io::stdin();
129//! loop {
130//! print!(">>> ");
131//! io::stdout().flush().ok();
132//! match stdin.lock().lines().next() {
133//! Some(Ok(ref l)) => {
134//! if l.trim().is_empty() {
135//! continue;
136//! }
137//! // Now we create a lexer with the `lexer` method with which we can lex an input.
138//! let lexer = lexerdef.lexer(l);
139//! // Pass the lexer to the parser and lex and parse the input.
140//! let (res, errs) = calc_y::parse(&lexer);
141//! for e in errs {
142//! println!("{}", e.pp(&lexer, &calc_y::token_epp));
143//! }
144//! match res {
145//! Some(Ok(r)) => println!("Result: {}", r),
146//! _ => eprintln!("Unable to evaluate expression.")
147//! }
148//! }
149//! _ => break
150//! }
151//! }
152//! }
153//! ```
154//!
155//! We can now `cargo run` our project and evaluate simple expressions:
156//!
157//! ```text
158//! >>> 2 + 3
159//! Result: 5
160//! >>> 2 + 3 * 4
161//! Result: 14
162//! >>> (2 + 3) * 4
163//! Result: 20
164//! ```
165//!
166//! `lrpar` also comes with advanced [error
167//! recovery](https://softdevteam.github.io/grmtools/master/book/errorrecovery.html) built-in:
168//!
169//! ```text
170//! >>> 2 + + 3
171//! Parsing error at line 1 column 5. Repair sequences found:
172//! 1: Delete +
173//! 2: Insert INT
174//! Result: 5
175//! >>> 2 + 3 3
176//! Parsing error at line 1 column 7. Repair sequences found:
177//! 1: Insert *
178//! 2: Insert +
179//! 3: Delete 3
180//! Result: 11
181//! >>> 2 + 3 4 5
182//! Parsing error at line 1 column 7. Repair sequences found:
183//! 1: Insert *, Delete 4
184//! 2: Insert +, Delete 4
185//! 3: Delete 4, Delete 5
186//! 4: Insert +, Shift 4, Delete 5
187//! 5: Insert +, Shift 4, Insert +
188//! 6: Insert *, Shift 4, Delete 5
189//! 7: Insert *, Shift 4, Insert *
190//! 8: Insert *, Shift 4, Insert +
191//! 9: Insert +, Shift 4, Insert *
192//! Result: 17
193//! ```
194
195mod cpctplus;
196#[doc(hidden)]
197pub mod ctbuilder;
198#[doc(hidden)]
199pub mod diagnostics;
200mod dijkstra;
201#[doc(hidden)]
202pub mod lex_api;
203#[doc(hidden)]
204pub mod parser;
205#[cfg(test)]
206pub mod test_utils;
207
208pub use crate::{
209 ctbuilder::{CTParser, CTParserBuilder, RustEdition, Visibility},
210 lex_api::{LexError, Lexeme, Lexer, LexerTypes, NonStreamingLexer},
211 parser::{LexParseError, Node, ParseError, ParseRepair, RTParserBuilder, RecoveryKind},
212};
213
214pub use crate::parser::action_generictree;
215/// A convenience macro for including statically compiled `.y` files. A file `src/a/b/c.y`
216/// processed by [CTParserBuilder::grammar_in_src_dir] can then be used in a crate with
217/// `lrpar_mod!("a/b/c.y")`.
218///
219/// Note that you can use `lrpar_mod` with [CTParserBuilder::output_path] if, and only if, the
220/// output file was placed in [std::env::var]`("OUT_DIR")` or one of its subdirectories.
221#[macro_export]
222macro_rules! lrpar_mod {
223 ($path:expr) => {
224 include!(concat!(env!("OUT_DIR"), "/", $path, ".rs"));
225 };
226}
227
228#[deprecated(
229 since = "0.13.0",
230 note = "Please import this as `cfgrammar::Span` instead"
231)]
232pub use cfgrammar::Span;
233
234/// This private module with pub items which is directly related to
235/// the "Sealed trait" pattern. These items are used within the current
236/// crate. See `unstable_api` module for enabling usage outside the crate.
237mod unstable {
238 #![allow(unused)]
239 #![allow(unreachable_pub)]
240 pub struct UnstableApi;
241 pub trait UnstableTrait {}
242}
243
244/// A module for lifting restrictions on visibility by enabling unstable features.
245///
246/// See the sources for a complete list of features, and members.
247pub mod unstable_api {
248 /// Unstable functions that take a value `UnstableApi` require
249 /// the "_unstable_api" feature. This feature controls
250 /// whether the value has `pub` visibility outside the crate.
251 #[cfg(feature = "_unstable_api")]
252 pub use crate::unstable::UnstableApi;
253
254 /// This is a a supertrait for traits that are considered to be Unstable.
255 /// Unstable traits do not provide any semver guarantees.
256 ///
257 /// Enabling the `_unsealed_unstable traits` makes this supertrait publicly
258 /// Visible.
259 ///
260 ///
261 /// Declaring an unstable Api within the crate:
262 /// ```ignore_rust
263 /// // Within the crate use `crate::unstable::` .
264 /// pub trait Foo: crate::unstable::UnstableTrait {
265 /// fn foo(key: crate::unstable::UnstableApi);
266 /// }
267 /// ```
268 ///
269 /// Deriving the trait outside the crate (requires feature `_unsealed_unstable_traits`)
270 /// ```ignore_rust
271 /// struct Bar;
272 /// impl unstable_api::UnstableTrait for Bar{}
273 /// impl Foo for Bar {
274 /// fn foo(key: unstable_api::UnstableApi) {
275 /// ...
276 /// }
277 /// }
278 /// ```
279 ///
280 ///
281 /// Calling an implementation of the trait outside the crate (requires feature `_unstable_api`:
282 /// ```ignore_rust
283 /// let x: &dyn Foo = ...;
284 /// x.foo(unstable_api::UnstableApi);
285 /// ```
286 #[cfg(feature = "_unsealed_unstable_traits")]
287 pub use crate::unstable::UnstableTrait;
288
289 /// An value that acts as a key to inform callers that they are
290 /// calling an unstable internal api. This value is public by default.
291 /// Access to it does not require any features to be enabled.
292 ///
293 /// Q. When this should be used?
294 ///
295 /// A. When generated code needs to call internal api within it,
296 /// where you do not want the caller to have to enable any features
297 /// to use the generated code.
298 pub struct InternalPublicApi;
299}