rucc_parse/lib.rs
1//! Recursive descent with a Pratt expression parser, declarators, and error recovery.
2//!
3//! Design: `spec/06-lexer-and-parser.md`. Layer rank 7, see `spec/18-package-layout.md`.
4//!
5//! # Status
6//!
7//! The grammar is here: expressions, declaration specifiers, declarators, initializers,
8//! statements and declarations, in every dialect from C89 to C23 and with the GNU extensions
9//! that real code cannot be read without. [`parse`] takes the tokens phase 7 produced and gives
10//! back a tree and the diagnostics it collected on the way. What is not here is the printer, so
11//! there is no way to turn the tree back into source yet, and there is nothing that checks the
12//! tree means anything.
13//!
14//! ```
15//! use rucc_base::Interner;
16//! use rucc_lex::{Convert, Keywords, Options, convert, tokenize};
17//! use rucc_parse::{Context, parse};
18//! use rucc_session::Std;
19//! use rucc_target::{TargetInfo, Triple};
20//!
21//! let std = Std::C23;
22//! // The keyword table is interned first, before any source is read.
23//! let mut interner = Interner::new();
24//! let keywords = Keywords::new(&mut interner, std, true);
25//! let target = TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().unwrap());
26//!
27//! let source = b"int main(void) { return 0; }";
28//! let (pp, _) = tokenize(source, 0, Options::new(), &mut interner);
29//! let cx = Convert { keywords: &keywords, interner: &interner, target: &target,
30//! std, pedantic: false };
31//! let (tokens, _) = convert(&pp, &cx);
32//!
33//! let parsed = parse(&tokens, Context::new(&interner, std));
34//! assert!(!parsed.failed());
35//! assert_eq!(parsed.ast.top_level().len(), 1);
36//! ```
37//!
38//! # What the parser reads
39//!
40//! A slice of [`rucc_lex::Token`], which is what phase 7 produces from the preprocessor's
41//! output. Directives are gone by then, adjacent string literals have been joined, constants
42//! have been converted, and a spelling no longer means anything. The parser never looks at
43//! source text and never asks the lexer a question, which is what makes the typedef decision in
44//! [`scope`] the only place the ambiguity in C's grammar is resolved.
45//!
46//! # What it builds
47//!
48//! A [`rucc_ast::Ast`], which records what was written rather than what it means. Nothing here
49//! resolves a name to a declaration, works out a type, folds a constant or desugars anything.
50//! A file that parses is not a file that compiles, and keeping the two apart is what lets the
51//! printer put back what it was given.
52//!
53//! # Where the productions are
54//!
55//! [`Parser`] holds the state and the helpers, and the productions are inherent methods on it
56//! written across six private modules: expressions, specifiers, declarators, initializers,
57//! statements and declarations. They are one recursive descent parser split up for reading
58//! rather than six things that call each other, so nothing is exported from them.
59//!
60//! Every crate in the workspace is published, and publishing implies a promise. This one is
61//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
62//! Depend on the `rucc` binary's behaviour, not on this.
63
64#![doc(html_root_url = "https://docs.rs/rucc-parse/0.2.8")]
65
66pub mod cursor;
67pub mod parser;
68pub mod recover;
69pub mod scope;
70
71mod decl;
72mod declarator;
73mod expr;
74mod init;
75mod spec;
76mod stmt;
77
78pub use crate::cursor::{Cursor, MAX_LOOKAHEAD, Mark};
79pub use crate::parser::{Context, MAX_NESTING, Parsed, Parser};
80pub use crate::recover::{Poison, push_about, skip_past_declaration, skip_to_statement_end};
81pub use crate::scope::{IdentKind, Scopes, TagKind};
82
83/// Parses a translation unit.
84///
85/// Always gives back a tree. A file that does not parse produces poisoned nodes where the
86/// productions gave up, so that everything after a mistake is still parsed and reported on, and
87/// [`Parsed::failed`] is what says whether to carry on with it.
88#[must_use]
89pub fn parse<'a>(tokens: &'a rucc_lex::Tokens, cx: Context<'a>) -> Parsed {
90 let mut parser = Parser::new(tokens, cx);
91 parser.translation_unit();
92 parser.finish()
93}
94
95/// The milestone in `spec/17-milestones.md` that fills this crate in.
96pub const MILESTONE: &str = "M2";
97
98#[cfg(test)]
99mod tests {
100 #[test]
101 fn milestone_is_recorded() {
102 assert!(super::MILESTONE.starts_with('M'));
103 }
104}