use std::fmt;
use std::ops::Index;
use rucc_base::float::Float;
use rucc_base::{Idx, IdxRange, Symbol};
use rucc_diag::Span;
use rucc_lex::StringLiteral;
use rucc_types::VlaId;
use crate::decl::{Decl, DeclId, DeclList, InitEntry};
use crate::expr::{Expr, ExprId, ExprList};
use crate::stmt::{Case, CaseId, Stmt, StmtId, StmtList};
pub type ConstId = Idx<Const>;
pub type StrId = Idx<StringLiteral>;
pub type LabelId = Idx<Label>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Const {
Int(i128),
Float(Float),
Address(Address),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Address {
pub base: Base,
pub offset: i128,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Base {
Decl(DeclId),
Str(StrId),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Label {
pub name: Symbol,
pub stmt: Option<StmtId>,
}
#[derive(Default)]
pub struct Tast {
exprs: Vec<Expr>,
expr_spans: Vec<Span>,
stmts: Vec<Stmt>,
stmt_spans: Vec<Span>,
decls: Vec<Decl>,
decl_spans: Vec<Span>,
consts: Vec<Const>,
strings: Vec<StringLiteral>,
labels: Vec<Label>,
vlas: Vec<ExprId>,
expr_refs: Vec<ExprId>,
stmt_refs: Vec<StmtId>,
decl_refs: Vec<DeclId>,
cases: Vec<Case>,
init_entries: Vec<InitEntry>,
top_level: Vec<DeclId>,
}
impl Tast {
#[must_use]
pub fn new() -> Tast {
Tast::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
}
pub fn set_decl(&mut self, id: DeclId, decl: Decl) {
self.decls[id.index()] = decl;
}
pub fn set_stmt(&mut self, id: StmtId, stmt: Stmt) {
self.stmts[id.index()] = stmt;
}
#[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()]
}
pub fn add_vla(&mut self, size: ExprId) -> VlaId {
let id = u32::try_from(self.vlas.len()).expect("too many variable length arrays");
self.vlas.push(size);
VlaId(id)
}
#[must_use]
pub fn vla_size(&self, id: VlaId) -> ExprId {
self.vlas[id.0 as usize]
}
pub fn define_label(&mut self, id: LabelId, stmt: StmtId) {
self.labels[id.index()].stmt = Some(stmt);
}
#[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 Tast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let counts = self.counts();
f.debug_struct("Tast")
.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 Tast {
type Output = $item;
#[inline]
fn index(&self, id: $id) -> &$item {
&self.$field[id.index()]
}
}
};
}
macro_rules! side_table {
(
$(#[$doc:meta])*
$add:ident, $id:ty => $item:ty, $field:ident
) => {
impl Tast {
$(#[$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);
};
}
macro_rules! list_table {
(
$(#[$doc:meta])*
$add:ident, $list:ty => $item:ty, $field:ident
) => {
impl Tast {
$(#[$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 Tast {
type Output = [$item];
#[inline]
fn index(&self, list: $list) -> &[$item] {
&self.$field[list.as_usize_range()]
}
}
};
}
node_table!(ExprId => Expr, exprs);
node_table!(StmtId => Stmt, stmts);
node_table!(DeclId => Decl, decls);
node_table!(CaseId => Case, cases);
side_table! {
add_const, ConstId => Const, consts
}
side_table! {
add_string, StrId => StringLiteral, strings
}
side_table! {
add_label, LabelId => Label, labels
}
list_table! {
add_expr_refs, ExprList => ExprId, expr_refs
}
list_table! {
add_stmt_refs, StmtList => StmtId, stmt_refs
}
list_table! {
add_decl_refs, DeclList => DeclId, decl_refs
}
list_table! {
add_cases, crate::stmt::CaseList => Case, cases
}
list_table! {
add_init_entries, crate::decl::InitList => InitEntry, init_entries
}
#[cfg(test)]
mod tests {
use rucc_ast::BinaryOp;
use rucc_types::{IntKind, Types};
use super::*;
use crate::decl::{DeclKind, Definition, Linkage, StorageDuration};
use crate::expr::{Category, Conversion, ExprKind};
#[test]
fn the_nodes_are_the_size_they_are_meant_to_be() {
assert_eq!(size_of::<Expr>(), 24);
assert_eq!(size_of::<Stmt>(), 24);
assert_eq!(size_of::<Decl>(), 44);
assert_eq!(size_of::<Case>(), 48);
}
#[test]
fn a_tree_hands_back_what_was_put_into_it() {
let types = Types::new();
let int = types.int(IntKind::Int);
let mut tast = Tast::new();
let one = tast.add_const(Const::Int(1));
let left = tast.expr(Expr::new(ExprKind::Const(one), int, Category::Rvalue), Span::DUMMY);
let right = tast.expr(Expr::new(ExprKind::Const(one), int, Category::Rvalue), Span::DUMMY);
let sum = Expr::new(
ExprKind::Binary { op: BinaryOp::Add, lhs: left, rhs: right },
int,
Category::Rvalue,
);
let sum = tast.expr(sum, Span::new(0, 5));
assert_eq!(tast[left].ty, int);
assert_eq!(tast[sum].category, Category::Rvalue);
assert_eq!(tast.expr_span(sum), Span::new(0, 5));
assert_eq!(tast.counts().exprs, 3);
assert_eq!(tast[one], Const::Int(1));
}
#[test]
fn a_conversion_is_a_node_and_not_a_difference_between_two_types() {
let types = Types::new();
let char_type = types.int(IntKind::Char);
let int = types.int(IntKind::Int);
let mut tast = Tast::new();
let object = tast.decl(
Decl {
name: None,
ty: char_type,
kind: DeclKind::Object,
linkage: Linkage::None,
duration: StorageDuration::Automatic,
state: Definition::Defined,
alignment: None,
init: None,
params: DeclList::EMPTY,
body: None,
},
Span::DUMMY,
);
let name =
tast.expr(Expr::new(ExprKind::Decl(object), char_type, Category::Lvalue), Span::DUMMY);
let read = tast.expr(
Expr::new(
ExprKind::Convert { kind: Conversion::Lvalue, operand: name },
char_type,
Category::Rvalue,
),
Span::DUMMY,
);
let promoted = tast.expr(
Expr::new(
ExprKind::Convert { kind: Conversion::Arithmetic, operand: read },
int,
Category::Rvalue,
),
Span::DUMMY,
);
assert_eq!(tast[promoted].ty, int);
let ExprKind::Convert { kind, operand } = tast[promoted].kind else { panic!("a convert") };
assert_eq!(kind, Conversion::Arithmetic);
assert_eq!(tast[operand].ty, char_type);
}
#[test]
fn a_run_comes_back_as_a_slice() {
let types = Types::new();
let int = types.int(IntKind::Int);
let mut tast = Tast::new();
let zero = tast.add_const(Const::Int(0));
let args: Vec<ExprId> = (0..3)
.map(|_| {
tast.expr(Expr::new(ExprKind::Const(zero), int, Category::Rvalue), Span::DUMMY)
})
.collect();
let list = tast.add_expr_refs(&args);
assert_eq!(&tast[list], args.as_slice());
}
#[test]
fn a_label_is_made_before_it_is_defined_because_a_goto_may_come_first() {
let mut tast = Tast::new();
let mut names = rucc_base::Interner::new();
let name = names.intern("done");
let label = tast.add_label(Label { name, stmt: None });
let jump = tast.stmt(Stmt::Goto(label), Span::DUMMY);
let target = tast.stmt(Stmt::Empty, Span::DUMMY);
tast.define_label(label, target);
assert_eq!(tast[jump], Stmt::Goto(label));
assert_eq!(tast[label].stmt, Some(target));
}
}