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 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 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 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 fn handle_symbols(
195 &mut self,
196 node: &KdlNode,
197 namespace: &mut Namespace<'_>,
198 ) -> Result<(), Error> {
199 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 fn handle_consts(
222 &mut self,
223 node: &KdlNode,
224 namespace: &mut Namespace<'_>,
225 ) -> Result<(), Error> {
226 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(®ion)
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(®ion), 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}