rucc_ast/lib.rs
1//! The arena-allocated AST and its printer.
2//!
3//! Design: `spec/06-lexer-and-parser.md`. Layer rank 6, see `spec/18-package-layout.md`.
4//!
5//! # Status
6//!
7//! The tree is here: the three arenas, the side tables, the nodes for every expression,
8//! statement and declaration this compiler intends to parse, the declarator representation that
9//! the type system reads, and the [`Printer`] that writes any of it back out as C. What fills
10//! the tree in is [`rucc-parse`](https://docs.rs/rucc-parse).
11//!
12//! Every crate in the workspace is published, and publishing implies a promise. This one is
13//! tier 3: its Rust API is explicitly unstable and will change without a major version bump.
14//! Depend on the `rucc` binary's behaviour, not on this.
15//!
16//! # What shape the tree is
17//!
18//! Flat vectors and four-byte indices, per `spec/03-architecture.md` section 3.3. An [`Ast`]
19//! owns everything in one translation unit, [`Expr`], [`Stmt`] and [`Decl`] live in three
20//! arenas of their own, and anything that would make a node bigger than it needs to be lives in
21//! a side table with an index in the node. Spans are in a vector parallel to each arena rather
22//! than in the node, because almost nothing that walks the tree reads them.
23//!
24//! ```
25//! use rucc_ast::{Ast, BinaryOp, Expr};
26//! use rucc_diag::Span;
27//!
28//! let mut ast = Ast::new();
29//! let left = ast.expr(Expr::Bool(true), Span::new(0, 4));
30//! let right = ast.expr(Expr::Bool(false), Span::new(8, 13));
31//! let both = ast.expr(Expr::Binary { op: BinaryOp::LogAnd, lhs: left, rhs: right },
32//! Span::new(0, 13));
33//!
34//! assert_eq!(ast[left], Expr::Bool(true));
35//! assert_eq!(ast.expr_span(both), Span::new(0, 13));
36//! assert_eq!(ast.counts().exprs, 3);
37//! ```
38//!
39//! # What the tree does not do
40//!
41//! It is not desugared and it never will be. `a[i]` is a subscript, `a += b` is a compound
42//! assignment, a `for` loop is a `for` loop, and a `switch` is a statement with cases in it
43//! rather than a table. Rewriting any of that here would make every diagnostic after this point
44//! talk about a program nobody wrote. The rewriting happens once, at IR construction, in
45//! `spec/08-ir.md`.
46//!
47//! It is also untyped. A [`Expr::Name`] is an identifier and not a declaration, a
48//! [`TypeSpec::Typedef`] is the name a typedef was written with and not the type behind it, and
49//! nothing here holds a type from `rucc-types`. Names are resolved and types are assigned by
50//! `rucc-sema`, which produces the typed tree that everything downstream reads.
51//!
52//! The one place the tree does more than record what was written is [`Builtin`], which holds
53//! the type keywords as the multiset they were written in and turns them into a type with
54//! [`Builtin::resolve`]. That is a table rather than a judgement, it is the same table in every
55//! dialect, and it is much easier to get right with a test next to it than spread across the
56//! parser.
57
58#![doc(html_root_url = "https://docs.rs/rucc-ast/0.2.8")]
59
60mod asm;
61mod ast;
62mod attr;
63mod decl;
64mod expr;
65mod init;
66mod print;
67mod spec;
68mod stmt;
69
70pub use crate::asm::{Asm, AsmId, AsmOperand, AsmQuals};
71pub use crate::ast::{
72 AsmOperandList, Ast, AttrArgList, AttrList, CharId, Counts, DeclList, DeclRef, DerivedList,
73 DesignatorList, EnumeratorList, ExprList, ExprRef, FloatId, GenericList, InitDeclaratorList,
74 InitItemList, IntId, MemberList, ParamList, StmtList, StmtRef, StrId, StrList, StrRef,
75 SymbolList,
76};
77pub use crate::attr::{AttrArg, AttrSyntax, Attribute};
78pub use crate::decl::{
79 ArraySize, Decl, DeclId, Declarator, DeclaratorId, Derived, Enumerator, Field, InitDeclarator,
80 MAX_DECLARATOR_DEPTH, Member, Param, ParamKind, TypeName, TypeNameId,
81};
82pub use crate::expr::{BinaryOp, Expr, ExprId, GenericAssoc, UnaryOp};
83pub use crate::init::{Designator, Init, InitId, InitItem};
84pub use crate::print::{Printer, print};
85pub use crate::spec::{
86 AlignSpec, Basic, Builtin, BuiltinError, BuiltinSet, Complexity, DeclSpecs, DeclSpecsId,
87 FuncSpecs, Quals, RecordKind, Scalar, StorageClass, TypeSpec, TypeofArg,
88};
89pub use crate::stmt::{ForInit, Stmt, StmtId};
90
91/// The milestone in `spec/17-milestones.md` that fills this crate in.
92pub const MILESTONE: &str = "M2";
93
94#[cfg(test)]
95mod tests {
96 #[test]
97 fn milestone_is_recorded() {
98 assert!(super::MILESTONE.starts_with('M'));
99 }
100}