use std::sync::atomic::{AtomicU64, Ordering};
use crate::intern::InternedStr;
use crate::source::SourceLocation;
use crate::token::Comment;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ExprId(pub u64);
static EXPR_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
impl ExprId {
pub fn next() -> Self {
Self(EXPR_ID_COUNTER.fetch_add(1, Ordering::Relaxed))
}
pub const INVALID: Self = Self(0);
pub fn is_valid(&self) -> bool {
self.0 != 0
}
}
impl std::fmt::Display for ExprId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ExprId({})", self.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MacroInvocation {
pub name: InternedStr,
pub call_loc: SourceLocation,
pub args: Option<Vec<String>>,
}
impl MacroInvocation {
pub fn new(name: InternedStr, call_loc: SourceLocation) -> Self {
Self {
name,
call_loc,
args: None,
}
}
pub fn with_args(name: InternedStr, call_loc: SourceLocation, args: Vec<String>) -> Self {
Self {
name,
call_loc,
args: Some(args),
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct MacroExpansionInfo {
pub chain: Vec<MacroInvocation>,
}
impl MacroExpansionInfo {
pub fn new() -> Self {
Self { chain: Vec::new() }
}
pub fn is_empty(&self) -> bool {
self.chain.is_empty()
}
pub fn push(&mut self, invocation: MacroInvocation) {
self.chain.push(invocation);
}
pub fn innermost(&self) -> Option<&MacroInvocation> {
self.chain.last()
}
pub fn outermost(&self) -> Option<&MacroInvocation> {
self.chain.first()
}
pub fn len(&self) -> usize {
self.chain.len()
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct NodeInfo {
pub loc: SourceLocation,
pub macro_expansion: Option<Box<MacroExpansionInfo>>,
}
impl NodeInfo {
pub fn new(loc: SourceLocation) -> Self {
Self {
loc,
macro_expansion: None,
}
}
pub fn with_macro_info(loc: SourceLocation, macro_info: MacroExpansionInfo) -> Self {
Self {
loc,
macro_expansion: if macro_info.is_empty() {
None
} else {
Some(Box::new(macro_info))
},
}
}
pub fn is_from_macro(&self) -> bool {
self.macro_expansion.is_some()
}
pub fn macro_info(&self) -> Option<&MacroExpansionInfo> {
self.macro_expansion.as_deref()
}
}
#[derive(Debug, Clone)]
pub struct TranslationUnit {
pub decls: Vec<ExternalDecl>,
}
#[derive(Debug, Clone)]
pub enum ExternalDecl {
FunctionDef(FunctionDef),
Declaration(Declaration),
}
impl ExternalDecl {
pub fn is_target(&self) -> bool {
match self {
ExternalDecl::FunctionDef(f) => f.is_target,
ExternalDecl::Declaration(d) => d.is_target,
}
}
}
#[derive(Debug, Clone)]
pub struct FunctionDef {
pub specs: DeclSpecs,
pub declarator: Declarator,
pub body: CompoundStmt,
pub info: NodeInfo,
pub comments: Vec<Comment>,
pub is_target: bool,
pub function_call_count: usize,
pub deref_count: usize,
}
impl FunctionDef {
pub fn loc(&self) -> &SourceLocation {
&self.info.loc
}
}
#[derive(Debug, Clone)]
pub struct Declaration {
pub specs: DeclSpecs,
pub declarators: Vec<InitDeclarator>,
pub info: NodeInfo,
pub comments: Vec<Comment>,
pub is_target: bool,
}
impl Declaration {
pub fn loc(&self) -> &SourceLocation {
&self.info.loc
}
}
#[derive(Debug, Clone, Default)]
pub struct DeclSpecs {
pub storage: Option<StorageClass>,
pub type_specs: Vec<TypeSpec>,
pub qualifiers: TypeQualifiers,
pub is_inline: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StorageClass {
Typedef,
Extern,
Static,
Auto,
Register,
}
#[derive(Debug, Clone)]
pub enum TypeSpec {
Void,
Char,
Short,
Int,
Long,
Float,
Double,
Signed,
Unsigned,
Bool,
Complex,
Float16,
Float32,
Float64,
Float128,
Float32x,
Float64x,
Int128,
TypeofExpr(Box<Expr>),
Struct(StructSpec),
Union(StructSpec),
Enum(EnumSpec),
TypedefName(InternedStr),
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct TypeQualifiers {
pub is_const: bool,
pub is_volatile: bool,
pub is_restrict: bool,
pub is_atomic: bool,
}
impl TypeQualifiers {
pub fn is_empty(&self) -> bool {
!self.is_const && !self.is_volatile && !self.is_restrict && !self.is_atomic
}
}
#[derive(Debug, Clone)]
pub struct StructSpec {
pub name: Option<InternedStr>,
pub members: Option<Vec<StructMember>>,
pub loc: SourceLocation,
}
#[derive(Debug, Clone)]
pub struct StructMember {
pub specs: DeclSpecs,
pub declarators: Vec<StructDeclarator>,
}
#[derive(Debug, Clone)]
pub struct StructDeclarator {
pub declarator: Option<Declarator>,
pub bitfield: Option<Box<Expr>>,
}
#[derive(Debug, Clone)]
pub struct EnumSpec {
pub name: Option<InternedStr>,
pub enumerators: Option<Vec<Enumerator>>,
pub loc: SourceLocation,
}
#[derive(Debug, Clone)]
pub struct Enumerator {
pub name: InternedStr,
pub value: Option<Box<Expr>>,
pub loc: SourceLocation,
}
#[derive(Debug, Clone)]
pub struct ParamDecl {
pub specs: DeclSpecs,
pub declarator: Option<Declarator>,
pub loc: SourceLocation,
}
#[derive(Debug, Clone)]
pub struct ParamList {
pub params: Vec<ParamDecl>,
pub is_variadic: bool,
}
#[derive(Debug, Clone)]
pub struct InitDeclarator {
pub declarator: Declarator,
pub init: Option<Initializer>,
}
#[derive(Debug, Clone)]
pub struct Declarator {
pub name: Option<InternedStr>,
pub derived: Vec<DerivedDecl>,
pub loc: SourceLocation,
}
#[derive(Debug, Clone)]
pub enum DerivedDecl {
Pointer(TypeQualifiers),
Array(ArrayDecl),
Function(ParamList),
}
#[derive(Debug, Clone)]
pub struct ArrayDecl {
pub size: Option<Box<Expr>>,
pub qualifiers: TypeQualifiers,
pub is_static: bool,
pub is_vla: bool,
}
#[derive(Debug, Clone)]
pub enum Initializer {
Expr(Box<Expr>),
List(Vec<InitializerItem>),
}
#[derive(Debug, Clone)]
pub struct InitializerItem {
pub designation: Vec<Designator>,
pub init: Initializer,
}
#[derive(Debug, Clone)]
pub enum Designator {
Index(Box<Expr>),
Member(InternedStr),
}
#[derive(Debug, Clone)]
pub enum Stmt {
Compound(CompoundStmt),
Expr(Option<Box<Expr>>, SourceLocation),
If {
cond: Box<Expr>,
then_stmt: Box<Stmt>,
else_stmt: Option<Box<Stmt>>,
loc: SourceLocation,
},
Switch {
expr: Box<Expr>,
body: Box<Stmt>,
loc: SourceLocation,
},
While {
cond: Box<Expr>,
body: Box<Stmt>,
loc: SourceLocation,
},
DoWhile {
body: Box<Stmt>,
cond: Box<Expr>,
loc: SourceLocation,
},
For {
init: Option<ForInit>,
cond: Option<Box<Expr>>,
step: Option<Box<Expr>>,
body: Box<Stmt>,
loc: SourceLocation,
},
Goto(InternedStr, SourceLocation),
Continue(SourceLocation),
Break(SourceLocation),
Return(Option<Box<Expr>>, SourceLocation),
Label {
name: InternedStr,
stmt: Box<Stmt>,
loc: SourceLocation,
},
Case {
expr: Box<Expr>,
stmt: Box<Stmt>,
loc: SourceLocation,
},
Default {
stmt: Box<Stmt>,
loc: SourceLocation,
},
Asm {
loc: SourceLocation,
},
}
#[derive(Debug, Clone)]
pub enum ForInit {
Expr(Box<Expr>),
Decl(Declaration),
}
#[derive(Debug, Clone)]
pub struct CompoundStmt {
pub items: Vec<BlockItem>,
pub info: NodeInfo,
}
impl CompoundStmt {
pub fn loc(&self) -> &SourceLocation {
&self.info.loc
}
}
#[derive(Debug, Clone)]
pub enum BlockItem {
Decl(Declaration),
Stmt(Stmt),
}
#[derive(Debug, Clone)]
pub enum BuiltinArg {
Expr(Box<Expr>),
TypeName(Box<TypeName>),
}
#[derive(Debug, Clone)]
pub enum ExprKind {
Ident(InternedStr),
IntLit(i64),
UIntLit(u64),
FloatLit(f64),
CharLit(u8),
StringLit(Vec<u8>),
Index {
expr: Box<Expr>,
index: Box<Expr>,
},
Call {
func: Box<Expr>,
args: Vec<Expr>,
},
Member {
expr: Box<Expr>,
member: InternedStr,
},
PtrMember {
expr: Box<Expr>,
member: InternedStr,
},
PostInc(Box<Expr>),
PostDec(Box<Expr>),
CompoundLit {
type_name: Box<TypeName>,
init: Vec<InitializerItem>,
},
PreInc(Box<Expr>),
PreDec(Box<Expr>),
AddrOf(Box<Expr>),
Deref(Box<Expr>),
UnaryPlus(Box<Expr>),
UnaryMinus(Box<Expr>),
BitNot(Box<Expr>),
LogNot(Box<Expr>),
Sizeof(Box<Expr>),
SizeofType(Box<TypeName>),
Alignof(Box<TypeName>),
Cast {
type_name: Box<TypeName>,
expr: Box<Expr>,
},
Binary {
op: BinOp,
lhs: Box<Expr>,
rhs: Box<Expr>,
},
Conditional {
cond: Box<Expr>,
then_expr: Box<Expr>,
else_expr: Box<Expr>,
},
Assign {
op: AssignOp,
lhs: Box<Expr>,
rhs: Box<Expr>,
},
Comma {
lhs: Box<Expr>,
rhs: Box<Expr>,
},
StmtExpr(CompoundStmt),
Assert {
kind: AssertKind,
condition: Box<Expr>,
},
MacroCall {
name: InternedStr,
args: Vec<Expr>,
expanded: Box<Expr>,
call_loc: SourceLocation,
},
BuiltinCall {
name: InternedStr,
args: Vec<BuiltinArg>,
},
}
#[derive(Debug, Clone)]
pub struct Expr {
pub id: ExprId,
pub kind: ExprKind,
pub loc: SourceLocation,
}
impl Expr {
pub fn new(kind: ExprKind, loc: SourceLocation) -> Self {
Self {
id: ExprId::next(),
kind,
loc,
}
}
pub fn loc(&self) -> &SourceLocation {
&self.loc
}
}
#[derive(Debug, Clone)]
pub struct TypeName {
pub specs: DeclSpecs,
pub declarator: Option<AbstractDeclarator>,
}
#[derive(Debug, Clone)]
pub struct AbstractDeclarator {
pub derived: Vec<DerivedDecl>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinOp {
Mul,
Div,
Mod,
Add,
Sub,
Shl,
Shr,
Lt,
Gt,
Le,
Ge,
Eq,
Ne,
BitAnd,
BitXor,
BitOr,
LogAnd,
LogOr,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AssignOp {
Assign,
MulAssign,
DivAssign,
ModAssign,
AddAssign,
SubAssign,
ShlAssign,
ShrAssign,
AndAssign,
XorAssign,
OrAssign,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AssertKind {
Assert,
AssertUnderscore,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::intern::StringInterner;
#[test]
fn test_type_qualifiers_is_empty() {
let empty = TypeQualifiers::default();
assert!(empty.is_empty());
let with_const = TypeQualifiers {
is_const: true,
..Default::default()
};
assert!(!with_const.is_empty());
}
#[test]
fn test_macro_invocation_new() {
let mut interner = StringInterner::new();
let name = interner.intern("FOO");
let loc = SourceLocation::default();
let inv = MacroInvocation::new(name, loc.clone());
assert_eq!(inv.name, name);
assert_eq!(inv.call_loc, loc);
assert!(inv.args.is_none());
}
#[test]
fn test_macro_invocation_with_args() {
let mut interner = StringInterner::new();
let name = interner.intern("ADD");
let loc = SourceLocation::default();
let args = vec!["a".to_string(), "b".to_string()];
let inv = MacroInvocation::with_args(name, loc.clone(), args.clone());
assert_eq!(inv.name, name);
assert_eq!(inv.call_loc, loc);
assert_eq!(inv.args, Some(args));
}
#[test]
fn test_macro_expansion_info_new() {
let info = MacroExpansionInfo::new();
assert!(info.is_empty());
assert_eq!(info.len(), 0);
assert!(info.innermost().is_none());
assert!(info.outermost().is_none());
}
#[test]
fn test_macro_expansion_info_push() {
let mut interner = StringInterner::new();
let mut info = MacroExpansionInfo::new();
let inv1 = MacroInvocation::new(interner.intern("FOO"), SourceLocation::default());
let inv2 = MacroInvocation::new(interner.intern("BAR"), SourceLocation::default());
info.push(inv1.clone());
assert_eq!(info.len(), 1);
assert!(!info.is_empty());
info.push(inv2.clone());
assert_eq!(info.len(), 2);
assert_eq!(info.outermost().unwrap().name, inv1.name);
assert_eq!(info.innermost().unwrap().name, inv2.name);
}
#[test]
fn test_macro_expansion_info_chain() {
let mut interner = StringInterner::new();
let mut info = MacroExpansionInfo::new();
info.push(MacroInvocation::new(interner.intern("A"), SourceLocation::default()));
info.push(MacroInvocation::new(interner.intern("B"), SourceLocation::default()));
info.push(MacroInvocation::new(interner.intern("C"), SourceLocation::default()));
assert_eq!(info.chain.len(), 3);
assert_eq!(interner.get(info.chain[0].name), "A");
assert_eq!(interner.get(info.chain[1].name), "B");
assert_eq!(interner.get(info.chain[2].name), "C");
}
#[test]
fn test_node_info_new() {
let loc = SourceLocation::new(crate::source::FileId::default(), 10, 5);
let info = NodeInfo::new(loc.clone());
assert_eq!(info.loc, loc);
assert!(!info.is_from_macro());
assert!(info.macro_info().is_none());
}
#[test]
fn test_node_info_with_empty_macro_info() {
let loc = SourceLocation::default();
let macro_info = MacroExpansionInfo::new();
let info = NodeInfo::with_macro_info(loc.clone(), macro_info);
assert!(!info.is_from_macro());
assert!(info.macro_info().is_none());
}
#[test]
fn test_node_info_with_macro_info() {
let mut interner = StringInterner::new();
let loc = SourceLocation::default();
let mut macro_info = MacroExpansionInfo::new();
macro_info.push(MacroInvocation::new(interner.intern("FOO"), SourceLocation::default()));
let info = NodeInfo::with_macro_info(loc.clone(), macro_info);
assert!(info.is_from_macro());
assert!(info.macro_info().is_some());
assert_eq!(info.macro_info().unwrap().len(), 1);
}
#[test]
fn test_node_info_default() {
let info = NodeInfo::default();
assert_eq!(info.loc, SourceLocation::default());
assert!(!info.is_from_macro());
}
}