Skip to content

rust: separate parse and codegen in module! #423

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

Open
wants to merge 1 commit into
base: rust
Choose a base branch
from
Open
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
227 changes: 118 additions & 109 deletions rust/macros/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn expect_byte_string(it: &mut token_stream::IntoIter) -> String {
try_byte_string(it).expect("Expected byte string")
}

#[derive(Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
enum ParamType {
Ident(String),
Array { vals: String, max_length: usize },
Expand Down Expand Up @@ -285,6 +285,44 @@ fn generated_array_ops_name(vals: &str, max_length: usize) -> String {
)
}

#[derive(Debug)]
struct ParamInfo {
name: String,
type_: ParamType,
default: String,
permission: String,
description: String,
}

impl ParamInfo {
fn try_parse(it: &mut token_stream::IntoIter) -> Option<Self> {
let param_name = match it.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
Some(_) => panic!("Expected Ident or end"),
None => return None,
};

assert_eq!(expect_punct(it), ':');
let param_type = expect_type(it);
let group = expect_group(it);
assert_eq!(group.delimiter(), Delimiter::Brace);

let mut param_it = group.stream().into_iter();
let param_default = get_default(&param_type, &mut param_it);
let param_permissions = get_literal(&mut param_it, "permissions");
let param_description = get_byte_string(&mut param_it, "description");
expect_end(&mut param_it);

Some(ParamInfo {
name: param_name,
type_: param_type,
default: param_default,
permission: param_permissions,
description: param_description,
})
}
}

#[derive(Debug, Default)]
struct ModuleInfo {
type_: String,
Expand All @@ -293,7 +331,7 @@ struct ModuleInfo {
author: Option<String>,
description: Option<String>,
alias: Option<String>,
params: Option<Group>,
params: Vec<ParamInfo>,
}

impl ModuleInfo {
Expand Down Expand Up @@ -339,7 +377,17 @@ impl ModuleInfo {
"alias_rtnl_link" => {
info.alias = Some(format!("rtnl-link-{}", expect_byte_string(it)))
}
"params" => info.params = Some(expect_group(it)),
"params" => {
let group = expect_group(it);
assert_eq!(group.delimiter(), Delimiter::Brace);
let mut inner_it = group.stream().into_iter();
let mut params = Vec::new();
while let Some(param) = ParamInfo::try_parse(&mut inner_it) {
assert_eq!(expect_punct(&mut inner_it), ',');
params.push(param);
}
info.params = params;
}
_ => panic!(
"Unknown key \"{}\". Valid keys are: {:?}.",
key, EXPECTED_KEYS
Expand Down Expand Up @@ -375,79 +423,52 @@ impl ModuleInfo {

info
}
}

pub fn module(ts: TokenStream) -> TokenStream {
let mut it = ts.into_iter();

let info = ModuleInfo::parse(&mut it);

let name = info.name.clone();

let mut modinfo = ModInfoBuilder::new(&name);
if let Some(author) = info.author {
modinfo.emit("author", &author);
}
if let Some(description) = info.description {
modinfo.emit("description", &description);
}
modinfo.emit("license", &info.license);
if let Some(alias) = info.alias {
modinfo.emit("alias", &alias);
}

// Built-in modules also export the `file` modinfo string
let file =
std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable");
modinfo.emit_only_builtin("file", &file);

let mut array_types_to_generate = Vec::new();
if let Some(params) = info.params {
assert_eq!(params.delimiter(), Delimiter::Brace);

let mut it = params.stream().into_iter();

loop {
let param_name = match it.next() {
Some(TokenTree::Ident(ident)) => ident.to_string(),
Some(_) => panic!("Expected Ident or end"),
None => break,
};

assert_eq!(expect_punct(&mut it), ':');
let param_type = expect_type(&mut it);
let group = expect_group(&mut it);
assert_eq!(expect_punct(&mut it), ',');
fn generate(&self) -> TokenStream {
let name = &self.name;
let mut modinfo = ModInfoBuilder::new(name);
if let Some(author) = &self.author {
modinfo.emit("author", author);
}
if let Some(description) = &self.description {
modinfo.emit("description", description);
}
modinfo.emit("license", &self.license);
if let Some(alias) = &self.alias {
modinfo.emit("alias", alias);
}

assert_eq!(group.delimiter(), Delimiter::Brace);
// Built-in modules also export the `file` modinfo string
let file = std::env::var("RUST_MODFILE")
.expect("Unable to fetch RUST_MODFILE environmental variable");
modinfo.emit_only_builtin("file", &file);

let mut param_it = group.stream().into_iter();
let param_default = get_default(&param_type, &mut param_it);
let param_permissions = get_literal(&mut param_it, "permissions");
let param_description = get_byte_string(&mut param_it, "description");
expect_end(&mut param_it);
let mut array_types_to_generate = Vec::new();
for param in self.params.iter() {
let param_name = &param.name;
let param_type = &param.type_;
let param_default = &param.default;
let param_permissions = &param.permission;
let param_description = &param.description;

// TODO: more primitive types
// TODO: other kinds: unsafes, etc.
let (param_kernel_type, ops): (String, _) = match param_type {
ParamType::Ident(ref param_type) => (
ParamType::Ident(param_type) => (
param_type.to_string(),
param_ops_path(param_type).to_string(),
),
ParamType::Array {
ref vals,
max_length,
} => {
array_types_to_generate.push((vals.clone(), max_length));
ParamType::Array { vals, max_length } => {
array_types_to_generate.push((vals.clone(), *max_length));
(
format!("__rust_array_param_{}_{}", vals, max_length),
generated_array_ops_name(vals, max_length),
generated_array_ops_name(vals, *max_length),
)
}
};

modinfo.emit_param("parmtype", &param_name, &param_kernel_type);
modinfo.emit_param("parm", &param_name, &param_description);
modinfo.emit_param("parmtype", param_name, &param_kernel_type);
modinfo.emit_param("parm", param_name, param_description);
let param_type_internal = match param_type {
ParamType::Ident(ref param_type) => match param_type.as_ref() {
"str" => "kernel::module_param::StringParam".to_string(),
Expand All @@ -462,7 +483,7 @@ pub fn module(ts: TokenStream) -> TokenStream {
max_length = max_length
),
};
let read_func = if permissions_are_readonly(&param_permissions) {
let read_func = if permissions_are_readonly(param_permissions) {
format!(
"
fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Value {{
Expand Down Expand Up @@ -553,27 +574,26 @@ pub fn module(ts: TokenStream) -> TokenStream {
)
);
}
}

let mut generated_array_types = String::new();
let mut generated_array_types = String::new();

for (vals, max_length) in array_types_to_generate {
let ops_name = generated_array_ops_name(&vals, max_length);
generated_array_types.push_str(&format!(
"
for (vals, max_length) in array_types_to_generate {
let ops_name = generated_array_ops_name(&vals, max_length);
generated_array_types.push_str(&format!(
"
kernel::make_param_ops!(
{ops_name},
kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}>
);
",
ops_name = ops_name,
vals = vals,
max_length = max_length,
));
}
",
ops_name = ops_name,
vals = vals,
max_length = max_length,
));
}

format!(
"
format!(
"
/// The module name.
///
/// Used by the printing macros, e.g. [`info!`].
Expand Down Expand Up @@ -659,23 +679,32 @@ pub fn module(ts: TokenStream) -> TokenStream {
{modinfo}

{generated_array_types}
",
type_ = info.type_,
name = info.name,
modinfo = modinfo.buffer,
generated_array_types = generated_array_types,
initcall_section = ".initcall6.init"
).parse().expect("Error parsing formatted string into token stream.")
",
type_ = self.type_,
name = self.name,
modinfo = modinfo.buffer,
generated_array_types = generated_array_types,
initcall_section = ".initcall6.init"
).parse().expect("Error parsing formatted string into token stream.")
}
}

pub fn module(ts: TokenStream) -> TokenStream {
let mut it = ts.into_iter();
let info = ModuleInfo::parse(&mut it);
info.generate()
}

pub fn module_misc_device(ts: TokenStream) -> TokenStream {
let mut it = ts.into_iter();

let info = ModuleInfo::parse(&mut it);
let mut info = ModuleInfo::parse(&mut it);
let type_ = info.type_;

let module = format!("__internal_ModuleFor{}", info.type_);
let module = format!("__internal_ModuleFor{}", type_);
info.type_ = module.clone();

format!(
let extra = format!(
"
#[doc(hidden)]
struct {module} {{
Expand All @@ -693,35 +722,15 @@ pub fn module_misc_device(ts: TokenStream) -> TokenStream {
}})
}}
}}

kernel::prelude::module! {{
type: {module},
name: b\"{name}\",
{author}
{description}
license: b\"{license}\",
{alias}
}}
",
module = module,
type_ = info.type_,
type_ = type_,
name = info.name,
author = info
.author
.map(|v| format!("author: b\"{}\",", v))
.unwrap_or_else(|| "".to_string()),
description = info
.description
.map(|v| format!("description: b\"{}\",", v))
.unwrap_or_else(|| "".to_string()),
alias = info
.alias
.map(|v| format!("alias: b\"{}\",", v))
.unwrap_or_else(|| "".to_string()),
license = info.license
)
.parse()
.expect("Error parsing formatted string into token stream.")
.expect("Error parsing formatted string into token stream.");

vec![extra, info.generate()].into_iter().collect()
}

#[cfg(test)]
Expand Down