core_isa_parser/
lib.rs

1//! Parse the core-isa.h headers from Espressif's xtensa-overlays repository.
2//!
3//! <https://github.com/espressif/xtensa-overlays>
4
5use std::{collections::HashMap, env, fs, path::PathBuf, str::FromStr};
6
7use anyhow::Result;
8use enum_as_inner::EnumAsInner;
9use regex::Regex;
10use strum_macros::{Display, EnumIter, EnumString};
11
12/// The chips which are present in the xtensa-overlays repository
13///
14/// When `.to_string()` is called on a variant, the resulting string is the path
15/// to the chip's corresponding directory.
16#[derive(Debug, Clone, Copy, PartialEq, Display, EnumIter)]
17pub enum Chip {
18    #[strum(to_string = "xtensa_esp32")]
19    Esp32,
20    #[strum(to_string = "xtensa_esp32s2")]
21    Esp32s2,
22    #[strum(to_string = "xtensa_esp32s3")]
23    Esp32s3,
24    #[strum(to_string = "xtensa_lx106")]
25    Esp8266,
26}
27
28impl Chip {
29    fn core_isa_path(&self) -> Result<PathBuf> {
30        let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
31            .join("xtensa-overlays")
32            .join(self.to_string())
33            .join("newlib/newlib/libc/sys/xtensa/include/xtensa/config/core-isa.h")
34            .canonicalize()?;
35
36        Ok(path)
37    }
38}
39
40/// The valid interrupt types declared in the `core-isa.h` headers
41#[derive(Debug, Clone, Copy, PartialEq, EnumString)]
42pub enum InterruptType {
43    #[strum(serialize = "XTHAL_INTTYPE_EXTERN_EDGE")]
44    ExternEdge,
45    #[strum(serialize = "XTHAL_INTTYPE_EXTERN_LEVEL")]
46    ExternLevel,
47    #[strum(serialize = "XTHAL_INTTYPE_NMI")]
48    Nmi,
49    #[strum(serialize = "XTHAL_INTTYPE_PROFILING")]
50    Profiling,
51    #[strum(serialize = "XTHAL_INTTYPE_SOFTWARE")]
52    Software,
53    #[strum(serialize = "XTHAL_INTTYPE_TIMER")]
54    Timer,
55    #[strum(serialize = "XTHAL_TIMER_UNCONFIGURED")]
56    TimerUnconfigured,
57}
58
59/// The allowable value types for definitions
60#[derive(Debug, Clone, PartialEq, EnumAsInner)]
61pub enum Value {
62    Integer(i64),
63    Interrupt(InterruptType),
64    String(String),
65}
66
67/// Parse the configuration for the given chip
68///
69/// Returns a hashmap with the definition identifiers as keys and the
70/// corresponding parsed values as values.
71pub fn get_config(chip: Chip) -> Result<HashMap<String, Value>> {
72    let re_define = Regex::new(r"^#define[\s]+([a-zA-Z\d_]+)[\s]+([^\s]+)")?;
73    let re_ident = Regex::new(r"^[a-zA-Z\d_]+$")?;
74    let re_string = Regex::new(r#""([^"]+)""#)?;
75
76    // Iterate through each line containing a definition. Attempt to match the
77    // various components and map identifiers to values.
78    let mut map: HashMap<String, Value> = HashMap::new();
79    for define in find_all_defines(chip)? {
80        if !re_define.is_match(&define) {
81            println!("Define not matched: {}", define);
82            continue;
83        }
84
85        let captures = re_define.captures(&define).unwrap();
86        let identifier = captures.get(1).unwrap().as_str().to_string();
87        let value = captures.get(2).unwrap().as_str().to_string();
88
89        let value = if let Ok(integer) = value.parse::<i64>() {
90            // Decimal integer literal
91            Value::Integer(integer)
92        } else if let Ok(integer) = i64::from_str_radix(&value.replace("0x", ""), 16) {
93            // Hexadecimal integer literal
94            Value::Integer(integer)
95        } else if let Ok(interrupt) = InterruptType::from_str(&value) {
96            // Interrupt type
97            Value::Interrupt(interrupt)
98        } else if re_string.is_match(&value) {
99            // String
100            Value::String(value.replace("\"", ""))
101        } else if re_ident.is_match(&value) && map.contains_key(&value) {
102            // Identifier
103            map.get(&value).unwrap().to_owned()
104        } else {
105            // We will handle this particular case after, so no need to report it.
106            if chip != Chip::Esp32 && identifier != "XCHAL_USE_MEMCTL" {
107                println!("Unable to process definition: {} = {}", identifier, value);
108            }
109            continue;
110        };
111
112        map.insert(identifier, value);
113    }
114
115    Ok(map)
116}
117
118fn find_all_defines(chip: Chip) -> Result<Vec<String>> {
119    let path = chip.core_isa_path()?;
120    let lines = fs::read_to_string(path)?
121        .lines()
122        .filter_map(|line| {
123            if line.starts_with("#define") {
124                Some(line.to_string())
125            } else {
126                None
127            }
128        })
129        .collect::<Vec<_>>();
130
131    Ok(lines)
132}