memory_spec/
lib.rs

1use std::{
2    collections::HashMap,
3    fmt::{Display, Write as _},
4    str::FromStr,
5};
6mod expr;
7pub mod region;
8use kdl::{KdlDocument, KdlError, KdlNode, KdlValue};
9pub use region::Region;
10
11use crate::expr::{EvalError, Namespace, Value};
12
13#[derive(Clone, Debug)]
14pub struct Regions {
15    region: Region,
16    subregions: HashMap<String, Regions>,
17}
18
19impl Regions {
20    fn new(region: Region) -> Self {
21        Self {
22            region,
23            subregions: Default::default(),
24        }
25    }
26
27    pub fn region(&self) -> &Region {
28        &self.region
29    }
30
31    pub fn origin(&self) -> u64 {
32        self.region.origin()
33    }
34
35    pub fn length(&self) -> u64 {
36        self.region.length()
37    }
38
39    pub fn end(&self) -> u64 {
40        self.region.end()
41    }
42
43    pub fn get(&self, key: &str) -> Option<&Regions> {
44        self.subregions.get(key)
45    }
46}
47
48impl std::ops::Index<&str> for Regions {
49    type Output = Regions;
50
51    fn index(&self, index: &str) -> &Self::Output {
52        &self.subregions[index]
53    }
54}
55
56#[derive(Copy, Clone, Debug, Default)]
57pub enum ConstantFormat {
58    #[default]
59    Decimal,
60    Binary,
61    Octal,
62    Hex,
63}
64
65impl ConstantFormat {
66    fn from_str(s: &str, path: impl FnOnce() -> String) -> Result<Self, Error> {
67        Ok(match s {
68            "decimal" => Self::Decimal,
69            "binary" => Self::Binary,
70            "octal" => Self::Octal,
71            "hex" => Self::Hex,
72            _ => {
73                return Err(Error::InvalidConstantFormat {
74                    path: path(),
75                    input: s.to_owned(),
76                });
77            }
78        })
79    }
80
81    fn str_variants() -> &'static [&'static str] {
82        &["decimal", "binary", "octal", "hex"]
83    }
84}
85
86#[derive(Clone, Debug)]
87pub struct Constant {
88    ty: String,
89    value: i64,
90    format: ConstantFormat,
91}
92
93#[derive(Clone, Default, Debug)]
94pub struct MemorySpec {
95    regions: HashMap<String, Regions>,
96    symbols: HashMap<String, u64>,
97    symbols_key_order: Vec<String>,
98    consts: HashMap<String, Constant>,
99    consts_key_order: Vec<String>,
100}
101
102impl FromStr for MemorySpec {
103    type Err = Error;
104
105    fn from_str(content: &str) -> Result<Self, Error> {
106        let doc: KdlDocument = content.parse()?;
107        let mut namespace = Namespace::default();
108        let mut spec = Self::default();
109        for node in doc.nodes() {
110            match node.name().value() {
111                "vars" => spec.handle_vars(node, &mut namespace)?,
112                "regions" => spec.handle_regions(node, &mut namespace)?,
113                "symbols" => spec.handle_symbols(node, &mut namespace)?,
114                "consts" => spec.handle_consts(node, &mut namespace)?,
115                _ => (),
116            }
117        }
118        Ok(spec)
119    }
120}
121
122impl MemorySpec {
123    pub fn regions(&self) -> &HashMap<String, Regions> {
124        &self.regions
125    }
126
127    /// Render out all symbols into a string that can be included in a linker script.
128    pub fn render_symbols(&self) -> String {
129        let mut r = String::new();
130        for name in &self.symbols_key_order {
131            writeln!(&mut r, "{name} = 0x{:08x};", self.symbols[name]).unwrap();
132        }
133        r
134    }
135
136    /// Render out all consts into a string that can be included in a rust source file.
137    pub fn render_consts(&self) -> String {
138        use ConstantFormat as F;
139        let mut r = String::new();
140        let mut value_formatted = String::new();
141        for name in &self.consts_key_order {
142            let Constant { ty, value, format } = &self.consts[name];
143            value_formatted.clear();
144            match format {
145                F::Decimal => write!(&mut value_formatted, "{value}").unwrap(),
146                F::Binary => write!(&mut value_formatted, "{value:b}").unwrap(),
147                F::Octal => write!(&mut value_formatted, "{value:o}").unwrap(),
148                F::Hex => write!(&mut value_formatted, "{value:x}").unwrap(),
149            }
150            let (prefix, chunk_size) = match format {
151                F::Decimal => ("", 3),
152                F::Binary => ("0b", 8),
153                F::Octal => ("0o", 4),
154                F::Hex => ("0x", 4),
155            };
156            if value_formatted.len() > chunk_size {
157                value_formatted = value_formatted
158                    .as_bytes()
159                    .rchunks(chunk_size)
160                    .rev()
161                    .map(|b| unsafe { str::from_utf8_unchecked(b) })
162                    .fold(prefix.to_owned(), |a, b| {
163                        if a.len() == prefix.len() {
164                            a + b
165                        } else {
166                            a + "_" + b
167                        }
168                    });
169            } else {
170                value_formatted.insert_str(0, prefix);
171            }
172            writeln!(&mut r, "pub const {name}: {ty} = {value_formatted};").unwrap();
173        }
174        r
175    }
176
177    fn handle_vars(&self, node: &KdlNode, namespace: &mut Namespace<'_>) -> Result<(), Error> {
178        // TODO warn/reject params
179        for child in node
180            .children()
181            .ok_or_else(|| Error::InvalidNode("vars".into()))?
182            .nodes()
183        {
184            let name = child.name().value();
185            let path = || format!("vars.{}", name);
186            let value = child.get(0).ok_or_else(|| Error::InvalidNode(path()))?;
187            let value = eval_kdl_value(value, namespace, path)?;
188            namespace.insert(name.into(), expr::Value::N(value));
189        }
190        Ok(())
191    }
192
193    // basically the same as handle_vars but add the entries to the symbols table instead
194    fn handle_symbols(
195        &mut self,
196        node: &KdlNode,
197        namespace: &mut Namespace<'_>,
198    ) -> Result<(), Error> {
199        // TODO warn/reject params
200        for child in node
201            .children()
202            .ok_or_else(|| Error::InvalidNode("vars".into()))?
203            .nodes()
204        {
205            let name = child.name().value();
206            let path = || format!("symbols.{}", name);
207            let value = child.get(0).ok_or_else(|| Error::InvalidNode(path()))?;
208            let value: i64 = eval_kdl_value(value, namespace, path)?;
209
210            let value = u64::try_from(value).map_err(|_| Error::InvalidNode(path()))?;
211            let prev = self.symbols.insert(name.into(), value);
212            if prev.is_some() {
213                return Err(Error::NameExists(path()));
214            }
215            self.symbols_key_order.push(name.into());
216        }
217        Ok(())
218    }
219
220    // basically the same as handle_vars but add the entries to the consts table instead
221    fn handle_consts(
222        &mut self,
223        node: &KdlNode,
224        namespace: &mut Namespace<'_>,
225    ) -> Result<(), Error> {
226        // TODO warn/reject params
227        for child in node
228            .children()
229            .ok_or_else(|| Error::InvalidNode("vars".into()))?
230            .nodes()
231        {
232            let name = child.name().value();
233            let path = || format!("symbols.{}", name);
234            let ty = child.get(0).ok_or_else(|| Error::InvalidNode(path()))?;
235            let ty = ty
236                .as_string()
237                .ok_or_else(|| Error::InvalidNode(path()))?
238                .to_owned();
239            let value = child.get(1).ok_or_else(|| Error::InvalidNode(path()))?;
240            let value: i64 = eval_kdl_value(value, namespace, path)?;
241            let format = match child.get("format") {
242                None => ConstantFormat::default(),
243                Some(s) => {
244                    let s = s.as_string().ok_or_else(|| Error::InvalidConstantFormat {
245                        path: path(),
246                        input: s.to_string(),
247                    })?;
248                    ConstantFormat::from_str(s, path)?
249                }
250            };
251
252            let prev = self
253                .consts
254                .insert(name.into(), Constant { ty, value, format });
255            if prev.is_some() {
256                return Err(Error::NameExists(path()));
257            }
258            self.consts_key_order.push(name.into());
259        }
260        Ok(())
261    }
262
263    fn handle_regions(
264        &mut self,
265        node: &KdlNode,
266        namespace: &mut Namespace<'_>,
267    ) -> Result<(), Error> {
268        let mut subregions = vec![];
269        for child in node
270            .children()
271            .ok_or_else(|| Error::InvalidNode("regions".into()))?
272            .nodes()
273        {
274            let r = self.handle_region(child, namespace, None, &mut vec![])?;
275            subregions.push((r, String::from(child.name().value())));
276        }
277        check_overlap(subregions, &[])?;
278        Ok(())
279    }
280
281    fn handle_region(
282        &mut self,
283        node: &KdlNode,
284        namespace: &mut Namespace<'_>,
285        parent_region: Option<&Region>,
286        path: &mut Vec<String>,
287    ) -> Result<Region, Error> {
288        let name = node.name().value();
289        let path_str = || format!("regions.{}.{}", path.join("."), name);
290        let origin = node
291            .get("origin")
292            .map(|v| eval_kdl_value(v, namespace, path_str))
293            .transpose()?;
294        let length = node
295            .get("length")
296            .map(|v| eval_kdl_value(v, namespace, path_str))
297            .transpose()?;
298        let end = node
299            .get("end")
300            .map(|v| eval_kdl_value(v, namespace, path_str))
301            .transpose()?;
302        let align = node
303            .get("align")
304            .map(|v| eval_kdl_value(v, namespace, path_str))
305            .transpose()?;
306        let origin = origin
307            .map(u64::try_from)
308            .transpose()
309            .map_err(|_| Error::InvalidNode(path_str()))?;
310        let length = length
311            .map(u64::try_from)
312            .transpose()
313            .map_err(|_| Error::InvalidNode(path_str()))?;
314        let end = end
315            .map(u64::try_from)
316            .transpose()
317            .map_err(|_| Error::InvalidNode(path_str()))?;
318        let align = align
319            .map(u64::try_from)
320            .transpose()
321            .map_err(|_| Error::InvalidNode(path_str()))?;
322
323        let region =
324            Region::new(origin, length, end).map_err(|_| Error::InvalidNode(path_str()))?;
325        if let Some(align) = align
326            && (region.origin() % align != 0 || region.end() % align != 0)
327        {
328            return Err(Error::AlignError(path_str()));
329        }
330        if let Some(parent_region) = parent_region
331            && !parent_region.contains(&region)
332        {
333            return Err(Error::SubregionError {
334                outer: format!("regions.{}", path.join(".")),
335                inner: name.into(),
336            });
337        }
338
339        path.push(name.into());
340        self.add_region(region, path);
341        add_value(namespace, path, Value::Namespace(Namespace::default()))?;
342        path.push("origin".into());
343        add_value(
344            namespace,
345            path,
346            Value::N(i64::try_from(region.origin()).unwrap()),
347        )?;
348        path.last_mut().unwrap().replace_range(.., "length");
349        add_value(
350            namespace,
351            path,
352            Value::N(i64::try_from(region.length()).unwrap()),
353        )?;
354        path.last_mut().unwrap().replace_range(.., "end");
355        add_value(
356            namespace,
357            path,
358            Value::N(i64::try_from(region.end()).unwrap()),
359        )?;
360        path.pop();
361
362        let children = node.children().map(|d| d.nodes());
363        if let Some(children) = children {
364            let mut subregions = vec![];
365            for child in children {
366                let subregion = self.handle_region(child, namespace, Some(&region), path)?;
367                let name = child.name().value();
368                subregions.push((subregion, String::from(name)));
369            }
370            check_overlap(subregions, path)?;
371        }
372        path.pop();
373
374        Ok(region)
375    }
376
377    fn add_region(&mut self, region: Region, path: &[String]) {
378        let mut map = &mut self.regions;
379        for name in &path[..path.len() - 1] {
380            map = &mut map.get_mut(name).unwrap().subregions;
381        }
382        map.insert(path.last().unwrap().clone(), Regions::new(region));
383    }
384}
385
386fn eval_kdl_value(
387    value: &KdlValue,
388    namespace: &Namespace<'_>,
389    path: impl Fn() -> String,
390) -> Result<i64, Error> {
391    match value {
392        KdlValue::Integer(n) => Ok(i64::try_from(*n).map_err(|_| Error::InvalidValue(path()))?),
393        KdlValue::String(ex) => Ok(expr::eval(ex, namespace)?),
394        _ => Err(Error::InvalidValue(path())),
395    }
396}
397
398fn check_overlap(mut regions: Vec<(Region, String)>, path: &[String]) -> Result<(), Error> {
399    let path_str = || {
400        if path.is_empty() {
401            "regions".into()
402        } else {
403            format!("regions.{}", path.join("."))
404        }
405    };
406    regions.sort_unstable();
407    for w in regions.windows(2) {
408        let ((l, lname), (r, rname)) = (&w[0], &w[1]);
409        if l.overlaps(r) {
410            return Err(Error::OverlapError {
411                parent_region: path_str(),
412                region1: lname.clone(),
413                region2: rname.clone(),
414            });
415        }
416    }
417    Ok(())
418}
419
420fn add_value<'a>(
421    namespace: &mut Namespace<'a>,
422    path: &[String],
423    value: expr::Value<'a>,
424) -> Result<(), Error> {
425    match path {
426        [k] => {
427            let r = namespace.insert(k.clone(), value);
428            if r.is_some() {
429                Err(Error::NameExists(k.clone()))
430            } else {
431                Ok(())
432            }
433        }
434        [k, tail @ ..] => add_value(
435            namespace.get_mut(k).unwrap().namespace_mut().unwrap(),
436            tail,
437            value,
438        ),
439        [] => unreachable!(),
440    }
441}
442
443#[derive(Debug, Clone, PartialEq, Eq)]
444pub enum Error {
445    KdlError(KdlError),
446    EvalError(EvalError),
447    AlignError(String),
448    InvalidNode(String),
449    InvalidValue(String),
450    NameExists(String),
451    OverlapError {
452        parent_region: String,
453        region1: String,
454        region2: String,
455    },
456    SubregionError {
457        outer: String,
458        inner: String,
459    },
460    InvalidConstantFormat {
461        path: String,
462        input: String,
463    },
464}
465
466impl Display for Error {
467    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468        match self {
469            Self::KdlError(e) => e.fmt(f),
470            Self::EvalError(e) => e.fmt(f),
471            Self::AlignError(r) => write!(f, "{} is not aligned", r),
472            Self::InvalidNode(n) => write!(f, "invalid node name {}", n),
473            Self::InvalidValue(n) => write!(f, "invalid value in {}", n),
474            Self::NameExists(n) => write!(f, "{} already exists", n),
475            Self::OverlapError {
476                parent_region,
477                region1,
478                region2,
479            } => write!(f, "{parent_region}: {region1} overlaps with {region2}"),
480            Self::SubregionError { outer, inner } => {
481                write!(f, "{} is not contained by {}", inner, outer)
482            }
483            Self::InvalidConstantFormat { path, input } => {
484                write!(
485                    f,
486                    "in {path}: \"{input}\" is not a known format, expected one of {:?}",
487                    ConstantFormat::str_variants(),
488                )
489            }
490        }
491    }
492}
493
494impl From<KdlError> for Error {
495    fn from(t: KdlError) -> Self {
496        Self::KdlError(t)
497    }
498}
499
500impl From<EvalError> for Error {
501    fn from(t: EvalError) -> Self {
502        Self::EvalError(t)
503    }
504}
505
506#[cfg(test)]
507mod tests {
508    use super::*;
509
510    #[test]
511    fn regions_test() {
512        let content = r#"regions {
513            region1 origin=0 end=2
514            region2 origin=2 end=3
515        }"#;
516
517        let r = MemorySpec::from_str(content).unwrap();
518    }
519
520    #[test]
521    fn overlap_test() {
522        let content = r#"regions {
523            region1 origin=0 end=2
524            region2 origin=1 end=3
525        }"#;
526
527        let _err = MemorySpec::from_str(content).unwrap_err();
528    }
529
530    #[test]
531    fn subregion_test() {
532        let content = r#"regions {
533            region1 origin=0 end=2 {
534                region2 origin=1 end=3
535            }
536        }"#;
537        let _err = MemorySpec::from_str(content).unwrap_err();
538    }
539}