Metadata-Version: 2.4
Name: eda-liberty-parser
Version: 0.1.0
Summary: A pure-Python parser for Liberty (.lib) files — reads, edits, and writes standard-cell and pad library data for EDA automation workflows
Author-email: Rohan Chadhury <rohaanshahid@gmail.com>
License: MIT License
        
        Copyright (c) 2025 Rohaan Scherpbier
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/rohaansch/liberty-parser
Keywords: eda,liberty,lib,cell library,pad library,vlsi,timing,power,pins,parser,semiconductor
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: color
Requires-Dist: termcolor>=1.1; extra == "color"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: termcolor>=1.1; extra == "dev"
Dynamic: license-file

# liberty-parser

A pure-Python parser for **Liberty** (`.lib`) cell-library files — the
industry-standard format used by EDA tools to describe the timing, power,
and interface of standard cells.

[![CI](https://github.com/rohaansch/liberty-parser/actions/workflows/ci.yml/badge.svg)](https://github.com/rohaansch/liberty-parser/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/python-3.7%2B-blue)](https://pypi.org/project/liberty-parser/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

## Features

- **Full Liberty grammar** — comments, simple attributes, complex attributes,
  and arbitrarily deep group blocks
- **Round-trip write** — `write_liberty()` reconstructs the file with the
  exact original whitespace and formatting
- **In-place editing** — `set_val()`, `set_vals()`, `set_LU_matrix()`,
  `add_simple()`, `add_complex()`, `add_block()`, `remove_block()`,
  `merge_blocks()`
- **Hierarchical navigation** — `navigate()` with exact-match or `{RE}regex`
  filters, `navigate_to_base()` to walk up the tree
- **Convenience helpers** — `get_cell_list()`, `get_pin_list()`,
  `map_LU_indexes()`
- **Parse from string** — no file required; pass a Liberty text block directly
- **Zero required dependencies** — only the Python standard library
- Optional colored output via [`termcolor`](https://pypi.org/project/termcolor/)
- CLI included: `liberty-parser cells.lib --cell INV_X1 --pins`

## Installation

```bash
pip install eda-liberty-parser                  # no extra dependencies
pip install "eda-liberty-parser[color]"         # adds termcolor for colored output
```

[![PyPI](https://img.shields.io/pypi/v/eda-liberty-parser)](https://pypi.org/project/eda-liberty-parser/)
[![Python](https://img.shields.io/pypi/pyversions/eda-liberty-parser)](https://pypi.org/project/eda-liberty-parser/)

Or for development:

```bash
git clone https://github.com/rohaansch/liberty-parser
cd liberty-parser
pip install -e ".[dev]"
```

## Quick start

```python
from liberty_parser import LP

lib = LP("cells.lib")

print(lib.get_cell_list())
# ['INV_X1', 'AND2_X1', 'DFFS_X2']

print(lib.get_pin_list(cell="INV_X1"))
# ['A', 'ZN']

# Read a library attribute
time_unit = lib.navigate_single('library', 'time_unit').get_val()
print(time_unit)
# 1ns
```

## Navigation

`navigate()` accepts a chain of filter strings, one per hierarchy level:

```python
# All cells in the library
cells = lib.navigate('library', 'cell')

# Specific cell by name
inv = lib.navigate_single('library', 'cell:INV_X1')

# Pin direction of a specific pin
direction = lib.navigate_single('library', 'cell:INV_X1', 'pin:ZN', 'direction')
print(direction.get_val())
# output

# Regex filter — find all *_template blocks
for tmpl in lib.navigate('library', '{RE}_template$'):
    print(tmpl.get_val())
```

`navigate_single()` returns the first match (or `None`).
`navigate_singles()` is the fastest variant — stops at the first match at
every level.

## In-place editing

All edits modify the in-memory dict and are written back with `write_liberty()`:

```python
# Change a simple attribute value
lib.navigate_single('library', 'time_unit').set_val('2ns')

# Or use the two-argument shorthand (nav_to, new_value)
lib.navigate_single('library').set_val('voltage_unit', '1.8V')

# Edit a lookup-table 2-D matrix
cell_rise = lib.navigate_single(
    'library', 'cell:INV_X1', 'pin:ZN', 'timing', 'cell_rise'
)
matrix = cell_rise.get_LU_matrix()
matrix[0][0] = '0.15'
cell_rise.set_LU_matrix(matrix)

# Write back to disk
lib.write_liberty("cells_modified.lib")
```

## Structural edits

```python
library = lib.navigate_single('library')

# Add a new simple attribute
library.add_simple('nom_voltage', '1.8')

# Add a new complex attribute
library.add_complex('operating_conditions', 'typical')

# Remove a cell
cell = lib.navigate_single('library', 'cell:INV_X1')
cell.remove_block()

# Merge another Liberty block into an existing one
lib.navigate_single('library').merge_blocks('extra_cells.lib')
```

## Parsing from a string

No file needed — pass Liberty text directly:

```python
snippet = 'library (mini) { voltage_unit : "1V"; nom_voltage : 1.8; }'
mini = LP(snippet)
print(mini.navigate_single('library', 'nom_voltage').get_val())
# 1.8
```

## Element type predicates

```python
from liberty_parser import is_comment, is_simple_attribute, is_complex_attribute, is_block

for element in lib.navigate('library'):
    if is_block(element):
        print(f"Block: {element.get_val()}")
    elif is_simple_attribute(element):
        print(f"Attr:  {element.get_val()}")
```

## API reference

### `LP(arg=None)`

| Argument | Behavior |
|---|---|
| `None` | Create empty LP object |
| `filepath` (`.lib` file) | Parse the file |
| `text` (Liberty string) | Parse the string as a Liberty block |
| `dict` | Wrap an existing internal dict |
| `LP` | Share the internal dict of another LP object |

### Navigation

| Method | Description |
|---|---|
| `navigate(*filters)` | Return all matching LP objects |
| `navigate_single(*filters)` | Return first match at last filter |
| `navigate_singles(*filters)` | Return first match at every filter (fastest) |
| `navigate_to_base()` | Walk up to the file root |

### Getters

| Method | Description |
|---|---|
| `get_val(nav_to=None)` | Return value or name string |
| `get_vals(nav_to=None)` | Return comma-separated value as a list |
| `get_LU_matrix(nav_to=None)` | Return LU table as a 2-D list |
| `get_type()` | Return the element type string |
| `get_cell_list()` | Return all cell names |
| `get_pin_list(cell=None)` | Return pin names for one or all cells |
| `map_LU_indexes()` | Return transition/load index map |

### Setters

| Method | Description |
|---|---|
| `set_val(nav_or_val, val=None)` | Set a simple or complex attribute value |
| `set_vals(nav_or_vals, vals=None)` | Replace comma-separated value list |
| `set_LU_matrix(nav_or_matrix, matrix=None)` | Replace LU table 2-D matrix |

### Structural edits

| Method | Description |
|---|---|
| `add_simple(name, value, insert_ndx=0)` | Insert a simple attribute |
| `add_complex(type, name, insert_ndx=0)` | Insert a complex attribute |
| `add_block(lp_block, insert_ndx=0)` | Insert an LP element |
| `add_blocks(input, insert_ndx=0)` | Parse and insert multiple elements |
| `remove_block(ndx=None)` | Remove element at index, or remove self |
| `move_block(from_index, to_index)` | Reorder elements |
| `merge_blocks(blocks_input)` | Deep merge another Liberty block |

### Write & debug

| Method | Description |
|---|---|
| `write_liberty(out_path)` | Write parsed dict back to a `.lib` file |
| `print(indent=0)` | Pretty-print the internal dict |

## Command-line usage

```
liberty-parser cells.lib
liberty-parser cells.lib --cells
liberty-parser cells.lib --cell INV_X1
liberty-parser cells.lib --cell INV_X1 --pins
liberty-parser cells.lib --attr voltage_unit
liberty-parser --version
```

Or without installing:

```
python -m liberty_parser cells.lib --cell INV_X1 --pins
```

## Running the tests

```bash
pip install -e ".[dev]"
pytest -v
```

## Companion projects

- [sdf-parser](https://github.com/rohaansch/sdf-parser) — Standard Delay Format
- [verilog-parser](https://github.com/rohaansch/verilog-parser) — Verilog cell-library interfaces

## License

[MIT](LICENSE)
