#[derive(Debug, Clone, Eq)]
pub enum Type {
Void,
Char,
UChar,
Short,
UShort,
Int,
Long,
UInt,
ULong,
Float,
Double,
Pointer(Box<Type>),
Array(Box<Type>, usize),
Struct {
tag: String,
members: Vec<MemberDecl>,
is_union: bool,
},
VaList,
Function {
return_type: Box<Type>,
param_types: Option<Vec<Type>>,
is_variadic: bool,
},
}
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Type::Void, Type::Void) => true,
(Type::Char, Type::Char) => true,
(Type::UChar, Type::UChar) => true,
(Type::Short, Type::Short) => true,
(Type::UShort, Type::UShort) => true,
(Type::Int, Type::Int) => true,
(Type::Long, Type::Long) => true,
(Type::UInt, Type::UInt) => true,
(Type::ULong, Type::ULong) => true,
(Type::Float, Type::Float) => true,
(Type::Double, Type::Double) => true,
(Type::VaList, Type::VaList) => true,
(Type::Pointer(a), Type::Pointer(b)) => a == b,
(Type::Array(a, sa), Type::Array(b, sb)) => a == b && sa == sb,
(Type::Struct { tag: ta, .. }, Type::Struct { tag: tb, .. }) => ta == tb, (
Type::Function {
return_type: ra,
param_types: pa,
is_variadic: va,
},
Type::Function {
return_type: rb,
param_types: pb,
is_variadic: vb,
},
) => ra == rb && pa == pb && va == vb,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemberDecl {
pub name: String,
pub member_type: Type,
}
impl Type {
pub fn is_void(&self) -> bool {
matches!(self, Type::Void)
}
pub fn is_struct(&self) -> bool {
matches!(self, Type::Struct { .. })
}
pub fn is_incomplete(&self) -> bool {
match self {
Type::Void => true,
Type::Struct { members, .. } => members.is_empty(),
_ => false,
}
}
pub fn is_object_type(&self) -> bool {
!matches!(self, Type::Void | Type::Function { .. }) && !self.is_incomplete()
}
pub fn is_va_list(&self) -> bool {
matches!(self, Type::VaList)
}
pub fn is_unsigned(&self) -> bool {
matches!(self, Type::UInt | Type::ULong | Type::UChar | Type::UShort)
}
pub fn is_character(&self) -> bool {
matches!(self, Type::Char | Type::UChar)
}
pub fn is_short(&self) -> bool {
matches!(self, Type::Short | Type::UShort)
}
pub fn is_float(&self) -> bool {
matches!(self, Type::Float)
}
pub fn is_double(&self) -> bool {
matches!(self, Type::Double)
}
pub fn is_floating(&self) -> bool {
matches!(self, Type::Float | Type::Double)
}
pub fn is_pointer(&self) -> bool {
matches!(self, Type::Pointer(_))
}
pub fn is_array(&self) -> bool {
matches!(self, Type::Array(_, _))
}
pub fn target_type(&self) -> Option<&Type> {
match self {
Type::Pointer(t) | Type::Array(t, _) => Some(t),
_ => None,
}
}
pub fn size(&self) -> usize {
match self {
Type::Void => panic!("void has no size"),
Type::Function { .. } => panic!("function type has no size"),
Type::Char | Type::UChar => 1,
Type::Short | Type::UShort => 2,
Type::Int | Type::UInt | Type::Float => 4,
Type::Long | Type::ULong | Type::Double | Type::Pointer(_) => 8,
Type::Array(elem, count) => elem.size() * count,
Type::Struct {
members,
tag,
is_union,
} => {
if members.is_empty() {
panic!("incomplete struct '{}' has no size", tag);
}
if *is_union {
union_size(members)
} else {
struct_size(members)
}
}
Type::VaList => 24,
}
}
pub fn alignment(&self) -> usize {
match self {
Type::Void => panic!("void has no alignment"),
Type::Function { .. } => panic!("function type has no alignment"),
Type::Char | Type::UChar => 1,
Type::Short | Type::UShort => 2,
Type::Int | Type::UInt | Type::Float => 4,
Type::Long | Type::ULong | Type::Double | Type::Pointer(_) => 8,
Type::Array(elem, _) => elem.alignment(),
Type::Struct { members, tag, .. } => {
if members.is_empty() {
panic!("incomplete struct '{}' has no alignment", tag);
}
struct_alignment(members)
}
Type::VaList => 8,
}
}
}
pub fn struct_alignment(members: &[MemberDecl]) -> usize {
members
.iter()
.map(|m| m.member_type.alignment())
.max()
.unwrap_or(1)
}
pub fn struct_size(members: &[MemberDecl]) -> usize {
let mut offset = 0;
for m in members {
let align = m.member_type.alignment();
if offset % align != 0 {
offset += align - (offset % align);
}
offset += m.member_type.size();
}
let struct_align = struct_alignment(members);
if offset % struct_align != 0 {
offset += struct_align - (offset % struct_align);
}
offset
}
pub fn union_size(members: &[MemberDecl]) -> usize {
let max_member = members
.iter()
.map(|m| m.member_type.size())
.max()
.unwrap_or(0);
let align = struct_alignment(members);
if max_member % align != 0 {
max_member + align - (max_member % align)
} else {
max_member
}
}
pub fn struct_member_offset_ex(
members: &[MemberDecl],
name: &str,
is_union: bool,
) -> Option<(usize, Type)> {
if is_union {
for m in members {
if m.name == name {
return Some((0, m.member_type.clone()));
}
}
None
} else {
let mut offset = 0;
for m in members {
let align = m.member_type.alignment();
if offset % align != 0 {
offset += align - (offset % align);
}
if m.name == name {
return Some((offset, m.member_type.clone()));
}
offset += m.member_type.size();
}
None
}
}
pub fn has_fam(members: &[MemberDecl]) -> bool {
members
.last()
.is_some_and(|m| matches!(&m.member_type, Type::Array(_, 0)))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StorageClass {
Static,
Extern,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TopLevelDecl {
Function(FunctionDecl),
Variable(Declaration),
Typedef { name: String, underlying_type: Type },
}
#[derive(Debug, Clone, PartialEq)]
pub struct Program {
pub declarations: Vec<TopLevelDecl>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionDecl {
pub name: String,
pub return_type: Type,
pub params: Vec<(Type, String)>,
pub body: Option<Vec<BlockItem>>,
pub storage_class: Option<StorageClass>,
pub is_variadic: bool,
pub has_prototype: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BlockItem {
Statement(Statement),
Declaration(Declaration),
Typedef { name: String, underlying_type: Type },
}
#[derive(Debug, Clone, PartialEq)]
pub struct Declaration {
pub name: String,
pub var_type: Type,
pub init: Option<Expr>,
pub storage_class: Option<StorageClass>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
Return(Option<Expr>),
Expression(Expr),
Null,
If {
condition: Expr,
then_branch: Box<Statement>,
else_branch: Option<Box<Statement>>,
},
Compound(Vec<BlockItem>),
While {
condition: Expr,
body: Box<Statement>,
},
DoWhile {
body: Box<Statement>,
condition: Expr,
},
For {
init: Box<ForInit>,
condition: Option<Expr>,
post: Option<Expr>,
body: Box<Statement>,
},
Break,
Continue,
Switch { expr: Expr, body: Box<Statement> },
Case { value: i64, body: Box<Statement> },
Default(Box<Statement>),
Goto(String),
Label { name: String, body: Box<Statement> },
}
#[derive(Debug, Clone, PartialEq)]
pub enum ForInit {
Declaration(Vec<Declaration>),
Expression(Option<Expr>),
}
#[derive(Debug, Clone, PartialEq)]
#[allow(clippy::enum_variant_names)]
pub enum Expr {
Constant(i64),
ConstantLong(i64),
ConstantUInt(u64),
ConstantULong(u64),
ConstantDouble(f64),
ConstantFloat(f32),
Cast {
target_type: Type,
source_type: Type,
expr: Box<Expr>,
},
Unary(UnaryOp, Box<Expr>),
Binary(BinaryOp, Box<Expr>, Box<Expr>),
Var(String),
Assign(Box<Expr>, Box<Expr>),
Conditional {
condition: Box<Expr>,
then_expr: Box<Expr>,
else_expr: Box<Expr>,
},
CompoundAssign(BinaryOp, Box<Expr>, Box<Expr>),
PostfixIncrement(Box<Expr>),
PostfixDecrement(Box<Expr>),
FunctionCall(String, Vec<Expr>),
CallExpr(Box<Expr>, Vec<Expr>),
Dereref(Box<Expr>),
AddrOf(Box<Expr>),
SizeOfType(Type),
SizeOfExpr(Box<Expr>),
StringLiteral(String),
Dot(Box<Expr>, String),
CompoundInit(Vec<Expr>),
CompoundLiteral { target_type: Type, init: Box<Expr> },
VaStart(Box<Expr>),
VaArg { ap: Box<Expr>, arg_type: Type },
VaEnd(Box<Expr>),
VaCopy(Box<Expr>, Box<Expr>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryOp {
Negate,
Complement,
Not,
PreIncrement,
PreDecrement,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryOp {
Add,
Subtract,
Multiply,
Divide,
Remainder,
LessThan,
LessEqual,
GreaterThan,
GreaterEqual,
Equal,
NotEqual,
LogicalAnd,
LogicalOr,
BitwiseAnd,
BitwiseOr,
BitwiseXor,
ShiftLeft,
ShiftRight,
Comma,
}