liberty_parse/
ast.rs

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