mod break_stmt;
mod compound_stmt;
mod continue_stmt;
mod for_stmt;
mod goto_stmt;
mod if_stmt;
mod labeled_stmt;
mod return_stmt;
mod switch_stmt;
mod variable_decl;
mod while_stmt;
pub use break_stmt::BreakStatement;
pub use compound_stmt::CompoundStatement;
pub use continue_stmt::ContinueStatement;
pub use for_stmt::{ForInit, ForStatement};
pub use goto_stmt::GotoStatement;
pub use if_stmt::IfStatement;
pub use labeled_stmt::LabeledStatement;
pub use return_stmt::ReturnStatement;
use serde::Serialize;
pub use switch_stmt::{CaseLabel, SwitchCase, SwitchStatement};
pub use variable_decl::VariableDecl;
pub use while_stmt::{DoWhileStatement, WhileStatement};
use crate::model::{
Assignment, CallExpression, Comment, Expression, PreprocessorDirective, SourceLocation,
};
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Statement {
Declaration(Box<VariableDecl>),
Expression(Box<Expression>),
If(IfStatement),
Return(ReturnStatement),
Goto(GotoStatement),
Labeled(LabeledStatement),
Compound(CompoundStatement),
Switch(SwitchStatement),
For(Box<ForStatement>),
While(WhileStatement),
DoWhile(DoWhileStatement),
Break(BreakStatement),
Continue(ContinueStatement),
Preprocessor(PreprocessorDirective),
Comment(Comment),
}
impl Statement {
pub fn walk<'s, F>(&'s self, f: &mut F)
where
F: FnMut(&'s Self),
{
f(self);
match self {
Self::If(if_stmt) => {
for stmt in &if_stmt.then_body {
stmt.walk(f);
}
if let Some(else_body) = &if_stmt.else_body {
for stmt in else_body {
stmt.walk(f);
}
}
}
Self::Compound(compound) => {
for stmt in &compound.statements {
stmt.walk(f);
}
}
Self::Labeled(labeled) => {
labeled.statement.walk(f);
}
Self::Switch(switch) => {
for case in &switch.cases {
for stmt in &case.body {
stmt.walk(f);
}
}
}
Self::For(for_stmt) => {
for stmt in &for_stmt.body {
stmt.walk(f);
}
}
Self::While(while_stmt) => {
for stmt in &while_stmt.body {
stmt.walk(f);
}
}
Self::DoWhile(do_while) => {
for stmt in &do_while.body {
stmt.walk(f);
}
}
Self::Declaration(_)
| Self::Expression(_)
| Self::Return(_)
| Self::Goto(_)
| Self::Break(_)
| Self::Continue(_)
| Self::Preprocessor(_)
| Self::Comment(_) => {}
}
}
pub fn for_each_child_block<F>(&self, mut f: F)
where
F: FnMut(&[Self]),
{
match self {
Self::If(if_stmt) => {
f(&if_stmt.then_body);
if let Some(else_body) = &if_stmt.else_body {
f(else_body);
}
}
Self::Compound(c) => f(&c.statements),
Self::Labeled(l) => f(std::slice::from_ref(&l.statement)),
Self::For(for_stmt) => f(&for_stmt.body),
Self::While(w) => f(&w.body),
Self::DoWhile(d) => f(&d.body),
Self::Switch(sw) => {
for case in &sw.cases {
f(&case.body);
}
}
Self::Declaration(_)
| Self::Expression(_)
| Self::Return(_)
| Self::Goto(_)
| Self::Break(_)
| Self::Continue(_)
| Self::Preprocessor(_)
| Self::Comment(_) => {}
}
}
pub fn visit_expressions<'s>(&'s self, f: &mut impl FnMut(&'s Expression)) {
match self {
Self::Expression(expr_stmt) => f(expr_stmt),
Self::Return(ret) => {
if let Some(v) = &ret.value {
f(v);
}
}
Self::Declaration(decl) => {
if let Some(init) = &decl.initializer {
f(init);
}
if let Some(size) = &decl.array_size {
f(size);
}
}
Self::If(if_stmt) => f(&if_stmt.condition),
Self::Switch(switch) => {
f(&switch.condition);
for case in &switch.cases {
if let Some(label_expr) = &case.label.value {
f(label_expr);
}
}
}
Self::For(for_stmt) => {
match &for_stmt.initializer {
Some(ForInit::Expr(init)) => f(init),
Some(ForInit::Decl(decl)) => {
if let Some(init) = &decl.initializer {
f(init);
}
}
None => {}
}
if let Some(cond) = &for_stmt.condition {
f(cond);
}
if let Some(update) = &for_stmt.update {
f(update);
}
}
Self::While(while_stmt) => f(&while_stmt.condition),
Self::DoWhile(do_while) => f(&do_while.condition),
Self::Goto(_)
| Self::Labeled(_)
| Self::Compound(_)
| Self::Break(_)
| Self::Continue(_)
| Self::Preprocessor(_)
| Self::Comment(_) => {}
}
}
pub fn location(&self) -> &SourceLocation {
match self {
Self::Declaration(d) => &d.location,
Self::Expression(e) => e.location(),
Self::If(i) => &i.location,
Self::Return(r) => &r.location,
Self::Goto(g) => &g.location,
Self::Labeled(l) => &l.location,
Self::Compound(c) => &c.location,
Self::Switch(s) => &s.location,
Self::For(f) => &f.location,
Self::While(w) => &w.location,
Self::DoWhile(d) => &d.location,
Self::Break(b) => &b.location,
Self::Continue(c) => &c.location,
Self::Preprocessor(p) => p.location(),
Self::Comment(c) => &c.location,
}
}
pub fn walk_expressions<'s, F>(&'s self, f: &mut F)
where
F: FnMut(&'s Expression),
{
self.walk(&mut |s| {
s.visit_expressions(f);
});
}
pub fn iter_switches<'s>(&'s self) -> impl Iterator<Item = &'s SwitchStatement> + 's {
let mut results: Vec<&'s SwitchStatement> = Vec::new();
self.walk(&mut |s| {
if let Self::Switch(sw) = s {
results.push(sw);
}
});
results.into_iter()
}
pub fn iter_if_statements<'s>(&'s self) -> impl Iterator<Item = &'s IfStatement> + 's {
let mut results: Vec<&'s IfStatement> = Vec::new();
self.walk(&mut |s| {
if let Self::If(if_stmt) = s {
results.push(if_stmt);
}
});
results.into_iter()
}
pub fn iter_declarations<'s>(&'s self) -> impl Iterator<Item = &'s VariableDecl> + 's {
let mut results: Vec<&'s VariableDecl> = Vec::new();
self.walk(&mut |s| {
if let Self::Declaration(decl) = s {
results.push(decl);
}
if let Self::For(for_stmt) = s
&& let Some(ForInit::Decl(decl)) = &for_stmt.initializer
{
results.push(decl);
}
});
results.into_iter()
}
pub fn iter_returns<'s>(&'s self) -> impl Iterator<Item = &'s ReturnStatement> + 's {
let mut results: Vec<&'s ReturnStatement> = Vec::new();
self.walk(&mut |s| {
if let Self::Return(ret) = s {
results.push(ret);
}
});
results.into_iter()
}
pub fn iter_assignments<'s>(&'s self) -> impl Iterator<Item = &'s Assignment> + 's {
let mut results: Vec<&'s Assignment> = Vec::new();
self.walk(&mut |s| {
if let Self::Expression(expr_stmt) = s
&& let Expression::Assignment(assign) = expr_stmt.as_ref()
{
results.push(assign);
}
});
results.into_iter()
}
pub fn iter_calls<'s>(&'s self) -> impl Iterator<Item = &'s CallExpression> + 's {
let mut exprs: Vec<&'s Expression> = Vec::new();
self.walk_expressions(&mut |expr| exprs.push(expr));
let mut results: Vec<&'s CallExpression> = Vec::new();
for expr in exprs {
expr.walk(&mut |e| {
if let Expression::Call(call) = e {
results.push(call);
}
});
}
results.into_iter()
}
pub fn extract_call(&self) -> Option<&CallExpression> {
if let Self::Expression(expr_stmt) = self
&& let Expression::Call(call) = expr_stmt.as_ref()
{
return Some(call);
}
None
}
pub fn is_assignment_to<F>(&self, target_var: &str, value_check: F) -> bool
where
F: Fn(&Expression) -> bool,
{
if let Self::Expression(expr_stmt) = self
&& let Expression::Assignment(assign) = expr_stmt.as_ref()
{
let lhs_text = assign.lhs.location().as_str().unwrap_or("");
return lhs_text.trim() == target_var.trim() && value_check(&assign.rhs);
}
false
}
pub fn extract_assignment(&self) -> Option<&Assignment> {
if let Self::Expression(expr_stmt) = self
&& let Expression::Assignment(assign) = expr_stmt.as_ref()
{
return Some(assign);
}
None
}
pub fn is_null_assignment_to(&self, var_name: &str) -> bool {
self.is_assignment_to(var_name, Expression::is_null)
}
fn non_comments(stmts: &[Self]) -> Vec<&Self> {
stmts
.iter()
.filter(|s| !matches!(s, Self::Comment(_)))
.collect()
}
pub fn for_each_pair<F>(statements: &[Self], mut f: F)
where
F: FnMut(&Self, &Self),
{
let nc = Self::non_comments(statements);
for w in nc.windows(2) {
f(w[0], w[1]);
}
}
pub fn for_each_triple<F>(statements: &[Self], mut f: F)
where
F: FnMut(&Self, &Self, &Self),
{
let nc = Self::non_comments(statements);
for w in nc.windows(3) {
f(w[0], w[1], w[2]);
}
}
pub fn walk_pairs<F>(stmts: &[Self], f: &mut F)
where
F: FnMut(&Self, &Self),
{
let nc = Self::non_comments(stmts);
for w in nc.windows(2) {
f(w[0], w[1]);
}
for stmt in stmts {
match stmt {
Self::If(if_stmt) => {
Self::walk_pairs(&if_stmt.then_body, f);
if let Some(else_body) = &if_stmt.else_body {
Self::walk_pairs(else_body, f);
}
}
Self::Compound(c) => Self::walk_pairs(&c.statements, f),
Self::Labeled(l) => Self::walk_pairs(std::slice::from_ref(&l.statement), f),
Self::For(for_stmt) => Self::walk_pairs(&for_stmt.body, f),
Self::While(w) => Self::walk_pairs(&w.body, f),
Self::DoWhile(d) => Self::walk_pairs(&d.body, f),
Self::Switch(sw) => {
for case in &sw.cases {
Self::walk_pairs(&case.body, f);
}
}
Self::Declaration(_)
| Self::Expression(_)
| Self::Return(_)
| Self::Goto(_)
| Self::Break(_)
| Self::Continue(_)
| Self::Preprocessor(_)
| Self::Comment(_) => {}
}
}
}
pub fn walk_triples<F>(stmts: &[Self], f: &mut F)
where
F: FnMut(&Self, &Self, &Self),
{
let nc = Self::non_comments(stmts);
for w in nc.windows(3) {
f(w[0], w[1], w[2]);
}
for stmt in stmts {
match stmt {
Self::If(if_stmt) => {
Self::walk_triples(&if_stmt.then_body, f);
if let Some(else_body) = &if_stmt.else_body {
Self::walk_triples(else_body, f);
}
}
Self::Compound(c) => Self::walk_triples(&c.statements, f),
Self::Labeled(l) => Self::walk_triples(std::slice::from_ref(&l.statement), f),
Self::For(for_stmt) => Self::walk_triples(&for_stmt.body, f),
Self::While(w) => Self::walk_triples(&w.body, f),
Self::DoWhile(d) => Self::walk_triples(&d.body, f),
Self::Switch(sw) => {
for case in &sw.cases {
Self::walk_triples(&case.body, f);
}
}
Self::Declaration(_)
| Self::Expression(_)
| Self::Return(_)
| Self::Goto(_)
| Self::Break(_)
| Self::Continue(_)
| Self::Preprocessor(_)
| Self::Comment(_) => {}
}
}
}
}