Skip to main content

ra_ap_parser/
lib.rs

1//! The Rust parser.
2//!
3//! NOTE: The crate is undergoing refactors, don't believe everything the docs
4//! say :-)
5//!
6//! The parser doesn't know about concrete representation of tokens
7//! and syntax trees. Abstract [`Input`] and [`Output`] traits are
8//! used to provide tokens instead. As a consequence, this crate does
9//! not contain a lexer.
10//!
11//! The [`Parser`] struct from the [`parser`] module is a cursor into the
12//! sequence of tokens.  Parsing routines use [`Parser`] to inspect current
13//! state and advance the parsing.
14//!
15//! The actual parsing happens in the [`grammar`] module.
16//!
17//! Tests for this crate live in the `syntax` crate.
18//!
19//! [`Parser`]: crate::parser::Parser
20
21#![allow(rustdoc::private_intra_doc_links)]
22#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
23
24#[cfg(not(feature = "in-rust-tree"))]
25extern crate ra_ap_rustc_lexer as rustc_lexer;
26#[cfg(feature = "in-rust-tree")]
27extern crate rustc_driver as _;
28#[cfg(feature = "in-rust-tree")]
29extern crate rustc_lexer;
30
31mod event;
32mod frontmatter;
33mod grammar;
34mod input;
35mod lexed_str;
36mod output;
37mod parser;
38mod shortcuts;
39mod syntax_kind;
40mod token_set;
41
42pub use T_ as T;
43
44#[cfg(test)]
45mod tests;
46
47pub(crate) use token_set::TokenSet;
48
49pub use edition::Edition;
50
51pub use crate::{
52    input::Input,
53    lexed_str::LexedStr,
54    output::{Output, Step},
55    shortcuts::StrStep,
56    syntax_kind::SyntaxKind,
57};
58
59/// True if `c` is whitespace in Rust source (Unicode Pattern_White_Space as in the language reference).
60///
61/// See <https://doc.rust-lang.org/reference/whitespace.html>.
62pub fn is_rust_whitespace(c: char) -> bool {
63    frontmatter::is_whitespace(c)
64}
65
66/// Parse the whole of the input as a given syntactic construct.
67///
68/// This covers two main use-cases:
69///
70///   * Parsing a Rust file.
71///   * Parsing a result of macro expansion.
72///
73/// That is, for something like
74///
75/// ```ignore
76/// quick_check! {
77///    fn prop() {}
78/// }
79/// ```
80///
81/// the input to the macro will be parsed with [`PrefixEntryPoint::Item`], and
82/// the result will be [`TopEntryPoint::MacroItems`].
83///
84/// [`TopEntryPoint::parse`] makes a guarantee that
85///   * all input is consumed
86///   * the result is a valid tree (there's one root node)
87#[derive(Debug)]
88pub enum TopEntryPoint {
89    SourceFile,
90    MacroStmts,
91    MacroItems,
92    Pattern,
93    Type,
94    Expr,
95    /// Edge case -- macros generally don't expand to attributes, with the
96    /// exception of `cfg_attr` which does!
97    MetaItem,
98}
99
100impl TopEntryPoint {
101    pub fn parse(&self, input: &Input) -> Output {
102        let _p = tracing::info_span!("TopEntryPoint::parse", ?self).entered();
103        let entry_point: fn(&'_ mut parser::Parser<'_>) = match self {
104            TopEntryPoint::SourceFile => grammar::entry::top::source_file,
105            TopEntryPoint::MacroStmts => grammar::entry::top::macro_stmts,
106            TopEntryPoint::MacroItems => grammar::entry::top::macro_items,
107            TopEntryPoint::Pattern => grammar::entry::top::pattern,
108            TopEntryPoint::Type => grammar::entry::top::type_,
109            TopEntryPoint::Expr => grammar::entry::top::expr,
110            TopEntryPoint::MetaItem => grammar::entry::top::meta_item,
111        };
112        let mut p = parser::Parser::new(input);
113        entry_point(&mut p);
114        let (events, errors) = p.finish();
115        let res = event::process(events, errors);
116
117        if cfg!(debug_assertions) {
118            let mut depth = 0;
119            let mut first = true;
120            for step in res.iter() {
121                assert!(depth > 0 || first);
122                first = false;
123                match step {
124                    Step::Enter { .. } => depth += 1,
125                    Step::Exit => depth -= 1,
126                    Step::FloatSplit { ends_in_dot: has_pseudo_dot } => {
127                        depth -= 1 + !has_pseudo_dot as usize
128                    }
129                    Step::Token { .. } | Step::Error { .. } => (),
130                }
131            }
132            assert!(!first, "no tree at all");
133            assert_eq!(depth, 0, "unbalanced tree");
134        }
135
136        res
137    }
138}
139
140/// Parse a prefix of the input as a given syntactic construct.
141///
142/// This is used by macro-by-example parser to implement things like `$i:item`
143/// and the naming of variants follows the naming of macro fragments.
144///
145/// Note that this is generally non-optional -- the result is intentionally not
146/// `Option<Output>`. The way MBE work, by the time we *try* to parse `$e:expr`
147/// we already commit to expression. In other words, this API by design can't be
148/// used to implement "rollback and try another alternative" logic.
149#[derive(Debug)]
150pub enum PrefixEntryPoint {
151    Vis,
152    Block,
153    Stmt,
154    Pat,
155    PatTop,
156    Ty,
157    Expr,
158    Path,
159    Item,
160    MetaItem,
161}
162
163impl PrefixEntryPoint {
164    pub fn parse(&self, input: &Input) -> Output {
165        let entry_point: fn(&'_ mut parser::Parser<'_>) = match self {
166            PrefixEntryPoint::Vis => grammar::entry::prefix::vis,
167            PrefixEntryPoint::Block => grammar::entry::prefix::block,
168            PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
169            PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
170            PrefixEntryPoint::PatTop => grammar::entry::prefix::pat_top,
171            PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
172            PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
173            PrefixEntryPoint::Path => grammar::entry::prefix::path,
174            PrefixEntryPoint::Item => grammar::entry::prefix::item,
175            PrefixEntryPoint::MetaItem => grammar::entry::prefix::meta_item,
176        };
177        let mut p = parser::Parser::new(input);
178        entry_point(&mut p);
179        let (events, errors) = p.finish();
180        event::process(events, errors)
181    }
182}
183
184/// A parsing function for a specific braced-block.
185pub struct Reparser(fn(&mut parser::Parser<'_>));
186
187impl Reparser {
188    /// If the node is a braced block, return the corresponding `Reparser`.
189    pub fn for_node(
190        node: SyntaxKind,
191        first_child: Option<SyntaxKind>,
192        parent: Option<SyntaxKind>,
193    ) -> Option<Reparser> {
194        grammar::reparser(node, first_child, parent).map(Reparser)
195    }
196
197    /// Re-parse given tokens using this `Reparser`.
198    ///
199    /// Tokens must start with `{`, end with `}` and form a valid brace
200    /// sequence.
201    pub fn parse(self, tokens: &Input) -> Output {
202        let Reparser(r) = self;
203        let mut p = parser::Parser::new(tokens);
204        r(&mut p);
205        let (events, errors) = p.finish();
206        event::process(events, errors)
207    }
208}