Skip to content

Commit

Permalink
Merge pull request #259 from rust-embedded/derive_field
Browse files Browse the repository at this point in the history
derive field
  • Loading branch information
burrbull authored Dec 31, 2024
2 parents a24feb5 + 43272fa commit 13d3f7e
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ env:

on:
push:
branches: master
branches: [master]
pull_request:
merge_group:

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG-rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ This changelog tracks the Rust `svdtools` project. See

## [Unreleased]

* `_derive` field

## [v0.3.20] 2024-11-14

* Implement `_expand_cluster`
Expand Down
2 changes: 1 addition & 1 deletion src/patch/iterators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ where
spec: &'b str,
}

impl<'b, I> Iterator for MatchIter<'b, I>
impl<I> Iterator for MatchIter<'_, I>
where
I: Iterator,
I::Item: Name,
Expand Down
13 changes: 13 additions & 0 deletions src/patch/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -821,6 +823,17 @@ impl Spec for str {
}
}

pub(crate) fn check_dimable_name(name: &str) -> Result<()> {
static PATTERN: Lazy<Regex> = 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<T: Interpolate>(path: &Option<&T>, s: Option<&str>) -> Option<String> {
if let Some(path) = path {
path.interpolate_opt(s)
Expand Down
96 changes: 46 additions & 50 deletions src/patch/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<RegisterCluster>>;
Expand Down Expand Up @@ -249,22 +246,22 @@ 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,
RegisterInfo::builder().derived_from(Some(rderive.into())),
)
} 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.
Expand All @@ -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!()
}

Expand Down Expand Up @@ -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<RegisterCluster>> {
self.registers.as_ref()
}
Expand Down Expand Up @@ -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<RegisterCluster>> {
Some(&self.children)
}
Expand Down Expand Up @@ -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:?}`")
})?;
}
}
Expand Down Expand Up @@ -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:?}`")
})?;
}
}
Expand Down
46 changes: 46 additions & 0 deletions src/patch/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub trait RegisterExt {
"_include",
"_path",
"_delete",
"_derive",
"_strip",
"_strip_end",
"_clear",
Expand All @@ -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;

Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 13d3f7e

Please sign in to comment.