Skip to main content

chipi_core/backend/
cpp.rs

1//! C++ decoder backend.
2//!
3//! Generates a single-header C++ decoder/disassembler from a validated
4//! `.chipi` definition.
5
6use crate::codegen_cpp;
7use crate::config::GenTarget;
8use crate::tree;
9use crate::types::ValidatedDef;
10
11use super::{CodegenBackend, CodegenError};
12
13/// The C++ code generation backend.
14pub struct CppBackend;
15
16/// Parsed C++-specific configuration from `lang_options`.
17pub struct CppOptions {
18    /// C++ namespace for generated code. Defaults to decoder name in snake_case.
19    pub namespace: Option<String>,
20    /// Include guard style: "pragma" (default) or "ifndef".
21    pub guard_style: GuardStyle,
22    /// Additional `#include` directives for user-provided type headers.
23    pub includes: Vec<String>,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum GuardStyle {
28    Pragma,
29    Ifndef,
30}
31
32impl CodegenBackend for CppBackend {
33    fn lang(&self) -> &str {
34        "cpp"
35    }
36
37    fn validate_lang_options(&self, options: &toml::Value) -> Result<(), Vec<String>> {
38        let table = match options {
39            toml::Value::Table(t) => t,
40            _ => return Ok(()),
41        };
42
43        let known_keys = ["namespace", "guard_style", "includes"];
44        let mut errors = Vec::new();
45        for key in table.keys() {
46            if !known_keys.contains(&key.as_str()) {
47                errors.push(format!("unknown lang_option: {}", key));
48            }
49        }
50
51        if errors.is_empty() {
52            Ok(())
53        } else {
54            Err(errors)
55        }
56    }
57
58    fn generate(&self, ir: &ValidatedDef, config: &GenTarget) -> Result<String, CodegenError> {
59        let tree = tree::build_tree(ir);
60        let opts = parse_cpp_options(config);
61        Ok(codegen_cpp::generate_cpp_code(
62            ir,
63            &tree,
64            &opts,
65            &config.type_map,
66        ))
67    }
68
69    fn formatter_command(&self) -> Option<&[&str]> {
70        Some(&["clang-format", "-i"])
71    }
72}
73
74fn parse_cpp_options(config: &GenTarget) -> CppOptions {
75    let table = match &config.lang_options {
76        Some(toml::Value::Table(t)) => Some(t.clone()),
77        _ => None,
78    };
79
80    let namespace = table
81        .as_ref()
82        .and_then(|t| t.get("namespace"))
83        .and_then(|v| v.as_str())
84        .map(|s| s.to_string());
85
86    let guard_style = table
87        .as_ref()
88        .and_then(|t| t.get("guard_style"))
89        .and_then(|v| v.as_str())
90        .map(|s| match s {
91            "ifndef" => GuardStyle::Ifndef,
92            _ => GuardStyle::Pragma,
93        })
94        .unwrap_or(GuardStyle::Pragma);
95
96    let includes = table
97        .as_ref()
98        .and_then(|t| t.get("includes"))
99        .and_then(|v| v.as_array())
100        .map(|arr| {
101            arr.iter()
102                .filter_map(|v| v.as_str().map(|s| s.to_string()))
103                .collect()
104        })
105        .unwrap_or_default();
106
107    CppOptions {
108        namespace,
109        guard_style,
110        includes,
111    }
112}