netlist_db/parser/
data_meas.rs

1#![expect(clippy::type_complexity)]
2use std::path::PathBuf;
3
4use super::{
5    BEGIN_TITLE,
6    utils::{name_str, space, space_newline},
7};
8use crate::{err::ParseError, span::LocatedSpan};
9use nom::{
10    IResult, Parser,
11    branch::alt,
12    bytes::complete::{tag, take_until},
13    character::complete::char,
14    combinator::{map, opt},
15    multi::{many1, separated_list1},
16    sequence::delimited,
17};
18
19const FAILED_MEAS: &str = "failed";
20
21#[inline]
22fn float(i: LocatedSpan) -> IResult<LocatedSpan, Option<f64>> {
23    alt((
24        map(super::utils::float, Some),
25        map(tag(FAILED_MEAS), |_| None),
26    ))
27    .parse_complete(i)
28}
29
30#[inline]
31fn data_meas_csv_nom(
32    i: LocatedSpan<'_>,
33) -> IResult<LocatedSpan<'_>, (Vec<&str>, Vec<Vec<Option<f64>>>)> {
34    map(
35        (
36            take_until(BEGIN_TITLE),
37            take_until("\n"),
38            space_newline,
39            separated_list1((space, char(','), space), map(name_str, |(s, _)| s)),
40            opt(char('#')),
41            opt((space, char(','))),
42            many1(delimited(
43                space_newline,
44                separated_list1((space, char(','), space), float),
45                opt((space, char(','))),
46            )),
47        ),
48        |(_, _, _, names, _, _, value_table): (_, _, _, Vec<&str>, _, _, Vec<Vec<Option<f64>>>)| {
49            (names, value_table)
50        },
51    )
52    .parse_complete(i)
53}
54
55pub struct MeasCols<'a> {
56    names: Vec<&'a str>,
57    idx: usize,
58    vt: &'a [Vec<Option<f64>>],
59    prefix: Option<&'a str>,
60}
61
62impl<'a> MeasCols<'a> {
63    pub fn new(
64        names: Vec<&'a str>,
65        value_table: &'a Vec<Vec<Option<f64>>>,
66        data_prefix: Option<&'a str>,
67    ) -> Self {
68        Self {
69            names,
70            idx: 0,
71            vt: value_table,
72            prefix: data_prefix,
73        }
74    }
75}
76
77pub struct Col<'a> {
78    vt: &'a [Vec<Option<f64>>],
79    col: usize,
80    row: usize,
81}
82
83impl<'a> Iterator for MeasCols<'a> {
84    type Item = (&'a str, Col<'a>);
85
86    fn next(&mut self) -> Option<Self::Item> {
87        while self.idx < self.names.len() {
88            let i = self.idx;
89            let name = self.names[i];
90            self.idx += 1;
91
92            if let Some(p) = self.prefix {
93                if !name.starts_with(p) {
94                    continue;
95                }
96            }
97
98            return Some((
99                name,
100                Col {
101                    vt: self.vt,
102                    col: i,
103                    row: 0,
104                },
105            ));
106        }
107        None
108    }
109}
110
111impl<'a> Iterator for Col<'a> {
112    type Item = Option<f64>;
113
114    fn next(&mut self) -> Option<Self::Item> {
115        if self.row >= self.vt.len() {
116            return None;
117        }
118        let v = self.vt[self.row][self.col]; // Option<f64>: Copy
119        self.row += 1;
120        Some(v)
121    }
122}
123
124#[inline]
125pub fn data_meas_csv(path: PathBuf, s: &str) -> Option<(Vec<&str>, Vec<Vec<Option<f64>>>)> {
126    match data_meas_csv_nom(s.into()) {
127        Ok((_, out)) => Some(out),
128        Err(e) => {
129            let err: ParseError = e.into();
130            err.report(&mut true, &crate::FileId::Include { path }, s);
131            None
132        }
133    }
134}
135
136#[test]
137fn sim_mt0_csv() {
138    crate::utlis::test::init_logger();
139    const DATA: &str = include_str!("../../tests/sim.mt0.csv");
140    let (names, value_table) =
141        data_meas_csv(PathBuf::from("tests/sim.mt0.csv"), DATA.into()).unwrap();
142    for (k, v) in MeasCols::new(names, &value_table, Some("kcell")) {
143        dbg!(k, v.collect::<Vec<_>>());
144    }
145}