use std::fmt;
use std::ops::Index;
use rucc_base::{Idx, IdxRange, Symbol};
use rucc_diag::Span;
use rucc_lex::{CharConstant, FloatConstant, IntConstant, StringLiteral};
use crate::asm::{Asm, AsmId, AsmOperand};
use crate::attr::{AttrArg, Attribute};
use crate::decl::{
Decl, DeclId, Declarator, DeclaratorId, Derived, Enumerator, InitDeclarator, Member, Param,
TypeName, TypeNameId,
};
use crate::expr::{Expr, ExprId, GenericAssoc};
use crate::init::{Designator, Init, InitId, InitItem};
use crate::spec::{DeclSpecs, DeclSpecsId};
use crate::stmt::{Stmt, StmtId};
pub type IntId = Idx<IntConstant>;
pub type FloatId = Idx<FloatConstant>;
pub type CharId = Idx<CharConstant>;
pub type StrId = Idx<StringLiteral>;
#[derive(Debug)]
pub struct ExprRef;
#[derive(Debug)]
pub struct StmtRef;
#[derive(Debug)]
pub struct DeclRef;
#[derive(Debug)]
pub struct StrRef;
pub type ExprList = IdxRange<ExprRef>;
pub type StmtList = IdxRange<StmtRef>;
pub type DeclList = IdxRange<DeclRef>;
pub type StrList = IdxRange<StrRef>;
pub type SymbolList = IdxRange<Symbol>;
pub type AttrList = IdxRange<Attribute>;
pub type AttrArgList = IdxRange<AttrArg>;
pub type DerivedList = IdxRange<Derived>;
pub type ParamList = IdxRange<Param>;
pub type MemberList = IdxRange<Member>;
pub type EnumeratorList = IdxRange<Enumerator>;
pub type InitDeclaratorList = IdxRange<InitDeclarator>;
pub type InitItemList = IdxRange<InitItem>;
pub type DesignatorList = IdxRange<Designator>;
pub type GenericList = IdxRange<GenericAssoc>;
pub type AsmOperandList = IdxRange<AsmOperand>;
#[derive(Default)]
pub struct Ast {
exprs: Vec<Expr>,
expr_spans: Vec<Span>,
stmts: Vec<Stmt>,
stmt_spans: Vec<Span>,
decls: Vec<Decl>,
decl_spans: Vec<Span>,
declarators: Vec<Declarator>,
type_names: Vec<TypeName>,
specs: Vec<DeclSpecs>,
inits: Vec<Init>,
asms: Vec<Asm>,
ints: Vec<IntConstant>,
floats: Vec<FloatConstant>,
chars: Vec<CharConstant>,
strings: Vec<StringLiteral>,
expr_refs: Vec<ExprId>,
stmt_refs: Vec<StmtId>,
decl_refs: Vec<DeclId>,
str_refs: Vec<StrId>,
symbols: Vec<Symbol>,
attrs: Vec<Attribute>,
attr_args: Vec<AttrArg>,
derived: Vec<Derived>,
params: Vec<Param>,
members: Vec<Member>,
enumerators: Vec<Enumerator>,
init_declarators: Vec<InitDeclarator>,
init_items: Vec<InitItem>,
designators: Vec<Designator>,
generics: Vec<GenericAssoc>,
asm_operands: Vec<AsmOperand>,
top_level: Vec<DeclId>,
}
impl Ast {
#[must_use]
pub fn new() -> Ast {
Ast::default()
}
#[must_use]
pub fn top_level(&self) -> &[DeclId] {
&self.top_level
}
pub fn add_top_level(&mut self, decl: DeclId) {
self.top_level.push(decl);
}
pub fn expr(&mut self, expr: Expr, span: Span) -> ExprId {
let id = Idx::from_usize(self.exprs.len());
self.exprs.push(expr);
self.expr_spans.push(span);
id
}
pub fn stmt(&mut self, stmt: Stmt, span: Span) -> StmtId {
let id = Idx::from_usize(self.stmts.len());
self.stmts.push(stmt);
self.stmt_spans.push(span);
id
}
pub fn decl(&mut self, decl: Decl, span: Span) -> DeclId {
let id = Idx::from_usize(self.decls.len());
self.decls.push(decl);
self.decl_spans.push(span);
id
}
#[must_use]
pub fn expr_span(&self, id: ExprId) -> Span {
self.expr_spans[id.index()]
}
#[must_use]
pub fn stmt_span(&self, id: StmtId) -> Span {
self.stmt_spans[id.index()]
}
#[must_use]
pub fn decl_span(&self, id: DeclId) -> Span {
self.decl_spans[id.index()]
}
#[must_use]
pub fn counts(&self) -> Counts {
Counts { exprs: self.exprs.len(), stmts: self.stmts.len(), decls: self.decls.len() }
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.exprs.is_empty() && self.stmts.is_empty() && self.decls.is_empty()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Counts {
pub exprs: usize,
pub stmts: usize,
pub decls: usize,
}
impl fmt::Debug for Ast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let counts = self.counts();
f.debug_struct("Ast")
.field("exprs", &counts.exprs)
.field("stmts", &counts.stmts)
.field("decls", &counts.decls)
.field("top_level", &self.top_level.len())
.finish()
}
}
macro_rules! node_table {
($id:ty => $item:ty, $field:ident) => {
impl Index<$id> for Ast {
type Output = $item;
#[inline]
fn index(&self, id: $id) -> &$item {
&self.$field[id.index()]
}
}
};
}
macro_rules! list_table {
(
$(#[$doc:meta])*
$add:ident, $list:ty => $item:ty, $field:ident
) => {
impl Ast {
$(#[$doc])*
pub fn $add(&mut self, items: &[$item]) -> $list {
let start = Idx::from_usize(self.$field.len());
self.$field.extend_from_slice(items);
let end = Idx::from_usize(self.$field.len());
IdxRange::new(start, end)
}
}
impl Index<$list> for Ast {
type Output = [$item];
#[inline]
fn index(&self, list: $list) -> &[$item] {
&self.$field[list.as_usize_range()]
}
}
};
}
macro_rules! side_table {
(
$(#[$doc:meta])*
$add:ident, $id:ty => $item:ty, $field:ident
) => {
impl Ast {
$(#[$doc])*
pub fn $add(&mut self, item: $item) -> $id {
let id = Idx::from_usize(self.$field.len());
self.$field.push(item);
id
}
}
node_table!($id => $item, $field);
};
}
node_table!(ExprId => Expr, exprs);
node_table!(StmtId => Stmt, stmts);
node_table!(DeclId => Decl, decls);
side_table! {
add_declarator, DeclaratorId => Declarator, declarators
}
side_table! {
add_type_name, TypeNameId => TypeName, type_names
}
side_table! {
add_specs, DeclSpecsId => DeclSpecs, specs
}
side_table! {
add_init, InitId => Init, inits
}
side_table! {
add_asm, AsmId => Asm, asms
}
side_table! {
add_int, IntId => IntConstant, ints
}
side_table! {
add_float, FloatId => FloatConstant, floats
}
side_table! {
add_char, CharId => CharConstant, chars
}
side_table! {
add_string, StrId => StringLiteral, strings
}
list_table! {
add_expr_list, ExprList => ExprId, expr_refs
}
list_table! {
add_stmt_list, StmtList => StmtId, stmt_refs
}
list_table! {
add_decl_list, DeclList => DeclId, decl_refs
}
list_table! {
add_str_list, StrList => StrId, str_refs
}
list_table! {
add_symbol_list, SymbolList => Symbol, symbols
}
list_table! {
add_attr_list, AttrList => Attribute, attrs
}
list_table! {
add_attr_args, AttrArgList => AttrArg, attr_args
}
list_table! {
add_derived_list, DerivedList => Derived, derived
}
list_table! {
add_param_list, ParamList => Param, params
}
list_table! {
add_member_list, MemberList => Member, members
}
list_table! {
add_enumerator_list, EnumeratorList => Enumerator, enumerators
}
list_table! {
add_init_declarator_list, InitDeclaratorList => InitDeclarator, init_declarators
}
list_table! {
add_init_item_list, InitItemList => InitItem, init_items
}
list_table! {
add_designator_list, DesignatorList => Designator, designators
}
list_table! {
add_generic_list, GenericList => GenericAssoc, generics
}
list_table! {
add_asm_operand_list, AsmOperandList => AsmOperand, asm_operands
}
#[cfg(test)]
mod tests {
use super::*;
use crate::expr::BinaryOp;
fn span(lo: u32, hi: u32) -> Span {
Span::new(lo, hi)
}
#[test]
fn a_new_tree_is_empty() {
let ast = Ast::new();
assert!(ast.is_empty());
assert!(ast.top_level().is_empty());
assert_eq!(ast.counts(), Counts { exprs: 0, stmts: 0, decls: 0 });
}
#[test]
fn nodes_come_back_by_index_and_spans_stay_beside_them() {
let mut ast = Ast::new();
let one = ast.expr(Expr::Nullptr, span(0, 7));
let two = ast.expr(Expr::Bool(true), span(10, 14));
let sum = ast.expr(Expr::Binary { op: BinaryOp::Add, lhs: one, rhs: two }, span(0, 14));
assert_eq!(ast[one], Expr::Nullptr);
assert_eq!(ast[two], Expr::Bool(true));
assert_eq!(ast[sum], Expr::Binary { op: BinaryOp::Add, lhs: one, rhs: two });
assert_eq!(ast.expr_span(one), span(0, 7));
assert_eq!(ast.expr_span(sum), span(0, 14));
assert_eq!(ast.counts().exprs, 3);
assert!(!ast.is_empty());
}
#[test]
fn a_run_comes_back_in_the_order_it_went_in() {
let mut ast = Ast::new();
let a = ast.expr(Expr::Nullptr, span(0, 1));
let b = ast.expr(Expr::Bool(false), span(2, 3));
let c = ast.expr(Expr::Bool(true), span(4, 5));
let first = ast.add_expr_list(&[a, b]);
let second = ast.add_expr_list(&[c]);
assert_eq!(ast[first], [a, b]);
assert_eq!(ast[second], [c]);
assert_eq!(first.len(), 2);
}
#[test]
fn an_empty_run_is_valid_before_anything_is_in_the_table() {
let ast = Ast::new();
assert!(ast[AttrList::EMPTY].is_empty());
assert!(ast[DerivedList::EMPTY].is_empty());
assert!(ast[ExprList::EMPTY].is_empty());
}
#[test]
fn the_three_arenas_are_numbered_independently() {
let mut ast = Ast::new();
let e = ast.expr(Expr::Nullptr, span(0, 1));
let s = ast.stmt(Stmt::Empty, span(0, 1));
let d = ast.decl(Decl::Error, span(0, 1));
assert_eq!(e.raw(), 0);
assert_eq!(s.raw(), 0);
assert_eq!(d.raw(), 0);
assert_eq!(ast[s], Stmt::Empty);
assert_eq!(ast[d], Decl::Error);
assert_eq!(ast.stmt_span(s), span(0, 1));
assert_eq!(ast.decl_span(d), span(0, 1));
}
#[test]
fn debug_reports_the_shape_rather_than_the_tree() {
let mut ast = Ast::new();
let d = ast.decl(Decl::Error, span(0, 1));
ast.add_top_level(d);
let text = format!("{ast:?}");
assert!(text.starts_with("Ast {"), "{text}");
assert!(text.contains("decls: 1"), "{text}");
assert!(text.contains("top_level: 1"), "{text}");
}
}