use std::{fmt, result};
use crate::error::Error;
use crate::liberty::Liberty;
use crate::parser::parse_libs;
use gpoint::GPoint;
use itertools::Itertools;
use nom::error::VerboseError;
pub type ParseResult<'a, T> = result::Result<T, Error<'a>>;
#[derive(Debug, Clone)]
pub struct LibertyAst(pub Vec<GroupItem>);
impl LibertyAst {
pub fn new(libs: Vec<GroupItem>) -> Self {
Self(libs)
}
pub fn from_string(input: &str) -> ParseResult<'_, Self> {
parse_libs::<VerboseError<&str>>(input)
.map_err(|e| Error::new(input, e))
.map(|(_, libs)| LibertyAst::new(libs))
}
pub fn into_liberty(self) -> Liberty {
Liberty::from_ast(self)
}
pub fn from_liberty(lib: Liberty) -> Self {
lib.to_ast()
}
}
impl fmt::Display for LibertyAst {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", items_to_string(&self.0, 0))
}
}
impl From<Liberty> for LibertyAst {
fn from(liberty: Liberty) -> Self {
LibertyAst::from_liberty(liberty)
}
}
fn continuation(align: usize) -> String {
format!(" \\\n{}", " ".repeat(align))
}
fn items_to_string(items: &[GroupItem], level: usize) -> String {
let indent = " ".repeat(level);
items
.iter()
.map(|item| match item {
GroupItem::SimpleAttr(name, Value::String(s)) if s.contains('\n') => format!(
"{}{} : \"{}\";",
indent,
name,
s.replace('\n', &continuation(indent.len() + name.len() + 4))
),
GroupItem::SimpleAttr(name, value) => {
format!("{}{} : {};", indent, name, value)
}
GroupItem::ComplexAttr(name, values) => {
let multiline =
values.len() > 1 && values.iter().all(|v| matches!(v, Value::FloatGroup(_)));
let sep = if multiline {
format!(",{}", continuation(indent.len() + name.len() + 2))
} else {
", ".to_string()
};
format!(
"{}{} ({});",
indent,
name,
values.iter().map(|v| v.to_string()).join(&sep)
)
}
GroupItem::Comment(v) => format!("/*\n{}\n*/", v),
GroupItem::Group(type_, name, group_items) => format!(
"{}{} ({}) {{\n{}\n{}}}",
indent,
type_,
name,
items_to_string(group_items, level + 1),
indent
),
})
.join("\n")
}
#[derive(Debug, PartialEq, Clone)]
pub enum GroupItem {
Group(String, String, Vec<GroupItem>),
SimpleAttr(String, Value),
ComplexAttr(String, Vec<Value>),
Comment(String),
}
impl GroupItem {
pub fn group(&self) -> (String, String, Vec<GroupItem>) {
if let GroupItem::Group(type_, name, items) = self {
(String::from(type_), String::from(name), items.clone())
} else {
panic!("Not variant GroupItem::Group");
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
Bool(bool),
Float(f64),
FloatGroup(Vec<f64>),
String(String),
Expression(String),
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Value::Expression(v) => v.fmt(f),
Value::String(v) => write!(f, "\"{}\"", v),
Value::Bool(v) => {
if *v {
write!(f, "true")
} else {
write!(f, "false")
}
}
Value::Float(v) => write!(f, "{}", GPoint(*v)),
Value::FloatGroup(v) => write!(f, "\"{}\"", v.iter().map(|x| GPoint(*x)).format(", ")),
}
}
}
impl Value {
pub fn float(&self) -> f64 {
if let Value::Float(v) = self {
*v
} else {
panic!("Not a float")
}
}
pub fn string(&self) -> String {
if let Value::String(v) = self {
v.clone()
} else {
panic!("Not a string")
}
}
pub fn expr(&self) -> String {
if let Value::Expression(v) = self {
v.clone()
} else {
panic!("Not a string")
}
}
pub fn bool(&self) -> bool {
if let Value::Bool(v) = self {
*v
} else {
panic!("Not a bool")
}
}
pub fn float_group(&self) -> Vec<f64> {
if let Value::FloatGroup(v) = self {
v.clone()
} else {
panic!("Not a float group")
}
}
}
#[cfg(test)]
mod test {
use super::{LibertyAst, Value};
macro_rules! parse_file {
($fname:ident) => {{
let data = include_str!(concat!("../data/", stringify!($fname), ".lib"));
LibertyAst::from_string(data).unwrap()
}};
}
#[test]
fn test_files() {
parse_file!(small);
parse_file!(cells);
parse_file!(cells_timing);
}
#[test]
fn test_values() {
assert!(!Value::Bool(false).bool());
assert_eq!(Value::Float(-3.45).float(), -3.45f64);
assert_eq!(Value::Expression("A & B".to_string()).expr(), "A & B");
assert_eq!(
Value::FloatGroup(vec![1.2, 3.4]).float_group(),
vec![1.2, 3.4]
);
assert_eq!(Value::String("abc def".to_string()).string(), "abc def");
}
}