Skip to content

Modules (form) #430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added

- Option `-f`(`--form`) let you split `lib.rs` on modules, as the `form` tool does
- Option `-o`(`--output`) let you specify output directory path

### Fixed

- Keyword sanitizing (`async`)
21 changes: 11 additions & 10 deletions src/generate/device.rs
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ use std::fs::File;
use std::io::Write;

use crate::errors::*;
use crate::modules::Module;
use crate::util::{self, ToSanitizedUpperCase};
use crate::Target;

@@ -17,8 +18,8 @@ pub fn render(
nightly: bool,
generic_mod: bool,
device_x: &mut String,
) -> Result<TokenStream> {
let mut out = TokenStream::new();
) -> Result<Module> {
let mut out = Module::new("lib", "");

let doc = format!(
"Peripheral access API for {0} microcontrollers \
@@ -159,16 +160,16 @@ pub fn render(
if generic_mod {
writeln!(File::create("generic.rs").unwrap(), "{}", generic_file).unwrap();
} else {
let tokens = syn::parse_file(generic_file).unwrap().into_token_stream();

out.extend(quote! {
let mut generic_module = Module::new(
"generic",
"Common register and bit access and modify traits",
);
generic_module.out.extend(quote! {
#[allow(unused_imports)]
use generic::*;
///Common register and bit access and modify traits
pub mod generic {
#tokens
}
});
generic_module.extend(syn::parse_file(generic_file).unwrap().into_token_stream());
out.push_module(generic_module);
}

for p in &d.peripherals {
@@ -177,7 +178,7 @@ pub fn render(
continue;
}

out.extend(peripheral::render(
out.push_module(peripheral::render(
p,
&d.peripherals,
&d.default_register_properties,
78 changes: 32 additions & 46 deletions src/generate/peripheral.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::modules::Module;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;
@@ -20,9 +21,7 @@ pub fn render(
all_peripherals: &[Peripheral],
defaults: &RegisterProperties,
nightly: bool,
) -> Result<TokenStream> {
let mut out = TokenStream::new();

) -> Result<Module> {
let p_derivedfrom = p_original
.derived_from
.as_ref()
@@ -31,13 +30,19 @@ pub fn render(
let p_merged = p_derivedfrom.map(|ancestor| p_original.derive_from(ancestor));
let p = p_merged.as_ref().unwrap_or(p_original);

let name_sc = p.name.to_sanitized_snake_case();
let description =
util::escape_brackets(util::respace(p.description.as_ref().unwrap_or(&p.name)).as_ref());

let mut module = Module::new(&name_sc, &description);

if p_original.derived_from.is_some() && p_derivedfrom.is_none() {
eprintln!(
"Couldn't find derivedFrom original: {} for {}, skipping",
p_original.derived_from.as_ref().unwrap(),
p_original.name
);
return Ok(out);
return Ok(module);
}

let span = Span::call_site();
@@ -46,15 +51,14 @@ pub fn render(
let description = util::respace(p.description.as_ref().unwrap_or(&p.name));
let derive_regs = p_derivedfrom.is_some() && p_original.registers.is_none();

let name_sc = Ident::new(&p.name.to_sanitized_snake_case(), span);
let base = if derive_regs {
Ident::new(&p_derivedfrom.unwrap().name.to_sanitized_snake_case(), span)
} else {
name_sc.clone()
Ident::new(&name_sc, span)
};

// Insert the peripheral structure
out.extend(quote! {
module.out.extend(quote! {
#[doc = #description]
pub struct #name_pc { _marker: PhantomData<*const ()> }

@@ -81,7 +85,7 @@ pub fn render(
// Derived peripherals may not require re-implementation, and will instead
// use a single definition of the non-derived version.
if derive_regs {
return Ok(out);
return Ok(module);
}

// erc: *E*ither *R*egister or *C*luster
@@ -143,23 +147,22 @@ pub fn render(
// No `struct RegisterBlock` can be generated
if registers.is_empty() && clusters.is_empty() {
// Drop the definition of the peripheral
return Ok(TokenStream::new());
return Ok(Module::new(&name_sc, &description));
}

let defaults = p.default_register_properties.derive_from(defaults);

// Push any register or cluster blocks into the output
let mut mod_items = TokenStream::new();
mod_items.extend(register_or_cluster_block(ercs, &defaults, None, nightly)?);
module.extend(register_or_cluster_block(ercs, &defaults, None, nightly)?);

// Push all cluster related information into the peripheral module
for c in &clusters {
mod_items.extend(cluster_block(c, &defaults, p, all_peripherals, nightly)?);
module.push_module(cluster_block(c, &defaults, p, all_peripherals, nightly)?);
}

// Push all regsiter realted information into the peripheral module
for reg in registers {
mod_items.extend(register::render(
module.push_module(register::render(
reg,
registers,
p,
@@ -168,22 +171,7 @@ pub fn render(
)?);
}

let description =
util::escape_brackets(util::respace(p.description.as_ref().unwrap_or(&p.name)).as_ref());

let open = Punct::new('{', Spacing::Alone);
let close = Punct::new('}', Spacing::Alone);

out.extend(quote! {
#[doc = #description]
pub mod #name_sc #open
});

out.extend(mod_items);

close.to_tokens(&mut out);

Ok(out)
Ok(module)
}

#[derive(Clone, Debug)]
@@ -716,9 +704,7 @@ fn cluster_block(
p: &Peripheral,
all_peripherals: &[Peripheral],
nightly: bool,
) -> Result<TokenStream> {
let mut mod_items = TokenStream::new();

) -> Result<Module> {
// name_sc needs to take into account array type.
let description =
util::escape_brackets(util::respace(c.description.as_ref().unwrap_or(&c.name)).as_ref());
@@ -731,16 +717,25 @@ fn cluster_block(
},
"",
);
let name_sc = Ident::new(&mod_name.to_sanitized_snake_case(), Span::call_site());

let mut module = Module::new(
&mod_name.to_sanitized_snake_case(),
&("Register block\n".to_string() + &description),
);

let defaults = c.default_register_properties.derive_from(defaults);

let reg_block = register_or_cluster_block(&c.children, &defaults, Some(&mod_name), nightly)?;
module.out.extend(register_or_cluster_block(
&c.children,
&defaults,
Some(&mod_name),
nightly,
)?);

// Generate definition for each of the registers.
let registers = util::only_registers(&c.children);
for reg in &registers {
mod_items.extend(register::render(
module.push_module(register::render(
reg,
&registers,
p,
@@ -752,18 +747,9 @@ fn cluster_block(
// Generate the sub-cluster blocks.
let clusters = util::only_clusters(&c.children);
for c in &clusters {
mod_items.extend(cluster_block(c, &defaults, p, all_peripherals, nightly)?);
module.push_module(cluster_block(c, &defaults, p, all_peripherals, nightly)?);
}

Ok(quote! {
#reg_block

///Register block
#[doc = #description]
pub mod #name_sc {
#mod_items
}
})
Ok(module)
}

/// Takes a svd::Register which may be a register array, and turn in into
86 changes: 34 additions & 52 deletions src/generate/register.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::modules::Module;
use crate::quote::ToTokens;
use crate::svd::{
Access, BitRange, EnumeratedValues, Field, Peripheral, Register, RegisterCluster,
@@ -16,13 +17,13 @@ pub fn render(
peripheral: &Peripheral,
all_peripherals: &[Peripheral],
defs: &RegisterProperties,
) -> Result<TokenStream> {
) -> Result<Module> {
let access = util::access_of(register);
let name = util::name_of(register);
let span = Span::call_site();
let name_pc = Ident::new(&name.to_sanitized_upper_case(), span);
let _name_pc = Ident::new(&format!("_{}", &name.to_sanitized_upper_case()), span);
let name_sc = Ident::new(&name.to_sanitized_snake_case(), span);
let name_sc = name.to_sanitized_snake_case();
let rsize = register
.size
.or(defs.size)
@@ -43,7 +44,7 @@ pub fn render(
.as_ref(),
);

let mut mod_items = TokenStream::new();
let mut module = Module::new(&name_sc, &description);
let mut r_impl_items = TokenStream::new();
let mut w_impl_items = TokenStream::new();
let mut methods = vec![];
@@ -53,7 +54,7 @@ pub fn render(

if can_read {
let desc = format!("Reader of register {}", register.name);
mod_items.extend(quote! {
module.extend(quote! {
#[doc = #desc]
pub type R = crate::R<#rty, super::#name_pc>;
});
@@ -63,13 +64,13 @@ pub fn render(
let res_val = register.reset_value.or(defs.reset_value).map(|v| v as u64);
if can_write {
let desc = format!("Writer for register {}", register.name);
mod_items.extend(quote! {
module.extend(quote! {
#[doc = #desc]
pub type W = crate::W<#rty, super::#name_pc>;
});
if let Some(rv) = res_val.map(util::hex) {
let doc = format!("Register {} `reset()`'s with value {}", register.name, &rv);
mod_items.extend(quote! {
module.extend(quote! {
#[doc = #doc]
impl crate::ResetValue for super::#name_pc {
type Type = #rty;
@@ -106,37 +107,36 @@ pub fn render(
&rty,
res_val,
access,
&mut mod_items,
&mut module,
&mut r_impl_items,
&mut w_impl_items,
)?;
}
}

let open = Punct::new('{', Spacing::Alone);
let close = Punct::new('}', Spacing::Alone);
let close = Punct::new('}', Spacing::Alone).into_token_stream();

if can_read {
mod_items.extend(quote! {
module.extend(quote! {
impl R #open
});

mod_items.extend(r_impl_items);
module.extend(r_impl_items);

close.to_tokens(&mut mod_items);
module.extend(close.clone());
}

if can_write {
mod_items.extend(quote! {
module.extend(quote! {
impl W #open
});

mod_items.extend(w_impl_items);
module.extend(w_impl_items);

close.to_tokens(&mut mod_items);
module.extend(close);
}

let mut out = TokenStream::new();
let methods = methods
.iter()
.map(|s| format!("[`{0}`](crate::generic::Reg::{0})", s))
@@ -151,7 +151,7 @@ pub fn render(
)
.as_str();
}
out.extend(quote! {
module.out.extend(quote! {
#[doc = #doc]
pub type #name_pc = crate::Reg<#rty, #_name_pc>;

@@ -165,7 +165,7 @@ pub fn render(
"`read()` method returns [{0}::R]({0}::R) reader structure",
&name_sc
);
out.extend(quote! {
module.out.extend(quote! {
#[doc = #doc]
impl crate::Readable for #name_pc {}
});
@@ -175,24 +175,12 @@ pub fn render(
"`write(|w| ..)` method takes [{0}::W]({0}::W) writer structure",
&name_sc
);
out.extend(quote! {
module.out.extend(quote! {
#[doc = #doc]
impl crate::Writable for #name_pc {}
});
}

out.extend(quote! {
#[doc = #description]
pub mod #name_sc #open
});

out.extend(mod_items);

out.extend(quote! {
#close
});

Ok(out)
Ok(module)
}

pub fn fields(
@@ -204,7 +192,7 @@ pub fn fields(
rty: &Ident,
reset_value: Option<u64>,
access: Access,
mod_items: &mut TokenStream,
module: &mut Module,
r_impl_items: &mut TokenStream,
w_impl_items: &mut TokenStream,
) -> Result<()> {
@@ -374,17 +362,17 @@ pub fn fields(
let pc = util::replace_suffix(base.field, "");
let pc = pc.to_sanitized_upper_case();
let base_pc_r = Ident::new(&(pc + "_A"), span);
derive_from_base(mod_items, &base, &pc_r, &base_pc_r, &description);
derive_from_base(module, &base, &pc_r, &base_pc_r, &description);

mod_items.extend(quote! {
module.extend(quote! {
#[doc = #readerdoc]
pub type #_pc_r = crate::R<#fty, #pc_r>;
});
} else {
let has_reserved_variant = evs.values.len() != (1 << width);
let variants = Variant::from_enumerated_values(evs)?;

add_from_variants(mod_items, &variants, &pc_r, &fty, &description, rv);
add_from_variants(module, &variants, &pc_r, &fty, &description, rv);

let mut enum_items = TokenStream::new();

@@ -458,7 +446,7 @@ pub fn fields(
});
}

mod_items.extend(quote! {
module.extend(quote! {
#[doc = #readerdoc]
pub type #_pc_r = crate::R<#fty, #pc_r>;
impl #_pc_r {
@@ -467,7 +455,7 @@ pub fn fields(
});
}
} else {
mod_items.extend(quote! {
module.extend(quote! {
#[doc = #readerdoc]
pub type #_pc_r = crate::R<#fty, #fty>;
})
@@ -494,9 +482,9 @@ pub fn fields(
let pc = util::replace_suffix(base.field, "");
let pc = pc.to_sanitized_upper_case();
let base_pc_w = Ident::new(&(pc + "_AW"), span);
derive_from_base(mod_items, &base, &pc_w, &base_pc_w, &description)
derive_from_base(module, &base, &pc_w, &base_pc_w, &description)
} else {
add_from_variants(mod_items, &variants, pc_w, &fty, &description, rv);
add_from_variants(module, &variants, pc_w, &fty, &description, rv);
}
}

@@ -584,7 +572,7 @@ pub fn fields(
offset_entry = quote! {};
}

mod_items.extend(quote! {
module.extend(quote! {
#[doc = #doc]
pub struct #_pc_w<'a> {
w: &'a mut W,
@@ -696,7 +684,7 @@ impl Variant {
}

fn add_from_variants(
mod_items: &mut TokenStream,
module: &mut Module,
variants: &[Variant],
pc: &Ident,
fty: &Ident,
@@ -728,7 +716,7 @@ fn add_from_variants(
desc.to_owned()
};

mod_items.extend(quote! {
module.extend(quote! {
#[doc = #desc]
#[derive(Clone, Copy, Debug, PartialEq)]
#repr
@@ -779,21 +767,15 @@ fn description_with_bits(description: &str, offset: u64, width: u32) -> String {
res
}

fn derive_from_base(
mod_items: &mut TokenStream,
base: &Base,
pc: &Ident,
base_pc: &Ident,
desc: &str,
) {
fn derive_from_base(module: &mut Module, base: &Base, pc: &Ident, base_pc: &Ident, desc: &str) {
let span = Span::call_site();
if let (Some(peripheral), Some(register)) = (&base.peripheral, &base.register) {
let pmod_ = peripheral.to_sanitized_snake_case();
let rmod_ = register.to_sanitized_snake_case();
let pmod_ = Ident::new(&pmod_, span);
let rmod_ = Ident::new(&rmod_, span);

mod_items.extend(quote! {
module.extend(quote! {
#[doc = #desc]
pub type #pc =
crate::#pmod_::#rmod_::#base_pc;
@@ -802,13 +784,13 @@ fn derive_from_base(
let mod_ = register.to_sanitized_snake_case();
let mod_ = Ident::new(&mod_, span);

mod_items.extend(quote! {
module.extend(quote! {
#[doc = #desc]
pub type #pc =
super::#mod_::#base_pc;
});
} else {
mod_items.extend(quote! {
module.extend(quote! {
#[doc = #desc]
pub type #pc = #base_pc;
});
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -489,6 +489,7 @@ use svd_parser as svd;

mod errors;
mod generate;
mod modules;
mod util;

pub use crate::util::Target;
@@ -523,7 +524,8 @@ pub fn generate(xml: &str, target: Target, nightly: bool) -> Result<Generation>
let device = svd::parse(xml).unwrap(); //TODO(AJM)
let mut device_x = String::new();
let items = generate::device::render(&device, target, nightly, false, &mut device_x)
.or(Err(SvdError::Render))?;
.or(Err(SvdError::Render))?
.items_into_token_stream();

let mut lib_rs = String::new();
writeln!(
50 changes: 44 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -6,10 +6,12 @@ extern crate error_chain;
extern crate log;
#[macro_use]
extern crate quote;
use std::path::PathBuf;
use svd_parser as svd;

mod errors;
mod generate;
mod modules;
mod util;

use std::fs::File;
@@ -51,6 +53,18 @@ fn run() -> Result<()> {
.short("g")
.help("Push generic mod in separate file"),
)
.arg(
Arg::with_name("form")
.long("form")
.short("f")
.help("Split on modules"),
)
.arg(
Arg::with_name("output")
.long("output")
.short("o")
.help("Directory to place generated files"),
)
.arg(
Arg::with_name("log_level")
.long("log")
@@ -98,17 +112,41 @@ fn run() -> Result<()> {

let generic_mod = matches.is_present("generic_mod");

let form = matches.is_present("form");

let mut device_x = String::new();
let items = generate::device::render(&device, target, nightly, generic_mod, &mut device_x)?;
let mut file = File::create("lib.rs").expect("Couldn't create lib.rs file");

let data = items.to_string().replace("] ", "]\n");
file.write_all(data.as_ref())
.expect("Could not write code to lib.rs");
let path = PathBuf::from(match matches.value_of("output") {
Some(path) => path,
None => ".",
});
if form {
items.lib_to_files(&path.join("src"));
} else {
let mut file = File::create(&path.join("lib.rs")).expect("Couldn't create lib.rs file");

let data = items
.items_into_token_stream()
.to_string()
.replace("] ", "]\n");
file.write_all(data.as_ref())
.expect("Could not write code to lib.rs");
}

if target == Target::CortexM || target == Target::Msp430 {
writeln!(File::create("device.x").unwrap(), "{}", device_x).unwrap();
writeln!(File::create("build.rs").unwrap(), "{}", build_rs()).unwrap();
writeln!(
File::create(&path.join("device.x")).unwrap(),
"{}",
device_x
)
.unwrap();
writeln!(
File::create(&path.join("build.rs")).unwrap(),
"{}",
build_rs()
)
.unwrap();
}

Ok(())
119 changes: 119 additions & 0 deletions src/modules.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use crate::quote::ToTokens;
use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream};
use std::fs::File;
use std::io::Write;
use std::path::Path;

pub enum Item {
Tokens(TokenStream),
Module(Module),
}

/// Structure of module:
///
/// | #out
/// |
/// | /// #description
/// | mod #name {
/// | #items
/// | }
pub struct Module {
name: String,
description: String,
pub out: TokenStream,
items: Vec<Item>,
}

impl Module {
pub fn new(name: &str, description: &str) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
out: TokenStream::new(),
items: Vec::new(),
}
}
pub fn push_module(&mut self, module: Module) {
self.items.push(Item::Module(module))
}
pub fn extend(&mut self, tokens: TokenStream) {
self.items.push(Item::Tokens(tokens))
}
pub fn into_token_stream(self) -> TokenStream {
let mut tokens = self.out;
let open = Punct::new('{', Spacing::Alone);
let close = Punct::new('}', Spacing::Alone);
let name = Ident::new(&self.name, Span::call_site());
let description = self.description;
if !self.items.is_empty() {
tokens.extend(quote! {
#[doc = #description]
pub mod #name #open
});
for item in self.items.into_iter() {
tokens.extend(match item {
Item::Tokens(t) => t,
Item::Module(m) => m.into_token_stream(),
});
}
close.to_tokens(&mut tokens);
}
tokens
}
pub fn items_into_token_stream(self) -> TokenStream {
let mut tokens = TokenStream::new();
for item in self.items.into_iter() {
tokens.extend(match item {
Item::Tokens(t) => t,
Item::Module(m) => m.into_token_stream(),
});
}
tokens
}
#[allow(dead_code)]
fn items_to_files(items: Vec<Item>, path: &Path, dir_path: &Path) {
let mut tokens = TokenStream::new();
std::fs::create_dir_all(&dir_path)
.expect(&format!("Could not create directory {:?}", dir_path));
for item in items.into_iter() {
tokens.extend(match item {
Item::Tokens(t) => t,
Item::Module(m) => m.to_files(&dir_path),
});
let mut file = File::create(path).unwrap();
file.write_all(tokens.to_string().as_ref())
.expect(&format!("Could not write code to {:?}", path));
}
}
#[allow(dead_code)]
pub fn to_files(self, path: &Path) -> TokenStream {
let Module {
name,
description,
mut out,
items,
} = self;
if !items.is_empty() {
Self::items_to_files(
items,
&path.join(&format!("{}.rs", name)),
&path.join(&name),
);
let name = Ident::new(&name, Span::call_site());
out.extend(quote! {
#[doc = #description]
pub mod #name;
});
}
out
}
#[allow(dead_code)]
pub fn lib_to_files(self, path: &Path) {
if path.exists() {
std::fs::remove_dir_all(path).unwrap();
}
if !self.items.is_empty() {
Self::items_to_files(self.items, &path.join("lib.rs"), path);
}
}
}