use std::fmt::Display;
use std::{collections::HashMap, str::FromStr};
use crate::error::{LibErrorContext, LibError};
use crate::model::{LibAst, LibCell, LibGroup, LibLibrary, LibPin, LibPinDirection};
pub trait FromLibGroup: Sized {
type Error: Sized;
fn from_group(group: LibGroup) -> Result<Self, Self::Error>;
}
impl LibLibrary {
pub fn from_ast(ast: LibAst) -> Result<Self, LibError> {
let group = LibGroup::from_ast(ast).context(|| "semantic root ast to group")?;
Self::from_group(group)
}
}
impl FromLibGroup for LibLibrary {
type Error = LibError;
fn from_group(group: LibGroup) -> Result<Self, LibError> {
let mut group = group;
let name = group.expect_name().context(|| "group name")?;
let (cell_groups, groups) = group.groups.into_iter().partition(|g| g.kind == "cell");
let mut cells = Vec::new();
for cell_group in cell_groups {
cells.push(LibCell::from_group(cell_group).context(|| "semantic cell")?);
}
Ok(Self {
name,
attrs: group.attrs,
groups,
cells,
})
}
}
impl FromLibGroup for LibCell {
type Error = LibError;
fn from_group(group: LibGroup) -> Result<Self, LibError> {
let mut group = group;
let name = group.expect_name().context(|| "cell name")?;
let err_context = |msg: &str| {
format!("{} in {} cell", msg, &name)
};
let (pin_groups, groups) = group.groups.into_iter().partition(|g| g.kind == "pin");
let mut pins = Vec::new();
for cell_group in pin_groups {
pins.push(LibPin::from_group(cell_group).context(|| err_context("semantic pin"))?);
}
Ok(Self {
name,
pins,
attrs: group.attrs,
groups,
})
}
}
impl FromLibGroup for LibPin {
type Error = LibError;
fn from_group(group: LibGroup) -> Result<Self, LibError> {
let mut group = group;
let name = group.expect_name().context(|| "pin name")?;
let err_context = |msg: &str| {
format!("{} in `{}` pin", msg, &name)
};
let direction = match group.expect_ident_attr_opt("direction")
.context(|| err_context("semantic `direction` attr"))?
{
Some(s) => LibPinDirection::from_str(&s)?,
None => LibPinDirection::default(),
};
let function = group.expect_string_attr_opt("function")
.context(|| err_context("semantic `function` attr"))?;
Ok(Self {
name,
direction,
function,
attrs: group.attrs,
groups: group.groups,
})
}
}
impl FromStr for LibPinDirection {
type Err = LibError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"input" => Ok(Self::Input),
"output" => Ok(Self::Output),
"inout" => Ok(Self::Inout),
"internal" => Ok(Self::Internal),
s => Err(LibError::UnsupportDirection(s.to_string()))
}
}
}
impl Display for LibPinDirection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Input => write!(f, "input"),
Self::Output => write!(f, "output"),
Self::Inout => write!(f, "inout"),
Self::Internal => write!(f, "internal"),
}
}
}
impl LibGroup {
pub fn from_ast(ast: LibAst) -> Result<Self, LibError> {
match ast {
LibAst::LibGroup { kind, params, items } => {
let mut attrs = HashMap::new();
let mut groups = Vec::new();
for item_ast in items {
if let LibAst::Attr { key, value } = item_ast {
attrs.insert(key, value);
} else {
groups.push(LibGroup::from_ast(item_ast)?);
}
}
Ok(Self {
kind,
params,
attrs,
groups
})
}
LibAst::Attr { key:_, value:_ } => Err(LibError::ExceptLibGroup),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_pin_into_group() {
let mut pin = LibPin::new("my_pin");
pin.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
}
#[test]
fn test_pin_from_group() {
let mut group = LibGroup::new("pin", vec!["a".to_string()]);
group.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
let pin = LibPin::from_group(group).unwrap();
assert_eq!(pin.name, "a");
assert_eq!(pin.groups.len(), 1);
}
#[test]
fn test_cell_into_group() {
let mut cell = LibCell::new("my_cell");
cell.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
cell.pins.push(LibPin::new("a"));
cell.pins.push(LibPin::new("b"));
}
#[test]
fn test_cell_from_group() {
let mut group = LibGroup::new("cell", vec!["AND2".to_string()]);
group.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
group.groups.push(LibGroup::new("pin", vec!["a".to_string()]));
group.groups.push(LibGroup::new("pin", vec!["b".to_string()]));
let cell = LibCell::from_group(group).unwrap();
assert_eq!(cell.name, "AND2");
assert_eq!(cell.groups.len(), 1);
assert_eq!(cell.pins.len(), 2);
}
#[test]
fn test_library_into_group() {
let mut lib = LibLibrary::new("my_lib");
lib.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
lib.cells.push(LibCell::new("AND2"));
lib.cells.push(LibCell::new("NAND2"));
}
#[test]
fn test_lib_from_group() {
let mut group = LibGroup::new("library", vec!["mylib".to_string()]);
group.groups.push(LibGroup::new("gtype", vec!["gname".to_string()]));
let mut cell = LibGroup::new("cell", vec!["AND2".to_string()]);
cell.groups.push(LibGroup::new("pin", vec!["a".to_string()]));
cell.groups.push(LibGroup::new("pin", vec!["b".to_string()]));
group.groups.push(cell);
let lib = LibLibrary::from_group(group).unwrap();
assert_eq!(lib.name, "mylib");
assert_eq!(lib.groups.len(), 1);
assert_eq!(lib.cells.len(), 1);
let converted_cell = lib.cell("AND2").unwrap();
assert_eq!(converted_cell.name, "AND2");
assert_eq!(converted_cell.groups.len(), 0);
assert_eq!(converted_cell.pins.len(), 2);
}
}