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