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;
198mod dijkstra;
199#[doc(hidden)]
200pub mod lex_api;
201#[doc(hidden)]
202pub mod parser;
203#[cfg(test)]
204pub mod test_utils;
205
206pub use crate::{
207 ctbuilder::{CTParser, CTParserBuilder, RustEdition, Visibility},
208 lex_api::{LexError, Lexeme, Lexer, LexerTypes, NonStreamingLexer},
209 parser::{LexParseError, Node, ParseError, ParseRepair, RTParserBuilder, RecoveryKind},
210};
211
212pub use crate::parser::action_generictree;
213/// A convenience macro for including statically compiled `.y` files. A file `src/a/b/c.y`
214/// processed by [CTParserBuilder::grammar_in_src_dir] can then be used in a crate with
215/// `lrpar_mod!("a/b/c.y")`.
216///
217/// Note that you can use `lrpar_mod` with [CTParserBuilder::output_path] if, and only if, the
218/// output file was placed in [std::env::var]`("OUT_DIR")` or one of its subdirectories.
219#[macro_export]
220macro_rules! lrpar_mod {
221 ($path:expr) => {
222 include!(concat!(env!("OUT_DIR"), "/", $path, ".rs"));
223 };
224}
225
226#[deprecated(
227 since = "0.13.0",
228 note = "Please import this as `cfgrammar::Span` instead"
229)]
230pub use cfgrammar::Span;
231
232/// This private module with pub items which is directly related to
233/// the "Sealed trait" pattern. These items are used within the current
234/// crate. See `unstable_api` module for enabling usage outside the crate.
235mod unstable {
236 #![allow(unused)]
237 #![allow(unreachable_pub)]
238 pub struct UnstableApi;
239 pub trait UnstableTrait {}
240}
241
242/// A module for lifting restrictions on visibility by enabling unstable features.
243///
244/// See the sources for a complete list of features, and members.
245pub mod unstable_api {
246 /// Unstable functions that take a value `UnstableApi` require
247 /// the "_unstable_api" feature. This feature controls
248 /// whether the value has `pub` visibility outside the crate.
249 #[cfg(feature = "_unstable_api")]
250 pub use crate::unstable::UnstableApi;
251
252 /// This is a a supertrait for traits that are considered to be Unstable.
253 /// Unstable traits do not provide any semver guarantees.
254 ///
255 /// Enabling the `_unsealed_unstable traits` makes this supertrait publicly
256 /// Visible.
257 ///
258 ///
259 /// Declaring an unstable Api within the crate:
260 /// ```ignore_rust
261 /// // Within the crate use `crate::unstable::` .
262 /// pub trait Foo: crate::unstable::UnstableTrait {
263 /// fn foo(key: crate::unstable::UnstableApi);
264 /// }
265 /// ```
266 ///
267 /// Deriving the trait outside the crate (requires feature `_unsealed_unstable_traits`)
268 /// ```ignore_rust
269 /// struct Bar;
270 /// impl unstable_api::UnstableTrait for Bar{}
271 /// impl Foo for Bar {
272 /// fn foo(key: unstable_api::UnstableApi) {
273 /// ...
274 /// }
275 /// }
276 /// ```
277 ///
278 ///
279 /// Calling an implementation of the trait outside the crate (requires feature `_unstable_api`:
280 /// ```ignore_rust
281 /// let x: &dyn Foo = ...;
282 /// x.foo(unstable_api::UnstableApi);
283 /// ```
284 #[cfg(feature = "_unsealed_unstable_traits")]
285 pub use crate::unstable::UnstableTrait;
286
287 /// An value that acts as a key to inform callers that they are
288 /// calling an unstable internal api. This value is public by default.
289 /// Access to it does not require any features to be enabled.
290 ///
291 /// Q. When this should be used?
292 ///
293 /// A. When generated code needs to call internal api within it,
294 /// where you do not want the caller to have to enable any features
295 /// to use the generated code.
296 pub struct InternalPublicApi;
297}