1use std::{fmt, result};
6
7use crate::error::Error;
8use crate::liberty::Liberty;
9use crate::parser::parse_libs;
10
11use gpoint::GPoint;
12use itertools::Itertools;
13use nom::error::VerboseError;
14
15pub type ParseResult<'a, T> = result::Result<T, Error<'a>>;
17
18#[derive(Debug, Clone)]
23pub struct LibertyAst(pub Vec<GroupItem>);
24
25impl LibertyAst {
26 pub fn new(libs: Vec<GroupItem>) -> Self {
28 Self(libs)
29 }
30
31 pub fn from_string(input: &str) -> ParseResult<'_, Self> {
33 parse_libs::<VerboseError<&str>>(input)
34 .map_err(|e| Error::new(input, e))
35 .map(|(_, libs)| LibertyAst::new(libs))
36 }
37
38 pub fn into_liberty(self) -> Liberty {
40 Liberty::from_ast(self)
41 }
42
43 pub fn from_liberty(lib: Liberty) -> Self {
45 lib.to_ast()
46 }
47}
48
49impl fmt::Display for LibertyAst {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 write!(f, "{}", items_to_string(&self.0, 0))
52 }
53}
54
55impl From<Liberty> for LibertyAst {
56 fn from(liberty: Liberty) -> Self {
57 LibertyAst::from_liberty(liberty)
58 }
59}
60
61fn continuation(align: usize) -> String {
63 format!(" \\\n{}", " ".repeat(align))
64}
65
66fn items_to_string(items: &[GroupItem], level: usize) -> String {
68 let indent = " ".repeat(level);
69 items
70 .iter()
71 .map(|item| match item {
72 GroupItem::SimpleAttr(name, Value::String(s)) if s.contains('\n') => format!(
76 "{}{} : \"{}\";",
77 indent,
78 name,
79 s.replace('\n', &continuation(indent.len() + name.len() + 4))
80 ),
81 GroupItem::SimpleAttr(name, value) => {
82 format!("{}{} : {};", indent, name, value)
83 }
84 GroupItem::ComplexAttr(name, values) => {
85 let multiline =
86 values.len() > 1 && values.iter().all(|v| matches!(v, Value::FloatGroup(_)));
87 let sep = if multiline {
90 format!(",{}", continuation(indent.len() + name.len() + 2))
91 } else {
92 ", ".to_string()
93 };
94 format!(
95 "{}{} ({});",
96 indent,
97 name,
98 values.iter().map(|v| v.to_string()).join(&sep)
99 )
100 }
101 GroupItem::Comment(v) => format!("/*\n{}\n*/", v),
102 GroupItem::Group(type_, name, group_items) => format!(
103 "{}{} ({}) {{\n{}\n{}}}",
104 indent,
105 type_,
106 name,
107 items_to_string(group_items, level + 1),
108 indent
109 ),
110 })
111 .join("\n")
112}
113
114#[derive(Debug, PartialEq, Clone)]
116pub enum GroupItem {
117 Group(String, String, Vec<GroupItem>),
119 SimpleAttr(String, Value),
121 ComplexAttr(String, Vec<Value>),
122 Comment(String),
124}
125
126impl GroupItem {
127 pub fn group(&self) -> (String, String, Vec<GroupItem>) {
129 if let GroupItem::Group(type_, name, items) = self {
130 (String::from(type_), String::from(name), items.clone())
131 } else {
132 panic!("Not variant GroupItem::Group");
133 }
134 }
135}
136
137#[derive(Debug, PartialEq, Clone)]
143pub enum Value {
144 Bool(bool),
146 Float(f64),
151 FloatGroup(Vec<f64>),
164 String(String),
166 Expression(String),
171}
172
173impl fmt::Display for Value {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 match self {
176 Value::Expression(v) => v.fmt(f),
177 Value::String(v) => write!(f, "\"{}\"", v),
178 Value::Bool(v) => {
179 if *v {
180 write!(f, "true")
181 } else {
182 write!(f, "false")
183 }
184 }
185 Value::Float(v) => write!(f, "{}", GPoint(*v)),
186 Value::FloatGroup(v) => write!(f, "\"{}\"", v.iter().map(|x| GPoint(*x)).format(", ")),
187 }
188 }
189}
190
191impl Value {
192 pub fn float(&self) -> f64 {
194 if let Value::Float(v) = self {
195 *v
196 } else {
197 panic!("Not a float")
198 }
199 }
200
201 pub fn string(&self) -> String {
203 if let Value::String(v) = self {
204 v.clone()
205 } else {
206 panic!("Not a string")
207 }
208 }
209
210 pub fn expr(&self) -> String {
212 if let Value::Expression(v) = self {
213 v.clone()
214 } else {
215 panic!("Not a string")
216 }
217 }
218
219 pub fn bool(&self) -> bool {
221 if let Value::Bool(v) = self {
222 *v
223 } else {
224 panic!("Not a bool")
225 }
226 }
227
228 pub fn float_group(&self) -> Vec<f64> {
230 if let Value::FloatGroup(v) = self {
231 v.clone()
232 } else {
233 panic!("Not a float group")
234 }
235 }
236}
237
238#[cfg(test)]
239mod test {
240 use super::{LibertyAst, Value};
241
242 macro_rules! parse_file {
243 ($fname:ident) => {{
244 let data = include_str!(concat!("../data/", stringify!($fname), ".lib"));
245 LibertyAst::from_string(data).unwrap()
246 }};
247 }
248
249 #[test]
250 fn test_files() {
251 parse_file!(small);
252 parse_file!(cells);
253 parse_file!(cells_timing);
254 }
255
256 #[test]
257 fn test_values() {
258 assert!(!Value::Bool(false).bool());
259 assert_eq!(Value::Float(-3.45).float(), -3.45f64);
260 assert_eq!(Value::Expression("A & B".to_string()).expr(), "A & B");
261 assert_eq!(
262 Value::FloatGroup(vec![1.2, 3.4]).float_group(),
263 vec![1.2, 3.4]
264 );
265 assert_eq!(Value::String("abc def".to_string()).string(), "abc def");
266 }
267}