mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-04-21 04:06:03 +09:00
556 lines
18 KiB
Rust
556 lines
18 KiB
Rust
//! Convert DOM to register interface defined in [`crate::types`], complementing optional parameters.
|
|
//!
|
|
//! root: [`Module::from_xml_dom`]
|
|
//!
|
|
//! error: [`DomConversionError`]
|
|
|
|
use std::{num, str};
|
|
|
|
use roxmltree::{Node, TextPos};
|
|
use thiserror::Error;
|
|
|
|
use crate::parser::{ParseCustomBool, ParseEnumError, ParsePrefixedU32, ParsePrefixedU32Error};
|
|
use crate::types::{
|
|
BitString, Block, Field, Fifo, Memory, Module, ModuleBlockElements, MultipleParams, Register,
|
|
Value,
|
|
};
|
|
|
|
/// Possible errors in conversion, with positional information.
|
|
#[derive(Debug, Error, PartialEq)]
|
|
pub enum DomConversionError {
|
|
#[error("attribute {attr} not found in element: {start} - {end}", start = pos.0, end = pos.1)]
|
|
AttributeNotFound {
|
|
attr: &'static str,
|
|
pos: (TextPos, TextPos),
|
|
},
|
|
#[error("string conversion error: {start} - {end}", start = pos.0, end = pos.1)]
|
|
ParseIntError {
|
|
attr: &'static str,
|
|
pos: (TextPos, TextPos),
|
|
#[source]
|
|
source: num::ParseIntError,
|
|
},
|
|
#[error("string conversion error: {start} - {end}", start = pos.0, end = pos.1)]
|
|
ParsePrefixedU32Error {
|
|
attr: &'static str,
|
|
pos: (TextPos, TextPos),
|
|
#[source]
|
|
source: ParsePrefixedU32Error,
|
|
},
|
|
#[error("string conversion error: {start} - {end}", start = pos.0, end = pos.1)]
|
|
ParseEnumError {
|
|
attr: &'static str,
|
|
pos: (TextPos, TextPos),
|
|
#[source]
|
|
source: ParseEnumError,
|
|
},
|
|
#[error("invalid node {found}: {start} - {end}", start = pos.0, end = pos.1)]
|
|
InvalidNode {
|
|
pos: (TextPos, TextPos),
|
|
found: String,
|
|
},
|
|
#[error("failed to complete/infer parameter {param}: {start} - {end}", start = pos.0, end = pos.1)]
|
|
ParameterCompletion {
|
|
param: &'static str,
|
|
pos: (TextPos, TextPos),
|
|
},
|
|
#[error("other dom conversion error: {comment}: {start} - {end}", start = pos.0, end = pos.1)]
|
|
OtherError {
|
|
comment: &'static str,
|
|
pos: (TextPos, TextPos),
|
|
},
|
|
}
|
|
|
|
impl DomConversionError {
|
|
fn attr_not_found(attr: &'static str, node: Node) -> Self {
|
|
let range = node.range();
|
|
let doc = node.document();
|
|
Self::AttributeNotFound {
|
|
attr,
|
|
pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)),
|
|
}
|
|
}
|
|
|
|
fn parse_int_error(e: num::ParseIntError, attr: &'static str, node: Node) -> Self {
|
|
let range = node.range();
|
|
let doc = node.document();
|
|
Self::ParseIntError {
|
|
attr,
|
|
pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)),
|
|
source: e,
|
|
}
|
|
}
|
|
|
|
fn parse_prefixed_u32_error(e: ParsePrefixedU32Error, attr: &'static str, node: Node) -> Self {
|
|
let range = node.range();
|
|
let doc = node.document();
|
|
Self::ParsePrefixedU32Error {
|
|
attr,
|
|
pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)),
|
|
source: e,
|
|
}
|
|
}
|
|
|
|
fn parse_enum_error(e: ParseEnumError, attr: &'static str, node: Node) -> Self {
|
|
let range = node.range();
|
|
let doc = node.document();
|
|
Self::ParseEnumError {
|
|
attr,
|
|
pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)),
|
|
source: e,
|
|
}
|
|
}
|
|
|
|
fn parameter_completion_error(param: &'static str, node: Node) -> Self {
|
|
let range = node.range();
|
|
let doc = node.document();
|
|
Self::ParameterCompletion {
|
|
param,
|
|
pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)),
|
|
}
|
|
}
|
|
|
|
fn invalid_node(found: String, node: Node) -> Self {
|
|
let range = node.range();
|
|
let doc = node.document();
|
|
Self::InvalidNode {
|
|
pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)),
|
|
found,
|
|
}
|
|
}
|
|
|
|
fn other_error(comment: &'static str, node: Node) -> Self {
|
|
let range = node.range();
|
|
let doc = node.document();
|
|
Self::OtherError {
|
|
comment,
|
|
pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)),
|
|
}
|
|
}
|
|
}
|
|
|
|
mod util {
|
|
use roxmltree::Node;
|
|
|
|
use crate::types::{DataType, RwSpecifier};
|
|
|
|
use super::DomConversionError;
|
|
|
|
pub(crate) fn get_name(node: Node) -> Result<String, DomConversionError> {
|
|
match node.attribute("name") {
|
|
Some(name) if !name.is_empty() => Ok(name.to_string()),
|
|
Some(_name) => Err(DomConversionError::other_error(
|
|
"name cannot be empty",
|
|
node,
|
|
)),
|
|
None => Err(DomConversionError::attr_not_found("name", node)),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn get_type(node: Node) -> Option<Result<DataType, DomConversionError>> {
|
|
node.attribute("type").map(|x| {
|
|
x.parse()
|
|
.map_err(|e| DomConversionError::parse_enum_error(e, "type", node))
|
|
})
|
|
}
|
|
|
|
pub(crate) fn get_modf(node: Node) -> Option<Result<RwSpecifier, DomConversionError>> {
|
|
node.attribute("modf").map(|x| {
|
|
x.parse()
|
|
.map_err(|e| DomConversionError::parse_enum_error(e, "modf", node))
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Module {
|
|
pub fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
|
assert!(node.parent().unwrap().is_root());
|
|
assert_eq!(node.tag_name().name(), "module");
|
|
|
|
let name = util::get_name(node)?;
|
|
let addr = match node.attribute("addr") {
|
|
Some(addr) => addr
|
|
.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "addr", node))?,
|
|
None => return Err(DomConversionError::attr_not_found("addr", node)),
|
|
};
|
|
let size = match node.attribute("size") {
|
|
Some(addr) => addr
|
|
.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "size", node))?,
|
|
None => return Err(DomConversionError::attr_not_found("size", node)),
|
|
};
|
|
let amod = node
|
|
.attribute("amod")
|
|
.map(|amod| {
|
|
amod.parse()
|
|
.map_err(|e| DomConversionError::parse_enum_error(e, "amod", node))
|
|
})
|
|
.transpose()?;
|
|
let r#type = util::get_type(node).transpose()?;
|
|
|
|
let child_bitstrings = node
|
|
.children()
|
|
.filter(|node| node.is_element())
|
|
.filter(|node| node.tag_name().name().eq("bitstring"))
|
|
.map(|node| BitString::from_xml_dom(node))
|
|
.collect::<Result<_, _>>()?;
|
|
let child_other = node
|
|
.children()
|
|
.filter(|node| node.is_element())
|
|
.map(|node| ModuleBlockElements::from_xml_dom(node))
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
Ok(Self {
|
|
name,
|
|
addr,
|
|
size,
|
|
amod,
|
|
r#type,
|
|
desc: node.attribute("desc").map(str::to_string),
|
|
elements_bitstring: child_bitstrings,
|
|
elements_other: child_other,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ModuleBlockElements {
|
|
pub(crate) fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
|
match node.tag_name().name() {
|
|
"block" => Ok(Self::Block(Block::from_xml_dom(node)?)),
|
|
"register" => Ok(Self::Register(Register::from_xml_dom(node)?)),
|
|
"memory" => Ok(Self::Memory(Memory::from_xml_dom(node)?)),
|
|
"fifo" => Ok(Self::Fifo(Fifo::from_xml_dom(node)?)),
|
|
s => Err(DomConversionError::invalid_node(s.to_string(), node)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BitString {
|
|
pub(crate) fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
|
let name = util::get_name(node)?;
|
|
let size = node
|
|
.attribute("size")
|
|
.map(|addr| {
|
|
addr.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "size", node))
|
|
})
|
|
.transpose()?;
|
|
let mask = node
|
|
.attribute("mask")
|
|
.map(|addr| {
|
|
addr.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "mask", node))
|
|
})
|
|
.transpose()?;
|
|
let modf = node
|
|
.attribute("modf")
|
|
.map(|addr| {
|
|
addr.parse()
|
|
.map_err(|e| DomConversionError::parse_enum_error(e, "modf", node))
|
|
})
|
|
.transpose()?;
|
|
|
|
let children = node
|
|
.children()
|
|
.filter(|node| node.is_element() && node.tag_name().name().eq("field"))
|
|
.map(|node| Field::from_xml_dom(node))
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
Ok(Self {
|
|
name,
|
|
size,
|
|
mask,
|
|
modf,
|
|
desc: node.attribute("desc").map(str::to_string),
|
|
elements_field: children,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Block {
|
|
pub(crate) fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
|
let name = util::get_name(node)?;
|
|
let addr = match node.attribute("addr") {
|
|
Some(addr) => addr
|
|
.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "addr", node))?,
|
|
None => 0,
|
|
};
|
|
let r#type = match util::get_type(node).transpose()? {
|
|
Some(x) => Some(x),
|
|
None => node
|
|
.ancestors()
|
|
.filter_map(util::get_type)
|
|
.next()
|
|
.transpose()?,
|
|
};
|
|
let modf = match util::get_modf(node).transpose()? {
|
|
Some(x) => Some(x),
|
|
None => node
|
|
.ancestors()
|
|
.filter_map(util::get_modf)
|
|
.next()
|
|
.transpose()?,
|
|
};
|
|
let multiple = MultipleParams::from_xml_dom(node)?;
|
|
let decoder = match node.attribute("decoder") {
|
|
Some(s) => s.to_string(),
|
|
None => match node
|
|
.ancestors()
|
|
.filter_map(|node| node.attribute("decoder"))
|
|
.next()
|
|
{
|
|
Some(s) => Ok(s.to_string()),
|
|
None => Err(DomConversionError::other_error(
|
|
"decoder format is not yet fixed",
|
|
node,
|
|
)),
|
|
}?,
|
|
};
|
|
let size = match node.attribute("size") {
|
|
Some(addr) => addr
|
|
.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "size", node))?,
|
|
None => return Err(DomConversionError::attr_not_found("size", node)),
|
|
};
|
|
let desc = node.attribute("desc").map(str::to_string);
|
|
|
|
let children = node
|
|
.children()
|
|
.filter(|node| node.is_element())
|
|
.map(|node| ModuleBlockElements::from_xml_dom(node))
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
Ok(Block {
|
|
name,
|
|
addr,
|
|
r#type,
|
|
modf,
|
|
multiple,
|
|
decoder,
|
|
size,
|
|
desc,
|
|
elements: children,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Register {
|
|
pub(crate) fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
|
let name = util::get_name(node)?;
|
|
let addr = match node.attribute("addr") {
|
|
Some(addr) => addr
|
|
.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "addr", node))?,
|
|
None => 0,
|
|
};
|
|
let r#type = util::get_type(node)
|
|
.transpose()?
|
|
.map_or_else(
|
|
|| {
|
|
node.ancestors()
|
|
.filter_map(util::get_type)
|
|
.next()
|
|
.transpose()
|
|
},
|
|
|x| Ok(Some(x)),
|
|
)?
|
|
.ok_or_else(|| DomConversionError::parameter_completion_error("type", node))?;
|
|
let mask = node
|
|
.attribute("mask")
|
|
.map(|addr| {
|
|
addr.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "mask", node))
|
|
})
|
|
.transpose()?;
|
|
let modf = util::get_modf(node)
|
|
.transpose()?
|
|
.map_or_else(
|
|
|| {
|
|
node.ancestors()
|
|
.filter_map(util::get_modf)
|
|
.next()
|
|
.transpose()
|
|
},
|
|
|x| Ok(Some(x)),
|
|
)?
|
|
.ok_or_else(|| DomConversionError::parameter_completion_error("modf", node))?;
|
|
let multiple = MultipleParams::from_xml_dom(node)?;
|
|
let default = node
|
|
.attribute("default")
|
|
.map(|x| {
|
|
x.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "default", node))
|
|
})
|
|
.transpose()?;
|
|
let desc = node.attribute("desc").map(str::to_string);
|
|
|
|
let children: Vec<_> = node
|
|
.children()
|
|
.filter(|node| node.is_element() && node.tag_name().name().eq("field"))
|
|
.map(Field::from_xml_dom)
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
// Validation
|
|
if mask.is_some() && !children.is_empty() {
|
|
return Err(DomConversionError::other_error(
|
|
"both mask and field are used in the same register",
|
|
node,
|
|
));
|
|
}
|
|
if default.is_some() && !children.is_empty() {
|
|
return Err(DomConversionError::other_error(
|
|
"both default and field are used in the same register",
|
|
node,
|
|
));
|
|
}
|
|
if let (Some(mask), Some(default)) = (mask, default) {
|
|
if default & !(mask) != 0 {
|
|
log::warn!(
|
|
"default value {} doesn't fit mask {}: {} - {}",
|
|
default,
|
|
mask,
|
|
node.document().text_pos_at(node.range().start),
|
|
node.document().text_pos_at(node.range().end)
|
|
)
|
|
}
|
|
}
|
|
|
|
Ok(Register {
|
|
name,
|
|
addr,
|
|
r#type,
|
|
mask,
|
|
modf,
|
|
multiple,
|
|
default,
|
|
desc,
|
|
elements: children,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Memory {
|
|
pub(crate) fn from_xml_dom(_node: Node) -> Result<Self, DomConversionError> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
impl Fifo {
|
|
pub(crate) fn from_xml_dom(_node: Node) -> Result<Self, DomConversionError> {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
impl Field {
|
|
pub(crate) fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
|
let name = util::get_name(node)?;
|
|
let mask = node
|
|
.attribute("mask")
|
|
.map(|addr| {
|
|
addr.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "mask", node))
|
|
})
|
|
.transpose()?
|
|
.ok_or_else(|| DomConversionError::attr_not_found("mask", node))?;
|
|
let interface = node
|
|
.attribute("interface")
|
|
.map(|s| {
|
|
s.parse()
|
|
.map_err(|e| DomConversionError::parse_enum_error(e, "interface", node))
|
|
})
|
|
.transpose()?;
|
|
let multiple = MultipleParams::from_xml_dom(node)?;
|
|
let default = node
|
|
.attribute("default")
|
|
.map(|x| {
|
|
x.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "default", node))
|
|
})
|
|
.transpose()?;
|
|
let sclr = node
|
|
.attribute("sclr")
|
|
.map(|s| {
|
|
s.parse_custom_bool()
|
|
.map_err(|e| DomConversionError::parse_enum_error(e, "sclr", node))
|
|
})
|
|
.transpose()?;
|
|
let desc = node.attribute("desc").map(str::to_string);
|
|
|
|
let children = node
|
|
.children()
|
|
.filter(|node| node.is_element())
|
|
.map(Value::from_xml_dom)
|
|
.collect::<Result<_, _>>()?;
|
|
|
|
// Validation
|
|
if let Some(default) = default {
|
|
if default & !(mask) != 0 {
|
|
log::warn!(
|
|
"default value {} doesn't fit mask {}: {} - {}",
|
|
default,
|
|
mask,
|
|
node.document().text_pos_at(node.range().start),
|
|
node.document().text_pos_at(node.range().end)
|
|
)
|
|
}
|
|
}
|
|
|
|
Ok(Field {
|
|
name,
|
|
mask,
|
|
interface,
|
|
multiple,
|
|
default,
|
|
sclr,
|
|
desc,
|
|
elements: children,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Value {
|
|
pub(crate) fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
|
let name = util::get_name(node)?;
|
|
let data = match node.attribute("data") {
|
|
Some(data) => data
|
|
.parse_prefixed_u32()
|
|
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "data", node))?,
|
|
None => return Err(DomConversionError::attr_not_found("data", node)),
|
|
};
|
|
let desc = node.attribute("desc").map(str::to_string);
|
|
|
|
Ok(Value { name, data, desc })
|
|
}
|
|
}
|
|
|
|
impl MultipleParams {
|
|
pub(crate) fn from_xml_dom(node: Node) -> Result<Option<Self>, DomConversionError> {
|
|
let multiple = match node
|
|
.attribute("multiple")
|
|
.map(|x| {
|
|
x.parse()
|
|
.map_err(|e| DomConversionError::parse_int_error(e, "multiple", node))
|
|
})
|
|
.transpose()?
|
|
{
|
|
Some(x) => x,
|
|
None => return Ok(None),
|
|
};
|
|
|
|
let offset = match node
|
|
.attribute("offset")
|
|
.map(|x| {
|
|
x.parse()
|
|
.map_err(|e| DomConversionError::parse_int_error(e, "offset", node))
|
|
})
|
|
.transpose()?
|
|
{
|
|
Some(x) => x,
|
|
None => return Ok(None),
|
|
};
|
|
|
|
Ok(Some(MultipleParams { multiple, offset }))
|
|
}
|
|
}
|