endcap-sl-software-ri-gener.../src/converter.rs

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 }))
}
}