tf_parser 0.1.0

technology(.tf) file parser in Rust
Documentation
use super::base_parser::{
    boolean_number, float, float_list, number_list, positive_number, qstring, ws,
};

use nom::{
    branch::{alt, permutation},
    bytes::complete::tag,
    combinator::{map, opt},
    error::context,
    sequence::{delimited, preceded, tuple},
};

use crate::{
    model::{TfCutRule, TfDensityRule, TfDesignRule, TfMetalRule, TfPRRule, TfPolyRule, TfRule},
    TfRes,
};

fn layer_pair(input: &str) -> TfRes<&str, (&str, &str)> {
    tuple((
        preceded(tuple((ws(tag("layer1")), ws(tag("=")))), qstring),
        preceded(tuple((ws(tag("layer2")), ws(tag("=")))), qstring),
    ))(input)
}

fn routelayer_rule(input: &str) -> TfRes<&str, TfMetalRule> {
    let (input, data) = tuple((
        preceded(tuple((ws(tag("minSpacing")), ws(tag("=")))), float),
        opt(preceded(
            tuple((ws(tag("stackable")), ws(tag("=")))),
            boolean_number,
        )),
    ))(input)?;
    Ok((
        input,
        TfMetalRule {
            minspacing: data.0,
            stackable: data.1.map_or(false, |x| x),
        },
    ))
}

fn cutlayer_rule(input: &str) -> TfRes<&str, TfCutRule> {
    let (input, data) = permutation((
        preceded(
            tuple((ws(tag("endOfLineEnc2NeighborTblSize")), ws(tag("=")))),
            positive_number,
        ),
        preceded(
            tuple((ws(tag("endOfLineEnc2NeighborThreshold")), ws(tag("=")))),
            float,
        ),
        preceded(
            tuple((
                ws(tag("endOfLineEnc2NeighborCornerKeepoutWidth")),
                ws(tag("=")),
            )),
            float,
        ),
        preceded(
            tuple((
                ws(tag("endOfLineEnc2NeighborSideKeepoutLength")),
                ws(tag("=")),
            )),
            float,
        ),
        preceded(
            tuple((ws(tag("endOfLineEnc2NeighborSideMinSpacing")), ws(tag("=")))),
            float,
        ),
        preceded(
            tuple((ws(tag("endOfLineEnc2NeighborMinLength")), ws(tag("=")))),
            float,
        ),
        preceded(
            tuple((ws(tag("endOfLineEnc2NeighborTbl")), ws(tag("=")))),
            float_list,
        ),
        preceded(
            tuple((ws(tag("endOfLineEnc2NeighborSpacingTbl")), ws(tag("=")))),
            float_list,
        ),
        preceded(
            tuple((
                ws(tag("endOfLineEnc2NeighborViaArrayExcludedTbl")),
                ws(tag("=")),
            )),
            float_list,
        ),
        preceded(
            tuple((
                ws(tag("endOfLineEnc2NeighborWireMinThreshold")),
                ws(tag("=")),
            )),
            float,
        ),
    ))(input)?;

    Ok((
        input,
        TfCutRule {
            tblsize: data.0,
            threshold: data.1,
            cornerkeepout_width: data.2,
            sidekeepout_length: data.3,
            sideminspacing: data.4,
            minlength: data.5,
            tbl: data.6,
            spacing_tbl: data.7,
            viaarray_excluded_tbl: data.8,
            wire_minthreshold: data.9,
        },
    ))
}

fn polylayer_rule(input: &str) -> TfRes<&str, TfPolyRule> {
    let (input, data) = tuple((
        preceded(
            tuple((ws(tag("fatWireViaEncTblSize")), ws(tag("=")))),
            positive_number,
        ),
        preceded(
            tuple((ws(tag("fatWireViaEncWidthThresholdTbl")), ws(tag("=")))),
            float_list,
        ),
        preceded(
            tuple((
                ws(tag("fatWireViaEncParallelLengthThresholdTbl")),
                ws(tag("=")),
            )),
            float_list,
        ),
        preceded(
            tuple((ws(tag("fatWireViaEncMaxSpacingThresholdTbl")), ws(tag("=")))),
            float_list,
        ),
        preceded(
            tuple((ws(tag("fatWireViaEnclosureTbl")), ws(tag("=")))),
            float_list,
        ),
        preceded(
            tuple((ws(tag("fatWireViaArrayExcludedTbl")), ws(tag("=")))),
            number_list,
        ),
    ))(input)?;
    Ok((
        input,
        TfPolyRule {
            fat_wire_via_enc_tbl_size: data.0,
            fat_wire_via_enc_width_threshold_tbl: data.1,
            fat_wire_via_enc_parallel_length_threshold_tbl: data.2,
            fat_wire_via_enc_max_spacing_threshold_tbl: data.3,
            fat_wire_via_enclosure_tbl: data.4,
            fat_wire_via_array_excluded_tbl: data.5,
        },
    ))
}

pub fn designrule_parser(input: &str) -> TfRes<&str, TfDesignRule> {
    context(
        "DesignRule Section",
        preceded(
            ws(tag("DesignRule")),
            delimited(
                ws(tag("{")),
                tuple((
                    layer_pair,
                    alt((
                        map(routelayer_rule, |x| TfRule::MetalRule(x)),
                        map(cutlayer_rule, |x| TfRule::CutRule(x)),
                        map(polylayer_rule, |x| TfRule::PolyRule(x)),
                    )),
                )),
                ws(tag("}")),
            ),
        ),
    )(input)
    .map(|(res, data)| {
        (
            res,
            TfDesignRule {
                layer1: (data.0).0.to_string(),
                layer2: (data.0).1.to_string(),
                rule_data: data.1,
            },
        )
    })
}

pub fn pr_rule_parser(input: &str) -> TfRes<&str, TfPRRule> {
    context(
        "PR Rule Section",
        preceded(
            ws(tag("PRRule")),
            delimited(
                ws(tag("{")),
                tuple((
                    preceded(tuple((ws(tag("rowSpacingTopTop")), ws(tag("=")))), float),
                    opt(preceded(
                        tuple((ws(tag("rowSpacingTopBot")), ws(tag("=")))),
                        float,
                    )),
                    preceded(tuple((ws(tag("rowSpacingBotBot")), ws(tag("=")))), float),
                    preceded(
                        tuple((ws(tag("abuttableTopTop")), ws(tag("=")))),
                        positive_number,
                    ),
                    preceded(
                        tuple((ws(tag("abuttableTopBot")), ws(tag("=")))),
                        positive_number,
                    ),
                    preceded(
                        tuple((ws(tag("abuttableBotBot")), ws(tag("=")))),
                        positive_number,
                    ),
                )),
                ws(tag("}")),
            ),
        ),
    )(input)
    .map(|(res, data)| {
        (
            res,
            TfPRRule {
                rowspacing_toptop: data.0,
                rowspacing_topbot: data.1,
                rowspacing_botbot: data.2,
                abuttable_toptop: data.3,
                abuttabletopbot: data.4,
                abuttablebotbot: data.5,
            },
        )
    })
}

pub fn density_rule_parser(input: &str) -> TfRes<&str, TfDensityRule> {
    context(
        "DensityRule Section",
        preceded(
            ws(tag("DensityRule")),
            delimited(
                ws(tag("{")),
                tuple((
                    preceded(tuple((ws(tag("layer")), ws(tag("=")))), qstring),
                    preceded(
                        tuple((ws(tag("windowSize")), ws(tag("=")))),
                        positive_number,
                    ),
                    preceded(
                        tuple((ws(tag("minDensity")), ws(tag("=")))),
                        positive_number,
                    ),
                    preceded(
                        tuple((ws(tag("maxDensity")), ws(tag("=")))),
                        positive_number,
                    ),
                )),
                ws(tag("}")),
            ),
        ),
    )(input)
    .map(|(res, data)| {
        (
            res,
            TfDensityRule {
                layer: data.0.to_string(),
                window_size: data.1,
                min_density: data.2,
                max_density: data.3,
            },
        )
    })
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_designrule() {
        let input = "DesignRule {
        layer1              = \"V3\"
        layer2              = \"V2\"
        minSpacing          = 0
        stackable           = 1
}
";
        let (_, _) = designrule_parser(input).unwrap();
    }

    #[test]
    fn test_designrule2() {
        let input = "DesignRule    {
            layer1              = \"via4Blockage\"
            layer2              = \"V4\"
            minSpacing          = 0.1
    }";
        let (_, _) = designrule_parser(input).unwrap();
    }

    #[test]
    fn test_designrule3() {
        let input = "DesignRule     {
            layer1 = \"M\"
            layer2 = \"CT\"
            fatWireViaEncTblSize = 1
            fatWireViaEncWidthThresholdTbl = (  0.121  )
            fatWireViaEncParallelLengthThresholdTbl = (  0.27  )
            fatWireViaEncMaxSpacingThresholdTbl = (  0.081  )
            fatWireViaEnclosureTbl = (  0.015  )
            fatWireViaArrayExcludedTbl = (1)
    }";
        let (_, _) = designrule_parser(input).unwrap();
    }

    #[test]
    fn test_designrule4() {
        let input = "DesignRule     {
            layer1 = \"M3\"
            layer2 = \"V2\"
            endOfLineEnc2NeighborTblSize = 7
            endOfLineEnc2NeighborThreshold = 0.099
            endOfLineEnc2NeighborCornerKeepoutWidth = 0.031
            endOfLineEnc2NeighborSideKeepoutLength = 0.096
            endOfLineEnc2NeighborSideMinSpacing = 0.08
            endOfLineEnc2NeighborMinLength = 0.069
            endOfLineEnc2NeighborTbl = (0.045, 0.04, 0.035, 0.03, 0.025, 0.02, 0.015)
            endOfLineEnc2NeighborSpacingTbl = (0.075, 0.08, 0.085, 0.09, 0.095, 0.100, 0.105)
            endOfLineEnc2NeighborViaArrayExcludedTbl = (0, 0, 0, 0, 0, 0, 0)
            endOfLineEnc2NeighborWireMinThreshold = 0.07
    }   ";
        let (_, _) = designrule_parser(input).unwrap();
    }

    #[test]
    fn test_prrule() {
        let input = "PRRule     {
        rowSpacingTopTop        = 0
        rowSpacingBotBot        = 0
        abuttableTopTop         = 1
        abuttableTopBot         = 0
        abuttableBotBot         = 1
}";
        let (_, _) = pr_rule_parser(input).unwrap();
    }

    #[test]
    fn test_density_rule() {
        let input = "DensityRule    {
        layer               = \"TM2\"
        windowSize                      = 200
        minDensity          = 20
        maxDensity          = 85
}";
        let (_, _) = density_rule_parser(input).unwrap();
    }
}