Skip to main content

liberty_parser/
ast.rs

1//! Defines the types and parser for the Abstract Syntax Tree (AST) representation of a Liberty
2//! file.
3//!
4
5use 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
15/// Result type for parsing
16pub type ParseResult<'a, T> = result::Result<T, Error<'a>>;
17
18/// Liberty file AST representation
19///
20/// Each liberty file can have one or more `library`s defined in it, which are represented as a
21/// [`GroupItem::Group`] variant.
22#[derive(Debug, Clone)]
23pub struct LibertyAst(pub Vec<GroupItem>);
24
25impl LibertyAst {
26    /// Create a new AST from a vector of `GroupItem`s
27    pub fn new(libs: Vec<GroupItem>) -> Self {
28        Self(libs)
29    }
30
31    /// Parse a Liberty file's string representation into the AST
32    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    /// Convert an AST into a [`Liberty`] struct
39    pub fn into_liberty(self) -> Liberty {
40        Liberty::from_ast(self)
41    }
42
43    /// Convert a [`Liberty`] struct into an AST
44    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
61// Recursively convert a vector of [`GroupItem`]s into a single `String`
62fn items_to_string(items: &[GroupItem], level: usize) -> String {
63    let indent = "  ".repeat(level);
64    items
65        .iter()
66        .map(|item| match item {
67            GroupItem::SimpleAttr(name, value) => {
68                format!("{}{} : {};", indent, name, value)
69            }
70            GroupItem::ComplexAttr(name, values) => format!(
71                "{}{} ({});",
72                indent,
73                name,
74                values.iter().map(|v| v.to_string()).join(", ")
75            ),
76            GroupItem::Comment(v) => format!("/*\n{}\n*/", v),
77            GroupItem::Group(type_, name, group_items) => format!(
78                "{}{} ({}) {{\n{}\n{}}}",
79                indent,
80                type_,
81                name,
82                items_to_string(group_items, level + 1),
83                indent
84            ),
85        })
86        .join("\n")
87}
88
89/// Intermediate representation
90#[derive(Debug, PartialEq, Clone)]
91pub enum GroupItem {
92    // type, name, values
93    Group(String, String, Vec<GroupItem>),
94    // name, value
95    SimpleAttr(String, Value),
96    ComplexAttr(String, Vec<Value>),
97    // contents
98    Comment(String),
99}
100
101impl GroupItem {
102    /// Convert [`Value::Float`] to `f64` or panic
103    pub fn group(&self) -> (String, String, Vec<GroupItem>) {
104        if let GroupItem::Group(type_, name, items) = self {
105            (String::from(type_), String::from(name), items.clone())
106        } else {
107            panic!("Not variant GroupItem::Group");
108        }
109    }
110}
111
112/// Liberty value type
113///
114/// A wide range of types are defined for the Liberty syntax. Because there is little to no way
115/// to parse enumerated types from the syntax alone, enumerated types are parsed as the
116/// [`Value::Expression`] variant.
117#[derive(Debug, PartialEq, Clone)]
118pub enum Value {
119    /// Boolean value, parsed from the keywords `true` and `false`
120    Bool(bool),
121    /// Floating point value.
122    ///
123    /// All numbers are parsed into `f64`. While the Liberty specification differentiates between
124    /// integers and floating point values on a per-field basis, all are parsed into an `f64`.
125    Float(f64),
126    /// Group of floating point values in quotation marks
127    ///
128    /// For example, this complex attribute
129    ///
130    /// ```text
131    /// values ( \
132    ///   "1.0, 2.0, 3.0", \
133    ///   "4.0, 5.0, 6.0" \
134    /// );
135    /// ```
136    ///
137    /// will be parsed into a `Vec<Value::FloatGroup>`.
138    FloatGroup(Vec<f64>),
139    /// String enclosed in quotation marks
140    String(String),
141    /// Expression
142    ///
143    /// Enumerated values, such as the `delay_model` simple attribute,  are parsed as a
144    /// [`Value::Expression`].
145    Expression(String),
146}
147
148impl fmt::Display for Value {
149    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150        match self {
151            Value::Expression(v) => v.fmt(f),
152            Value::String(v) => write!(f, "\"{}\"", v),
153            Value::Bool(v) => {
154                if *v {
155                    write!(f, "true")
156                } else {
157                    write!(f, "false")
158                }
159            }
160            Value::Float(v) => write!(f, "{}", GPoint(*v)),
161            Value::FloatGroup(v) => write!(f, "\"{}\"", v.iter().map(|x| GPoint(*x)).format(", ")),
162        }
163    }
164}
165
166impl Value {
167    /// Convert [`Value::Float`] to `f64` or panic
168    pub fn float(&self) -> f64 {
169        if let Value::Float(v) = self {
170            *v
171        } else {
172            panic!("Not a float")
173        }
174    }
175
176    /// Convert [`Value::String`] to `String` or panic
177    pub fn string(&self) -> String {
178        if let Value::String(v) = self {
179            v.clone()
180        } else {
181            panic!("Not a string")
182        }
183    }
184
185    /// Convert [`Value::Expression`] to `String` or panic
186    pub fn expr(&self) -> String {
187        if let Value::Expression(v) = self {
188            v.clone()
189        } else {
190            panic!("Not a string")
191        }
192    }
193
194    /// Convert [`Value::Bool`] to `bool` or panic
195    pub fn bool(&self) -> bool {
196        if let Value::Bool(v) = self {
197            *v
198        } else {
199            panic!("Not a bool")
200        }
201    }
202
203    /// Convert [`Value::FloatGroup`] to `Vec<f64>` or panic
204    pub fn float_group(&self) -> Vec<f64> {
205        if let Value::FloatGroup(v) = self {
206            v.clone()
207        } else {
208            panic!("Not a float group")
209        }
210    }
211}
212
213#[cfg(test)]
214mod test {
215    use super::{LibertyAst, Value};
216
217    macro_rules! parse_file {
218        ($fname:ident) => {{
219            let data = include_str!(concat!("../data/", stringify!($fname), ".lib"));
220            LibertyAst::from_string(data).unwrap()
221        }};
222    }
223
224    #[test]
225    fn test_files() {
226        parse_file!(small);
227        parse_file!(cells);
228        parse_file!(cells_timing);
229    }
230
231    #[test]
232    fn test_values() {
233        assert_eq!(Value::Bool(false).bool(), false);
234        assert_eq!(Value::Float(-3.45).float(), -3.45f64);
235        assert_eq!(Value::Expression("A & B".to_string()).expr(), "A & B");
236        assert_eq!(
237            Value::FloatGroup(vec![1.2, 3.4]).float_group(),
238            vec![1.2, 3.4]
239        );
240        assert_eq!(Value::String("abc def".to_string()).string(), "abc def");
241    }
242}