Skip to main content

bubbles/compiler/
validate.rs

1//! Compile-time validation of cross-node references.
2
3use crate::compiler::ast::Stmt;
4use crate::compiler::program::Program;
5use crate::error::{DialogueError, Result};
6
7/// Validates all jump and detour targets in `program` refer to existing nodes.
8///
9/// # Errors
10/// Returns [`DialogueError::Validation`] on the first broken reference found.
11pub fn validate(program: &Program) -> Result<()> {
12    for variants in program.nodes.values() {
13        for node in variants {
14            validate_stmts(node.body.as_ref(), program)?;
15        }
16    }
17    Ok(())
18}
19
20fn validate_stmts(stmts: &[Stmt], program: &Program) -> Result<()> {
21    for stmt in stmts {
22        match stmt {
23            Stmt::Jump(target) | Stmt::Detour(target) if !program.node_exists(target) => {
24                return Err(DialogueError::Validation(format!(
25                    "reference to unknown node '{target}'"
26                )));
27            }
28            Stmt::If {
29                branches,
30                else_body,
31            } => {
32                for b in branches {
33                    validate_stmts(&b.body, program)?;
34                }
35                validate_stmts(else_body, program)?;
36            }
37            Stmt::Once {
38                body, else_body, ..
39            } => {
40                validate_stmts(body, program)?;
41                validate_stmts(else_body, program)?;
42            }
43            Stmt::Options(items) => {
44                for item in items {
45                    validate_stmts(&item.body, program)?;
46                }
47            }
48            _ => {}
49        }
50    }
51    Ok(())
52}
53
54#[cfg(test)]
55#[path = "validate_tests.rs"]
56mod tests;