use crate::capture::SourceRange;
use crate::lex::{CharLit, FloatLit, IntLit, StrLit};
#[derive(Clone, Debug, PartialEq)]
pub struct Spanned<T> {
pub node: T,
pub range: SourceRange,
}
impl<T> Spanned<T> {
pub fn new(node: T, range: SourceRange) -> Self {
Self { node, range }
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ident {
pub name: String,
pub range: SourceRange,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct TypeQualifiers {
pub is_const: bool,
pub is_volatile: bool,
pub is_restrict: bool,
pub is_atomic: bool,
}
impl TypeQualifiers {
pub const NONE: Self = Self {
is_const: false,
is_volatile: false,
is_restrict: false,
is_atomic: false,
};
pub fn any(self) -> bool {
self.is_const || self.is_volatile || self.is_restrict
}
pub fn merge(self, other: Self) -> Self {
Self {
is_const: self.is_const || other.is_const,
is_volatile: self.is_volatile || other.is_volatile,
is_restrict: self.is_restrict || other.is_restrict,
is_atomic: self.is_atomic || other.is_atomic,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Sign {
Signed,
Unsigned,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IntSize {
Short,
Int,
Long,
LongLong,
Int128,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FloatSize {
Float,
Double,
LongDouble,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RecordKind {
Struct,
Union,
}
impl RecordKind {
pub fn as_str(self) -> &'static str {
match self {
RecordKind::Struct => "struct",
RecordKind::Union => "union",
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ArraySize {
Unspecified,
Star,
Expr(Box<Expr>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Type {
pub kind: TypeKind,
pub qualifiers: TypeQualifiers,
pub range: SourceRange,
}
impl Type {
pub fn new(kind: TypeKind, qualifiers: TypeQualifiers, range: SourceRange) -> Self {
Self {
kind,
qualifiers,
range,
}
}
pub fn plain(kind: TypeKind, range: SourceRange) -> Self {
Self::new(kind, TypeQualifiers::NONE, range)
}
pub fn is_error(&self) -> bool {
matches!(self.kind, TypeKind::Error)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TypeKind {
Void,
Bool,
Char(Option<Sign>),
Int {
sign: Sign,
size: IntSize,
},
Float(FloatSize),
Complex(FloatSize),
Imaginary(FloatSize),
Pointer(Box<Type>),
Array {
elem: Box<Type>,
size: ArraySize,
qualifiers: TypeQualifiers,
is_static: bool,
},
Function(Box<FunctionType>),
Record(RecordSpecId),
Enum(EnumSpecId),
Typedef(Ident),
Typeof {
id: TypeofId,
unqual: bool,
},
Auto,
Error,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TypeofId(pub u32);
impl TypeofId {
pub fn index(self) -> usize {
self.0 as usize
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum TypeofOperand {
Expr(Expr),
Type(TypeName),
}
#[derive(Clone, Debug, PartialEq)]
pub struct FunctionType {
pub ret: Type,
pub params: Vec<ParamDecl>,
pub variadic: bool,
pub ellipsis: Option<SourceRange>,
pub has_prototype: bool,
pub kr_names: Vec<Ident>,
pub old_style: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ParamDecl {
pub specifiers: DeclSpecifiers,
pub name: Option<Ident>,
pub ty: Type,
pub range: SourceRange,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RecordSpecId(pub u32);
impl RecordSpecId {
pub fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct EnumSpecId(pub u32);
impl EnumSpecId {
pub fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct RecordSpec {
pub kind: RecordKind,
pub name: Option<Ident>,
pub fields: Option<Vec<FieldDecl>>,
pub asserts: Vec<StaticAssert>,
pub attrs: Attributes,
pub pack: Option<u32>,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub struct StaticAssert {
pub cond: Expr,
pub message: Option<String>,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub struct FieldDecl {
pub specifiers: DeclSpecifiers,
pub name: Option<Ident>,
pub ty: Type,
pub bit_width: Option<Expr>,
pub attrs: Attributes,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub struct EnumSpec {
pub name: Option<Ident>,
pub enumerators: Option<Vec<Enumerator>>,
pub underlying: Option<Type>,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Enumerator {
pub name: Ident,
pub value: Option<Expr>,
pub range: SourceRange,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StorageClass {
Typedef,
Extern,
Static,
Auto,
Register,
Constexpr,
}
impl StorageClass {
pub fn as_str(self) -> &'static str {
match self {
StorageClass::Typedef => "typedef",
StorageClass::Extern => "extern",
StorageClass::Static => "static",
StorageClass::Auto => "auto",
StorageClass::Register => "register",
StorageClass::Constexpr => "constexpr",
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Alignment {
pub kind: AlignmentKind,
pub from_attribute: bool,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub enum AlignmentKind {
Expr(Expr),
Type(Box<TypeName>),
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Attributes {
pub noreturn: Option<SourceRange>,
pub always_inline: Option<SourceRange>,
pub noinline: Option<SourceRange>,
pub cold: Option<SourceRange>,
pub deprecated: Option<Spanned<Option<String>>>,
pub packed: Option<SourceRange>,
pub aligned: Option<Alignment>,
pub section: Option<Spanned<String>>,
pub constructor: Option<SourceRange>,
pub destructor: Option<SourceRange>,
pub cleanup: Option<Cleanup>,
pub mode: Option<Spanned<String>>,
pub safe: Option<SourceRange>,
pub weak: Option<SourceRange>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Cleanup {
pub func: Option<Ident>,
pub range: SourceRange,
}
impl Attributes {
pub fn is_empty(&self) -> bool {
*self == Attributes::default()
}
pub fn merge(&mut self, other: Attributes) {
self.noreturn = self.noreturn.or(other.noreturn);
self.always_inline = self.always_inline.or(other.always_inline);
self.noinline = self.noinline.or(other.noinline);
self.cold = self.cold.or(other.cold);
self.deprecated = self.deprecated.take().or(other.deprecated);
self.packed = self.packed.or(other.packed);
self.aligned = self.aligned.take().or(other.aligned);
self.section = self.section.take().or(other.section);
self.constructor = self.constructor.or(other.constructor);
self.destructor = self.destructor.or(other.destructor);
self.cleanup = self.cleanup.take().or(other.cleanup);
self.mode = self.mode.take().or(other.mode);
self.safe = self.safe.or(other.safe);
self.weak = self.weak.or(other.weak);
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct DeclSpecifiers {
pub storage: Option<Spanned<StorageClass>>,
pub thread_local: Option<SourceRange>,
pub inline: bool,
pub noreturn: Option<SourceRange>,
pub alignas: Vec<Alignment>,
pub attrs: Attributes,
pub base: Type,
pub range: SourceRange,
}
impl DeclSpecifiers {
pub fn is_typedef(&self) -> bool {
matches!(
self.storage,
Some(Spanned {
node: StorageClass::Typedef,
..
})
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Decl {
pub specifiers: DeclSpecifiers,
pub declarators: Vec<InitDeclarator>,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub struct InitDeclarator {
pub name: Option<Ident>,
pub ty: Type,
pub init: Option<Initializer>,
pub attrs: Attributes,
pub asm_label: Option<Spanned<String>>,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Initializer {
pub kind: InitializerKind,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub enum InitializerKind {
Expr(Expr),
List(Vec<InitItem>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct InitItem {
pub designators: Vec<Designator>,
pub init: Initializer,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Designator {
Field(Ident),
Index(Expr),
Range(Expr, Expr),
}
#[derive(Clone, Debug, PartialEq)]
pub struct TypeName {
pub specifiers: DeclSpecifiers,
pub ty: Type,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Block {
pub items: Vec<BlockItem>,
pub local_labels: Vec<Ident>,
pub range: SourceRange,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum BlockItem {
Decl(Decl),
Stmt(Stmt),
StaticAssert(StaticAssert),
NestedFunction(FunctionDef),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Stmt {
pub kind: StmtKind,
pub range: SourceRange,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum StmtKind {
Labeled {
label: Ident,
body: Box<Stmt>,
},
Case {
value: Expr,
upper: Option<Expr>,
body: Box<Stmt>,
},
Default {
body: Box<Stmt>,
},
Compound(Block),
Expr(Option<Expr>),
If {
cond: Expr,
then_branch: Box<Stmt>,
else_branch: Option<Box<Stmt>>,
},
Switch {
cond: Expr,
body: Box<Stmt>,
},
While {
cond: Expr,
body: Box<Stmt>,
},
DoWhile {
body: Box<Stmt>,
cond: Expr,
},
For {
init: ForInit,
cond: Option<Expr>,
step: Option<Expr>,
body: Box<Stmt>,
},
Goto(Ident),
GotoPtr(Expr),
Continue,
Break,
Return(Option<Expr>),
Error,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ForInit {
None,
Expr(Expr),
Decl(Box<Decl>),
StaticAssert(StaticAssert),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UnaryOp {
Plus,
Minus,
LogNot,
BitNot,
Deref,
AddrOf,
}
impl UnaryOp {
pub fn as_str(self) -> &'static str {
match self {
UnaryOp::Plus => "+",
UnaryOp::Minus => "-",
UnaryOp::LogNot => "!",
UnaryOp::BitNot => "~",
UnaryOp::Deref => "*",
UnaryOp::AddrOf => "&",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Rem,
Shl,
Shr,
Lt,
Gt,
Le,
Ge,
Eq,
Ne,
BitAnd,
BitXor,
BitOr,
LogAnd,
LogOr,
}
impl BinaryOp {
pub fn as_str(self) -> &'static str {
use BinaryOp::*;
match self {
Add => "+",
Sub => "-",
Mul => "*",
Div => "/",
Rem => "%",
Shl => "<<",
Shr => ">>",
Lt => "<",
Gt => ">",
Le => "<=",
Ge => ">=",
Eq => "==",
Ne => "!=",
BitAnd => "&",
BitXor => "^",
BitOr => "|",
LogAnd => "&&",
LogOr => "||",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IncDec {
Inc,
Dec,
}
impl IncDec {
pub fn as_str(self) -> &'static str {
match self {
IncDec::Inc => "++",
IncDec::Dec => "--",
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Expr {
pub kind: ExprKind,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ExprKind {
Ident(Ident),
Int(IntLit),
Float(FloatLit),
Char(CharLit),
Str(StrLit),
Unary {
op: UnaryOp,
operand: Box<Expr>,
},
Binary {
op: BinaryOp,
lhs: Box<Expr>,
rhs: Box<Expr>,
},
Assign {
op: Option<BinaryOp>,
lhs: Box<Expr>,
rhs: Box<Expr>,
},
Conditional {
cond: Box<Expr>,
then_expr: Option<Box<Expr>>,
else_expr: Box<Expr>,
},
Comma {
lhs: Box<Expr>,
rhs: Box<Expr>,
},
Call {
callee: Box<Expr>,
args: Vec<Expr>,
},
Member {
base: Box<Expr>,
arrow: bool,
field: Ident,
},
Index {
base: Box<Expr>,
index: Box<Expr>,
},
PostIncDec {
op: IncDec,
operand: Box<Expr>,
},
PreIncDec {
op: IncDec,
operand: Box<Expr>,
},
Cast {
ty: Box<TypeName>,
expr: Box<Expr>,
},
LabelAddr(Ident),
SizeofExpr(Box<Expr>),
SizeofType(Box<TypeName>),
AlignofExpr(Box<Expr>),
AlignofType(Box<TypeName>),
Generic {
controlling: Box<Expr>,
assocs: Vec<GenericAssoc>,
},
Bool(bool),
Nullptr,
VaArg {
ap: Box<Expr>,
ty: Box<TypeName>,
},
OffsetOf {
ty: Box<TypeName>,
member: Ident,
path: Vec<Designator>,
},
CompoundLiteral {
ty: Box<TypeName>,
init: Vec<InitItem>,
},
StmtExpr(Box<Block>),
TypesCompatible {
lhs: Box<TypeName>,
rhs: Box<TypeName>,
},
ChooseExpr {
cond: Box<Expr>,
then_expr: Box<Expr>,
else_expr: Box<Expr>,
},
ComplexPart {
real: bool,
operand: Box<Expr>,
},
Error,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GenericAssoc {
pub ty: Option<TypeName>,
pub value: Expr,
pub range: SourceRange,
}
#[derive(Clone, Debug, PartialEq)]
pub struct FunctionDef {
pub specifiers: DeclSpecifiers,
pub name: Ident,
pub ty: Type,
pub kr_decls: Vec<Decl>,
pub attrs: Attributes,
pub asm_label: Option<Spanned<String>>,
pub body: Block,
pub uses_label_addrs: bool,
pub range: SourceRange,
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq)]
pub enum ExternalDecl {
Function(FunctionDef),
Decl(Decl),
StaticAssert(StaticAssert),
}
#[derive(Clone, Debug, PartialEq)]
pub struct TranslationUnit {
pub items: Vec<ExternalDecl>,
pub records: Vec<RecordSpec>,
pub enums: Vec<EnumSpec>,
pub typeofs: Vec<TypeofOperand>,
pub range: SourceRange,
}
impl TranslationUnit {
pub fn record(&self, id: RecordSpecId) -> &RecordSpec {
&self.records[id.index()]
}
pub fn enum_spec(&self, id: EnumSpecId) -> &EnumSpec {
&self.enums[id.index()]
}
pub fn typeof_operand(&self, id: TypeofId) -> &TypeofOperand {
&self.typeofs[id.index()]
}
}