libertyparse 0.3.0

Liberty cell library parser
Documentation
//! Downgrade a new liberty library to the compatibility of an
//! old liberty library.
//!
//! `Downgrade` means deleting *new* types of unparsed content from the
//! source string and write out the result.
//! It is supposed that after downgrading, the new liberty library could
//! be as compatible as the old one and without extra confusing content.

use libertyparse::{Liberty, Unparsed};
use indexmap::IndexSet;
use std::env;
use std::fs::{self, File};
use std::ops::Range;
use std::io::{BufWriter, Write};

fn read_liberty(path: impl AsRef<std::path::Path>) -> (String, Liberty) {
    let s = fs::read_to_string(path)
        .expect("Error reading liberty source file");
    let lib = match Liberty::parse_str(&s) {
        Ok(lib) => lib,
        Err(e) => panic!("{e}")
    };
    (s, lib)
}

fn main() {
    clilog::init_stderr_color_debug();
    let args: Vec<String> = env::args().collect();
    assert!(args.len() == 4,
            "Usage: {} <old_lib_path> <new_lib_path> <write_out>",
            args[0]);

    let (_, lib_old) = read_liberty(&args[1]);
    clilog::info!("Old liberty file {}", args[1]);
    let (old_libwise, old_cellwise, old_pinwise, old_timingwise) =
        lib_old.debug_report_unparsed();

    let (s_new, lib_new) = read_liberty(&args[2]);
    clilog::info!("New liberty file {}", args[2]);
    let (mut new_libwise, mut new_cellwise, mut new_pinwise, mut new_timingwise) =
        lib_new.debug_report_unparsed();

    for s in old_libwise {
        new_libwise.remove(&s);
    }
    for s in old_cellwise {
        new_cellwise.remove(&s);
    }
    for s in old_pinwise {
        new_pinwise.remove(&s);
    }
    for s in old_timingwise {
        new_timingwise.remove(&s);
    }

    clilog::info!("Removing {} lib-wise items: {:?}",
                  new_libwise.len(), new_libwise);
    clilog::info!("Removing {} cell-wise items: {:?}",
                  new_cellwise.len(), new_cellwise);
    clilog::info!("Removing {} pin-wise items: {:?}",
                  new_pinwise.len(), new_pinwise);
    clilog::info!("Removing {} timing-wise items: {:?}",
                  new_timingwise.len(), new_timingwise);

    let mut removed_spans: Vec<Range<usize>> = Vec::new();
    let mut insert_into_removed_spans =
        |v: &Vec<Unparsed>, filt: &IndexSet<&str>| {
            for u in v {
                if filt.contains(u.keyword.as_str()) {
                    removed_spans.push(u.span.clone());
                }
            }
        };

    for (_, lib) in &lib_new.libs {
        insert_into_removed_spans(&lib.unparsed, &new_libwise);
        for (_, cell) in &lib.cells {
            insert_into_removed_spans(&cell.unparsed, &new_cellwise);
            for (_, pin) in &cell.pins {
                insert_into_removed_spans(&pin.unparsed, &new_pinwise);
                for timing in &pin.timings {
                    insert_into_removed_spans(&timing.unparsed, &new_timingwise);
                }
            }
        }
    }

    println!("Removing {} snippets from {} and saving to {}",
             removed_spans.len(), args[2], args[3]);

    // sort the removed spans, and write out the file.
    // we enumerate carefully among the gaps between removed spans.
    removed_spans[..].sort_unstable_by_key(|rng| rng.start);

    let f = File::create(&args[3]).unwrap();
    let mut f = BufWriter::new(f);
    let mut i = 0;  // current byte index
    for span in removed_spans {
        f.write(s_new[i..span.start].as_bytes()).unwrap();
        i = span.end;
    }
    f.write(s_new[i..].as_bytes()).unwrap();
}