mod assignment;
mod binary;
mod call;
mod cast;
mod conditional;
mod field_access;
mod identifier;
mod initializer_list;
mod literal;
mod offsetof;
mod sizeof;
mod subscript;
mod unary;
mod update;
pub use assignment::Assignment;
pub use binary::BinaryExpression;
pub use call::{Argument, CallExpression};
pub use cast::CastExpression;
pub use conditional::ConditionalExpression;
pub use field_access::FieldAccessExpression;
pub use identifier::IdentifierExpression;
pub use initializer_list::{Designator, InitializerItem, InitializerListExpression};
pub use literal::{
BooleanExpression, CharLiteralExpression, CommentExpression, GenericExpression, NullExpression,
NumberLiteralExpression, StringLiteralExpression,
};
pub use offsetof::{OffsetOfExpression, StructField};
use serde::Serialize;
pub use sizeof::{SizeofExpression, SizeofOperand};
pub use subscript::SubscriptExpression;
pub use unary::UnaryExpression;
pub use update::UpdateExpression;
use crate::model::SourceLocation;
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Expression {
Call(CallExpression),
Assignment(Assignment),
Binary(BinaryExpression),
Unary(UnaryExpression),
Identifier(IdentifierExpression),
FieldAccess(FieldAccessExpression),
StringLiteral(StringLiteralExpression),
NumberLiteral(NumberLiteralExpression),
Null(NullExpression),
Boolean(BooleanExpression),
Cast(CastExpression),
Conditional(ConditionalExpression),
Sizeof(SizeofExpression),
Subscript(SubscriptExpression),
InitializerList(InitializerListExpression),
CharLiteral(CharLiteralExpression),
Update(UpdateExpression),
Comment(CommentExpression),
OffsetOf(OffsetOfExpression),
Generic(GenericExpression),
}
impl Expression {
pub fn location(&self) -> &SourceLocation {
match self {
Self::Call(c) => &c.location,
Self::Assignment(a) => &a.location,
Self::Binary(b) => &b.location,
Self::Unary(u) => &u.location,
Self::Identifier(i) => &i.location,
Self::FieldAccess(f) => &f.location,
Self::StringLiteral(s) => &s.location,
Self::NumberLiteral(n) => &n.location,
Self::Null(n) => &n.location,
Self::Boolean(b) => &b.location,
Self::Cast(c) => &c.location,
Self::Conditional(c) => &c.location,
Self::Sizeof(s) => &s.location,
Self::Subscript(s) => &s.location,
Self::InitializerList(i) => &i.location,
Self::CharLiteral(c) => &c.location,
Self::Update(u) => &u.location,
Self::Comment(c) => &c.location,
Self::OffsetOf(o) => &o.location,
Self::Generic(g) => &g.location,
}
}
pub fn to_source_string<'a>(&self, source: &'a [u8]) -> Option<&'a str> {
let loc = self.location();
std::str::from_utf8(&source[loc.start_byte..loc.end_byte]).ok()
}
pub fn walk<'s, F>(&'s self, f: &mut F)
where
F: FnMut(&'s Self),
{
f(self);
match self {
Self::Call(call) => {
call.function.walk(f);
for arg in &call.arguments {
let Argument::Expression(e) = arg;
e.walk(f);
}
}
Self::Assignment(assign) => {
assign.lhs.walk(f);
assign.rhs.walk(f);
}
Self::Unary(unary) => {
unary.operand.walk(f);
}
Self::Binary(binary) => {
binary.left.walk(f);
binary.right.walk(f);
}
Self::Cast(cast) => {
cast.operand.walk(f);
}
Self::Conditional(cond) => {
cond.condition.walk(f);
cond.then_expr.walk(f);
cond.else_expr.walk(f);
}
Self::Subscript(subscript) => {
subscript.array.walk(f);
subscript.index.walk(f);
}
Self::Update(update) => {
update.operand.walk(f);
}
Self::FieldAccess(field) => {
field.base.walk(f);
}
Self::InitializerList(init) => {
for item in &init.items {
if let Some(Designator::Subscript(idx)) = &item.designator {
idx.walk(f);
}
item.value.walk(f);
}
}
Self::Identifier(_)
| Self::StringLiteral(_)
| Self::NumberLiteral(_)
| Self::Null(_)
| Self::Boolean(_)
| Self::Sizeof(_)
| Self::CharLiteral(_)
| Self::Comment(_)
| Self::OffsetOf(_)
| Self::Generic(_) => {}
}
}
pub fn extract_variable_name<'a>(&self, source: &'a [u8]) -> Option<&'a str> {
match self {
Self::Identifier(_) | Self::FieldAccess(_) => self.location().as_str(source),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self, Self::Null(_)) || matches!(self, Self::Identifier(id) if id.name == "NULL")
}
pub fn is_zero(&self) -> bool {
matches!(self, Self::NumberLiteral(n) if n.value.trim() == "0")
}
pub fn is_string_literal(&self) -> bool {
matches!(self, Self::StringLiteral(_))
}
pub fn extract_string_value(&self) -> Option<String> {
match self {
Self::StringLiteral(lit) => Some(lit.value.trim_matches('"').to_string()),
_ => None,
}
}
pub fn is_string_or_macro_string(&self) -> bool {
self.extract_string_value().is_some()
}
pub fn contains_identifier(&self, name: &str) -> bool {
let mut found = false;
self.walk(&mut |e| {
if let Self::Identifier(id) = e
&& id.name == name
{
found = true;
}
});
found
}
pub fn collect_identifiers(&self) -> Vec<String> {
let mut identifiers = Vec::new();
self.walk(&mut |e| {
if let Self::Identifier(id) = e {
identifiers.push(id.name.clone());
}
});
identifiers
}
pub fn is_call_to(&self, function_name: &str) -> bool {
matches!(self, Self::Call(call) if call.is_function(function_name))
}
pub fn is_call_to_any(&self, function_names: &[&str]) -> bool {
matches!(self, Self::Call(call) if call.function_name_str().is_some_and(|name| function_names.contains(&name)))
}
}