Skip to main content

chipi_core/
backend.rs

1//! Code generation backends.
2//!
3//! Each backend implements [`CodegenBackend`] to emit source code for a
4//! specific target language. Only the Rust backend exists today; the trait
5//! is the extensibility point for future languages.
6
7pub mod binja;
8pub mod cpp;
9pub mod ida;
10pub mod rust;
11
12use crate::config::GenTarget;
13use crate::types::ValidatedDef;
14
15/// Operand classification shared across backends.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum OperandKind {
18    Register,
19    Immediate,
20    Address,
21    Memory,
22}
23
24/// Flow analysis configuration shared across backends.
25#[derive(Debug, Clone, Default)]
26pub struct FlowConfig {
27    pub calls: Vec<String>,
28    pub branches: Vec<String>,
29    pub unconditional_branches: Vec<String>,
30    pub returns: Vec<String>,
31    pub stops: Vec<String>,
32}
33
34/// Trait for language-specific code generation backends.
35pub trait CodegenBackend {
36    /// Language identifier (e.g., `"rust"`).
37    fn lang(&self) -> &str;
38
39    /// Validate language-specific options from the `lang_options` config field.
40    /// Returns errors for unknown or invalid keys.
41    fn validate_lang_options(&self, options: &toml::Value) -> Result<(), Vec<String>>;
42
43    /// Generate source code from the decoder IR and configuration.
44    fn generate(&self, ir: &ValidatedDef, config: &GenTarget) -> Result<String, CodegenError>;
45
46    /// Optional: command to format the generated source.
47    fn formatter_command(&self) -> Option<&[&str]>;
48}
49
50#[derive(Debug)]
51pub enum CodegenError {
52    Internal(String),
53}
54
55impl std::fmt::Display for CodegenError {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        match self {
58            CodegenError::Internal(msg) => write!(f, "codegen error: {}", msg),
59        }
60    }
61}
62
63impl std::error::Error for CodegenError {}
64
65/// Get a backend by language name.
66pub fn get_backend(lang: &str) -> Option<Box<dyn CodegenBackend>> {
67    match lang {
68        "rust" => Some(Box::new(rust::RustBackend)),
69        "ida" => Some(Box::new(ida::IdaBackend)),
70        "binja" => Some(Box::new(binja::BinjaBackend)),
71        "cpp" => Some(Box::new(cpp::CppBackend)),
72        _ => None,
73    }
74}
75
76/// Run a formatter command on a file. Warns on failure; does not error.
77pub fn run_formatter(cmd: &[&str], path: &str) {
78    let status = std::process::Command::new(cmd[0])
79        .args(&cmd[1..])
80        .arg(path)
81        .status();
82
83    match status {
84        Ok(s) if s.success() => {}
85        Ok(s) => eprintln!("warning: formatter exited with {}", s),
86        Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
87            eprintln!("warning: formatter '{}' not found, skipping", cmd[0]);
88        }
89        Err(e) => eprintln!("warning: failed to run formatter: {}", e),
90    }
91}