From 43272fa04d85ee4b6f82e3b2f9fb61aca8027f4b Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Mon, 30 Dec 2024 23:13:12 +0300 Subject: [PATCH] derive field & allow spec in _derive header --- .github/workflows/ci.yaml | 2 +- CHANGELOG-rust.md | 2 + src/patch/iterators.rs | 2 +- src/patch/mod.rs | 13 ++++++ src/patch/peripheral.rs | 96 +++++++++++++++++++-------------------- src/patch/register.rs | 46 +++++++++++++++++++ 6 files changed, 109 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bab0f527..78f3701f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,7 +5,7 @@ env: on: push: - branches: master + branches: [master] pull_request: merge_group: diff --git a/CHANGELOG-rust.md b/CHANGELOG-rust.md index f800b836..d2ca338c 100644 --- a/CHANGELOG-rust.md +++ b/CHANGELOG-rust.md @@ -5,6 +5,8 @@ This changelog tracks the Rust `svdtools` project. See ## [Unreleased] +* `_derive` field + ## [v0.3.20] 2024-11-14 * Implement `_expand_cluster` diff --git a/src/patch/iterators.rs b/src/patch/iterators.rs index 0acb0146..0697662d 100644 --- a/src/patch/iterators.rs +++ b/src/patch/iterators.rs @@ -11,7 +11,7 @@ where spec: &'b str, } -impl<'b, I> Iterator for MatchIter<'b, I> +impl Iterator for MatchIter<'_, I> where I: Iterator, I::Item: Name, diff --git a/src/patch/mod.rs b/src/patch/mod.rs index 36eae12c..2a3a0ed5 100644 --- a/src/patch/mod.rs +++ b/src/patch/mod.rs @@ -1,5 +1,7 @@ pub mod patch_cli; +use once_cell::sync::Lazy; +use regex::Regex; use std::borrow::Cow; use std::fs::File; use std::io::{Read, Write}; @@ -821,6 +823,17 @@ impl Spec for str { } } +pub(crate) fn check_dimable_name(name: &str) -> Result<()> { + static PATTERN: Lazy = Lazy::new(|| { + Regex::new("^(((%s)|(%s)[_A-Za-z]{1}[_A-Za-z0-9]*)|([_A-Za-z]{1}[_A-Za-z0-9]*(\\[%s\\])?)|([_A-Za-z]{1}[_A-Za-z0-9]*(%s)?[_A-Za-z0-9]*))$").unwrap() + }); + if PATTERN.is_match(name) { + Ok(()) + } else { + Err(anyhow!("`{name}` is incorrect name")) + } +} + fn opt_interpolate(path: &Option<&T>, s: Option<&str>) -> Option { if let Some(path) = path { path.interpolate_opt(s) diff --git a/src/patch/peripheral.rs b/src/patch/peripheral.rs index a5f8cd4a..2935aa4b 100644 --- a/src/patch/peripheral.rs +++ b/src/patch/peripheral.rs @@ -128,9 +128,6 @@ pub(crate) trait RegisterBlockExt: Name { /// Get register by name fn get_reg(&self, name: &str) -> Option<&Register>; - /// Get mutable register by name - fn get_mut_reg(&mut self, name: &str) -> Option<&mut Register>; - /// Register/cluster block #[allow(unused)] fn children(&self) -> Option<&Vec>; @@ -249,7 +246,7 @@ pub(crate) trait RegisterBlockExt: Name { /// Remove fields from rname and mark it as derivedFrom rderive. /// Update all derivedFrom referencing rname - fn derive_register(&mut self, rname: &str, rderive: &Yaml, bpath: &BlockPath) -> PatchResult { + fn derive_register(&mut self, rspec: &str, rderive: &Yaml, bpath: &BlockPath) -> PatchResult { let (rderive, info) = if let Some(rderive) = rderive.as_str() { ( rderive, @@ -257,14 +254,14 @@ pub(crate) trait RegisterBlockExt: Name { ) } else if let Some(hash) = rderive.as_hash() { let rderive = hash.get_str("_from")?.ok_or_else(|| { - anyhow!("derive: source register not given, please add a _from field to {rname}") + anyhow!("derive: source register not given, please add a _from field to {rspec}") })?; ( rderive, make_register(hash, Some(bpath))?.derived_from(Some(rderive.into())), ) } else { - return Err(anyhow!("derive: incorrect syntax for {rname}")); + return Err(anyhow!("derive: incorrect syntax for {rspec}")); }; // Attempt to verify that the destination register name is correct. @@ -280,25 +277,30 @@ pub(crate) trait RegisterBlockExt: Name { })?; } - match self.get_mut_reg(rname) { - Some(register) => register.modify_from(info, VAL_LVL)?, - None => { - let register = info.name(rname.into()).build(VAL_LVL)?.single(); - self.add_child(RegisterCluster::Register(register)); - } + let mut found = Vec::new(); + for register in self.iter_registers(rspec) { + found.push(register.name.to_string()); + register.modify_from(info.clone(), VAL_LVL)?; + } + if found.is_empty() { + super::check_dimable_name(rspec)?; + let register = info.name(rspec.into()).build(VAL_LVL)?.single(); + self.add_child(RegisterCluster::Register(register)); } - for r in self - .regs_mut() - .filter(|r| r.derived_from.as_deref() == Some(rname)) - { - r.derived_from = Some(rderive.into()); + for rname in found { + for r in self + .regs_mut() + .filter(|r| r.derived_from.as_deref() == Some(&rname)) + { + r.derived_from = Some(rderive.into()); + } } Ok(()) } /// Remove fields from rname and mark it as derivedFrom rderive. /// Update all derivedFrom referencing rname - fn derive_cluster(&mut self, _cname: &str, _cderive: &Yaml, _bpath: &BlockPath) -> PatchResult { + fn derive_cluster(&mut self, _cspec: &str, _cderive: &Yaml, _bpath: &BlockPath) -> PatchResult { todo!() } @@ -881,9 +883,6 @@ impl RegisterBlockExt for Peripheral { fn get_reg(&self, name: &str) -> Option<&Register> { self.get_register(name) } - fn get_mut_reg(&mut self, name: &str) -> Option<&mut Register> { - self.get_mut_register(name) - } fn children(&self) -> Option<&Vec> { self.registers.as_ref() } @@ -919,9 +918,6 @@ impl RegisterBlockExt for Cluster { fn get_reg(&self, name: &str) -> Option<&Register> { self.get_register(name) } - fn get_mut_reg(&mut self, name: &str) -> Option<&mut Register> { - self.get_mut_register(name) - } fn children(&self) -> Option<&Vec> { Some(&self.children) } @@ -1128,29 +1124,29 @@ impl PeripheralExt for Peripheral { } } - for (rname, rderive) in pmod.hash_iter("_derive") { - let rname = rname.str()?; - match rname { + for (rspec, rderive) in pmod.hash_iter("_derive") { + let rspec = rspec.str()?; + match rspec { "_registers" => { - for (rname, val) in rderive.hash()? { - let rname = rname.str()?; - self.derive_register(rname, val, &ppath).with_context(|| { - format!("Deriving register `{rname}` from `{val:?}`") + for (rspec, val) in rderive.hash()? { + let rspec = rspec.str()?; + self.derive_register(rspec, val, &ppath).with_context(|| { + format!("Deriving register `{rspec}` from `{val:?}`") })?; } } "_clusters" => { - for (cname, val) in rderive.hash()? { - let cname = cname.str()?; - self.derive_cluster(rname, val, &ppath).with_context(|| { - format!("Deriving cluster `{cname}` from `{val:?}`") + for (cspec, val) in rderive.hash()? { + let cspec = cspec.str()?; + self.derive_cluster(cspec, val, &ppath).with_context(|| { + format!("Deriving cluster `{cspec}` from `{val:?}`") })?; } } _ => { - self.derive_register(rname, rderive, &ppath) + self.derive_register(rspec, rderive, &ppath) .with_context(|| { - format!("Deriving register `{rname}` from `{rderive:?}`") + format!("Deriving register `{rspec}` from `{rderive:?}`") })?; } } @@ -1421,29 +1417,29 @@ impl ClusterExt for Cluster { } } - for (rname, rderive) in cmod.hash_iter("_derive") { - let rname = rname.str()?; - match rname { + for (rspec, rderive) in cmod.hash_iter("_derive") { + let rspec = rspec.str()?; + match rspec { "_registers" => { - for (rname, val) in rderive.hash()? { - let rname = rname.str()?; - self.derive_register(rname, val, &cpath).with_context(|| { - format!("Deriving register `{rname}` from `{val:?}`") + for (rspec, val) in rderive.hash()? { + let rspec = rspec.str()?; + self.derive_register(rspec, val, &cpath).with_context(|| { + format!("Deriving register `{rspec}` from `{val:?}`") })?; } } "_clusters" => { - for (cname, val) in rderive.hash()? { - let cname = cname.str()?; - self.derive_cluster(rname, val, &cpath).with_context(|| { - format!("Deriving cluster `{cname}` from `{val:?}`") + for (cspec, val) in rderive.hash()? { + let cspec = cspec.str()?; + self.derive_cluster(cspec, val, &cpath).with_context(|| { + format!("Deriving cluster `{cspec}` from `{val:?}`") })?; } } _ => { - self.derive_register(rname, rderive, &cpath) + self.derive_register(rspec, rderive, &cpath) .with_context(|| { - format!("Deriving register `{rname}` from `{rderive:?}`") + format!("Deriving register `{rspec}` from `{rderive:?}`") })?; } } diff --git a/src/patch/register.rs b/src/patch/register.rs index a2883484..7552d3a9 100644 --- a/src/patch/register.rs +++ b/src/patch/register.rs @@ -44,6 +44,7 @@ pub trait RegisterExt { "_include", "_path", "_delete", + "_derive", "_strip", "_strip_end", "_clear", @@ -69,6 +70,9 @@ pub trait RegisterExt { /// Delete fields matched by fspec inside rtag fn delete_field(&mut self, fspec: &str) -> PatchResult; + /// Clear field from rname and mark it as derivedFrom rderive. + fn derive_field(&mut self, fname: &str, fderive: &Yaml, rpath: &RegisterPath) -> PatchResult; + /// Clear contents of fields matched by fspec inside rtag fn clear_field(&mut self, fspec: &str) -> PatchResult; @@ -186,6 +190,12 @@ impl RegisterExt for Register { self.add_field(fname, fadd.hash()?, &rpath) .with_context(|| format!("Adding field `{fname}`"))?; } + // Handle derives + for (fspec, fderive) in rmod.hash_iter("_derive") { + let fspec = fspec.str()?; + self.derive_field(fspec, fderive, &rpath) + .with_context(|| format!("Deriving field `{fspec}` from `{fderive:?}`"))?; + } // Handle merges match rmod.get_yaml("_merge") { @@ -342,6 +352,42 @@ impl RegisterExt for Register { Ok(()) } + fn derive_field(&mut self, fspec: &str, fderive: &Yaml, rpath: &RegisterPath) -> PatchResult { + fn make_path(dpath: &str, rpath: &RegisterPath) -> String { + let mut parts = dpath.split("."); + if let (Some(reg), Some(field), None) = (parts.next(), parts.next(), parts.next()) { + let fpath = rpath.block.new_register(reg).new_field(field); + fpath.to_string() + } else { + dpath.into() + } + } + let info = if let Some(dpath) = fderive.as_str() { + FieldInfo::builder().derived_from(Some(make_path(dpath, rpath))) + } else if let Some(hash) = fderive.as_hash() { + let dpath = hash.get_str("_from")?.ok_or_else(|| { + anyhow!("derive: source field not given, please add a _from field to {fspec}") + })?; + make_field(hash, Some(rpath))?.derived_from(Some(make_path(dpath, rpath))) + } else { + return Err(anyhow!("derive: incorrect syntax for {fspec}")); + }; + + let mut found = false; + for field in self.iter_fields(fspec) { + found = true; + field.modify_from(info.clone(), VAL_LVL)?; + } + if !found { + { + super::check_dimable_name(fspec)?; + let field = info.name(fspec.into()).build(VAL_LVL)?.single(); + self.fields.get_or_insert(Vec::new()).push(field); + } + } + Ok(()) + } + fn clear_field(&mut self, fspec: &str) -> PatchResult { for ftag in self.iter_fields(fspec) { if ftag.derived_from.is_some() {