use crate::parser::{Expression, FunctionDecl, Program, Statement};
use anyhow::{bail, Result};
pub fn validate_for_gpu(program: &Program) -> Result<()> {
for item in &program.items {
if let crate::parser::Item::Function { decl, .. } = item {
validate_function(decl)?;
}
}
Ok(())
}
fn validate_function(func: &FunctionDecl) -> Result<()> {
if has_recursion(func) {
bail!(
"Function '{}' contains recursion, which is not supported on GPU",
func.name
);
}
for stmt in &func.body {
validate_statement(stmt)?;
}
Ok(())
}
fn has_recursion(func: &FunctionDecl) -> bool {
for stmt in &func.body {
if statement_calls_function(stmt, &func.name) {
return true;
}
}
false
}
fn statement_calls_function(stmt: &Statement, func_name: &str) -> bool {
match stmt {
Statement::Expression { expr, .. } => expression_calls_function(expr, func_name),
Statement::Let { value, .. } => expression_calls_function(value, func_name),
Statement::Return {
value: Some(expr), ..
} => expression_calls_function(expr, func_name),
Statement::If {
condition,
then_block,
else_block,
..
} => {
expression_calls_function(condition, func_name)
|| then_block
.iter()
.any(|s| statement_calls_function(s, func_name))
|| else_block.as_ref().is_some_and(|block| {
block.iter().any(|s| statement_calls_function(s, func_name))
})
}
Statement::While {
condition, body, ..
} => {
expression_calls_function(condition, func_name)
|| body.iter().any(|s| statement_calls_function(s, func_name))
}
Statement::For { body, .. } => body.iter().any(|s| statement_calls_function(s, func_name)),
_ => false,
}
}
fn expression_calls_function(expr: &Expression, func_name: &str) -> bool {
match expr {
Expression::Call {
function,
arguments,
..
} => {
if let Expression::Identifier { name, .. } = &**function {
if name == func_name {
return true;
}
}
arguments
.iter()
.any(|(_, arg)| expression_calls_function(arg, func_name))
}
Expression::Binary { left, right, .. } => {
expression_calls_function(left, func_name)
|| expression_calls_function(right, func_name)
}
Expression::Unary { operand, .. } => expression_calls_function(operand, func_name),
Expression::Block { statements, .. } => statements
.iter()
.any(|s| statement_calls_function(s, func_name)),
_ => false,
}
}
fn validate_statement(stmt: &Statement) -> Result<()> {
match stmt {
Statement::While { .. } => {
Ok(())
}
Statement::For { .. } => {
Ok(())
}
_ => Ok(()),
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_recursion_detection() {
}
}