netlist_db/parser/
utils.rs

1use std::{
2    path::Path,
3    sync::{Arc, LazyLock},
4};
5
6use indexmap::IndexMap;
7use nom::{
8    IResult, Input, Parser,
9    branch::alt,
10    bytes::complete::{tag, take, take_till, take_while, take_while1},
11    character::{char, complete::digit1},
12    combinator::{map, map_res, opt},
13    error::ErrorKind,
14    multi::{many0, many1},
15    sequence::{delimited, preceded},
16};
17use regex::Regex;
18
19use super::{ast, manager::ParseManager};
20use crate::{
21    ast::{ASTBuilder, LocalAST, SubcktBuilder},
22    err::ParseErrorInner,
23    parser::{
24        cmds::{init_condition, nodeset},
25        instance::instance,
26    },
27};
28use crate::{
29    ast::{
30        DataBuilder, DataFileBuilder, DataFilesBuilder, DataValuesBuilder, KeyValueBuilder,
31        ModelBuilder, PnameColNumBuilder, TokenBuilder, UnknwonBuilder, ValueBuilder,
32    },
33    span::{EndReason, FileId, LocatedSpan, Pos, Span},
34};
35
36#[inline]
37pub(super) fn space(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
38    take_while(|c: char| matches!(c, '\t' | '\r' | ' ')).parse_complete(i)
39}
40#[inline]
41pub(super) fn space1(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
42    take_while1(|c: char| matches!(c, '\t' | '\r' | ' ')).parse_complete(i)
43}
44#[inline]
45pub(super) fn space_newline(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
46    take_while(|c: char| matches!(c, '\t' | '\r' | ' ' | '\n')).parse_complete(i)
47}
48
49#[inline]
50pub(super) fn multiline_sep<'a, T>(
51    f: fn(LocatedSpan<'a>) -> IResult<LocatedSpan<'a>, T>,
52) -> impl MyParser<'a, T> {
53    alt((
54        preceded(space1, f),
55        map(
56            (comment_space_newline, char('+'), space, f),
57            |(_, _, _, t)| t,
58        ),
59    ))
60}
61
62#[inline]
63pub(super) fn loss_sep(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
64    alt((
65        map((comment_space_newline, char('+'), space), |(_, _, s)| s),
66        space,
67    ))
68    .parse_complete(i)
69}
70
71#[inline]
72pub(super) fn key(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
73    map(take_while1(is_key), |s: LocatedSpan| s.into()).parse_complete(i)
74}
75
76enum Unit {
77    Factor(f64),
78    DB,
79}
80
81// hspice/index.htm#page/hspice_5/about_hspice_measurement_system.htm#wwID0EOGJM
82#[inline]
83fn unit(i: LocatedSpan) -> IResult<LocatedSpan, Unit> {
84    map_res(take_while1(char::is_alphanumeric), |s: LocatedSpan| match s
85        .fragment()
86        .to_uppercase()
87        .as_str()
88    {
89        "T" => Ok(Unit::Factor(1e+12)),
90        "G" => Ok(Unit::Factor(1e+9)),
91        "ME" | "MEG" | "X" | "Z" => Ok(Unit::Factor(1e+6)),
92        "K" => Ok(Unit::Factor(1e+3)),
93        "MI" | "MIL" => Ok(Unit::Factor(25.4e-6)),
94        "U" => Ok(Unit::Factor(1e-6)),
95        "N" => Ok(Unit::Factor(1e-9)),
96        "P" => Ok(Unit::Factor(1e-12)),
97        "F" => Ok(Unit::Factor(1e-15)),
98        "A" => Ok(Unit::Factor(1e-18)),
99        "DB" => Ok(Unit::DB),
100        "MIN" => Ok(Unit::Factor(60.0)),
101        "HR" => Ok(Unit::Factor(3600.0)),
102        "DAY" => Ok(Unit::Factor(86400.0)),
103        "YR" => Ok(Unit::Factor(31536000.0)),
104        _ => Err(ErrorKind::Float),
105    })
106    .parse_complete(i)
107}
108
109#[inline]
110pub(super) fn _float(i: LocatedSpan) -> IResult<LocatedSpan, f64> {
111    match fast_float2::parse_partial(i) {
112        Ok((f, pos)) => Ok((i.take_from(pos), f)),
113        Err(_) => Err(nom::Err::Error(nom::error::Error::new(i, ErrorKind::Float))),
114    }
115}
116
117#[inline]
118pub(super) fn float_unit(i: LocatedSpan) -> IResult<LocatedSpan, f64> {
119    map((_float, opt(unit)), |(f, u)| match u {
120        Some(Unit::Factor(u)) => f * u,
121        Some(Unit::DB) => 10.0_f64.powf(f / 20.0),
122        None => f,
123    })
124    .parse_complete(i)
125}
126
127#[inline]
128pub(super) fn key_str(i: LocatedSpan) -> IResult<LocatedSpan, (&str, Span)> {
129    map(take_while1(is_key), |s: LocatedSpan| {
130        let _s: &str = s.fragment();
131        (_s, s.into())
132    })
133    .parse_complete(i)
134}
135
136#[inline]
137fn is_path(c: char) -> bool {
138    c.is_alphanumeric() || !c.is_whitespace()
139}
140
141#[inline]
142fn is_key(c: char) -> bool {
143    c.is_alphanumeric() || c == '_'
144}
145fn is_name(c: char) -> bool {
146    c.is_alphanumeric() || "/_.+-*^:@".contains(c)
147}
148fn is_formula(c: char) -> bool {
149    c.is_alphanumeric() || "/_.+-*^:".contains(c)
150}
151
152#[inline]
153pub(super) fn path(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
154    alt((
155        unquote,
156        map(take_while1(is_path), |s: LocatedSpan| s.into()),
157    ))
158    .parse_complete(i)
159}
160
161#[inline]
162pub(super) fn integer(i: LocatedSpan) -> IResult<LocatedSpan, usize> {
163    map_res(digit1, |s: LocatedSpan| s.parse()).parse_complete(i)
164}
165
166#[inline]
167pub(super) fn path_str(i: LocatedSpan) -> IResult<LocatedSpan, &str> {
168    alt((
169        unquote_str,
170        map(take_while1(is_path), |s: LocatedSpan| {
171            let _s: &str = s.fragment();
172            _s
173        }),
174    ))
175    .parse_complete(i)
176}
177
178#[inline]
179pub(super) fn name_char(i: LocatedSpan) -> IResult<LocatedSpan, (u8, Span)> {
180    map(take_while1(is_name), |s: LocatedSpan| {
181        (s.fragment().as_bytes()[0], s.into())
182    })
183    .parse_complete(i)
184}
185
186#[inline]
187pub(super) fn name(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
188    map(take_while1(is_name), |s: LocatedSpan| s.into()).parse_complete(i)
189}
190
191#[inline]
192pub(super) fn name_str(i: LocatedSpan) -> IResult<LocatedSpan, (&str, Span)> {
193    map(take_while1(is_name), |s: LocatedSpan| {
194        let _s: &str = s.fragment();
195        (_s, s.into())
196    })
197    .parse_complete(i)
198}
199
200#[inline]
201pub(super) fn formula(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
202    map(take_while1(is_formula), |s: LocatedSpan| s.into()).parse_complete(i)
203}
204
205#[inline]
206pub(super) fn unquote(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
207    map(
208        delimited(char('\''), take_till(|c| c == '\''), take(1_usize)),
209        |s: LocatedSpan| s.into(),
210    )
211    .parse_complete(i)
212}
213
214#[inline]
215pub(super) fn unquote_str(i: LocatedSpan) -> IResult<LocatedSpan, &str> {
216    map(
217        delimited(char('\''), take_till(|c| c == '\''), take(1_usize)),
218        |s: LocatedSpan| {
219            let _s: &str = s.fragment();
220            _s
221        },
222    )
223    .parse_complete(i)
224}
225
226#[inline]
227pub(super) fn comment(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
228    delimited(tag("*"), take_till(|c| c == '\n'), take(1_usize)).parse_complete(i)
229}
230
231#[inline]
232pub(super) fn comment_space_newline(i: LocatedSpan) -> IResult<LocatedSpan, LocatedSpan> {
233    preceded(many0((space_newline, comment)), space_newline).parse_complete(i)
234}
235
236#[inline]
237pub(super) fn value(i: LocatedSpan) -> IResult<LocatedSpan, ValueBuilder> {
238    if let Ok((i, s)) = unquote.parse_complete(i) {
239        return Ok((i, ValueBuilder::Expr(s)));
240    }
241    match (float_unit.parse_complete(i), formula.parse_complete(i)) {
242        (Ok((i_num, num)), Ok((i_formula, formula))) => {
243            // when the len of rest of formula is less than num
244            // means there is a non-quote expression, like `2+2`
245            if i_formula.len() < i_num.len() {
246                Ok((i_formula, ValueBuilder::Expr(formula)))
247            } else {
248                Ok((i_num, ValueBuilder::Num(num)))
249            }
250        }
251        (Ok((i_num, num)), Err(_)) => Ok((i_num, ValueBuilder::Num(num))),
252        (Err(_), Ok((i_formula, formula))) => Ok((i_formula, ValueBuilder::Expr(formula))),
253        (Err(e), Err(_)) => Err(e),
254    }
255}
256
257pub(super) trait MyParser<'a, T>:
258    Parser<LocatedSpan<'a>, Output = T, Error = nom::error::Error<LocatedSpan<'a>>>
259{
260}
261
262impl<'a, T, P> MyParser<'a, T> for P where
263    P: Parser<LocatedSpan<'a>, Output = T, Error = nom::error::Error<LocatedSpan<'a>>>
264{
265}
266
267#[inline]
268pub(super) fn equal<'a, T, F: MyParser<'a, T>>(f: F) -> impl MyParser<'a, T> {
269    map((space, char('='), space, f), |(_, _, _, v)| v)
270}
271
272#[inline]
273pub(super) fn key_value(i: LocatedSpan) -> IResult<LocatedSpan, KeyValueBuilder> {
274    map((name, equal(value)), |(k, v)| KeyValueBuilder { k, v }).parse_complete(i)
275}
276
277#[inline]
278pub(super) fn token(input: LocatedSpan) -> IResult<LocatedSpan, TokenBuilder> {
279    alt((
280        map(key_value, TokenBuilder::KV),
281        map(v, TokenBuilder::V),
282        map(i, TokenBuilder::I),
283        map(value, TokenBuilder::Value),
284    ))
285    .parse_complete(input)
286}
287
288#[inline]
289pub(super) fn option(i: LocatedSpan) -> IResult<LocatedSpan, (Span, Option<ValueBuilder>)> {
290    alt((
291        map(key_value, |kv| (kv.k, Some(kv.v))),
292        map(name, |k| (k, None)),
293    ))
294    .parse_complete(i)
295}
296#[inline]
297pub(super) fn v(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
298    map(
299        (
300            alt((char('V'), char('v'))),
301            loss_sep,
302            char('('),
303            loss_sep,
304            name,
305            loss_sep,
306            char(')'),
307        ),
308        |(_, _, _, _, name, _, _)| name,
309    )
310    .parse_complete(i)
311}
312#[inline]
313pub(super) fn i(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
314    map(
315        (
316            alt((char('I'), char('i'))),
317            loss_sep,
318            char('('),
319            loss_sep,
320            name,
321            loss_sep,
322            char(')'),
323        ),
324        |(_, _, _, _, name, _, _)| name,
325    )
326    .parse_complete(i)
327}
328
329pub(super) fn many0_dummyfirst<'a, T, F>(
330    mut f: F,
331) -> impl FnMut(LocatedSpan<'a>) -> IResult<LocatedSpan<'a>, Vec<T>>
332where
333    F: MyParser<'a, T>,
334    T: Default,
335{
336    move |mut i: LocatedSpan| {
337        let mut acc = Vec::with_capacity(4);
338        acc.push(T::default());
339        loop {
340            let len = i.input_len();
341            match f.parse_complete(i) {
342                Err(nom::Err::Error(_)) => return Ok((i, acc)),
343                Err(e) => return Err(e),
344                Ok((i1, o)) => {
345                    // infinite loop check: the parser must always consume
346                    if i1.input_len() == len {
347                        return Err(nom::Err::Error(nom::error::Error::new(i, ErrorKind::Many0)));
348                    }
349
350                    i = i1;
351                    acc.push(o);
352                }
353            }
354        }
355    }
356}
357
358#[inline]
359pub(super) fn ports_params(
360    i: LocatedSpan,
361) -> IResult<LocatedSpan, (Vec<Span>, Vec<KeyValueBuilder>)> {
362    map(
363        (
364            many1(multiline_sep(name)),
365            opt((equal(value), many0_dummyfirst(multiline_sep(key_value)))),
366        ),
367        |(mut ports, _params)| match _params {
368            Some((first_value, mut params)) => {
369                let first_key = ports.pop().unwrap();
370                params[0] = KeyValueBuilder {
371                    k: first_key,
372                    v: first_value,
373                };
374                (ports, params)
375            }
376            None => (ports, Vec::new()),
377        },
378    )
379    .parse_complete(i)
380}
381#[inline]
382pub(super) fn data(mut i: LocatedSpan) -> IResult<LocatedSpan, DataBuilder> {
383    #[inline]
384    fn enddata(i: LocatedSpan) -> IResult<LocatedSpan, ()> {
385        map_res((char('.'), key_str), |(_, (key, _))| {
386            if key.to_uppercase().as_str() == "ENDDATA" {
387                Ok(())
388            } else {
389                // TODO: error information?
390                Err(())
391            }
392        })
393        .parse_complete(i)
394    }
395    #[inline]
396    fn data_files(i: LocatedSpan) -> IResult<LocatedSpan, DataFilesBuilder> {
397        #[inline]
398        fn file(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
399            map_res(
400                (multiline_sep(name_str), equal(path)),
401                |((key, _), path)| {
402                    if key.to_uppercase().as_str() == "FILE" {
403                        Ok(path)
404                    } else {
405                        Err(())
406                    }
407                },
408            )
409            .parse_complete(i)
410        }
411        #[inline]
412        fn out(i: LocatedSpan) -> IResult<LocatedSpan, Span> {
413            map_res(
414                (multiline_sep(name_str), equal(path)),
415                |((key, _), path)| {
416                    if key.to_uppercase().as_str() == "OUT" {
417                        Ok(path)
418                    } else {
419                        Err(())
420                    }
421                },
422            )
423            .parse_complete(i)
424        }
425        #[inline]
426        fn pname_col_num(i: LocatedSpan) -> IResult<LocatedSpan, PnameColNumBuilder> {
427            map_res(
428                (multiline_sep(name_str), equal(integer)),
429                |((pname_str, pname), col_num)| {
430                    let binding = pname_str.to_uppercase();
431                    let s = binding.as_str();
432                    if s != "FILE" && s != "OUT" {
433                        Ok(PnameColNumBuilder { pname, col_num })
434                    } else {
435                        Err(())
436                    }
437                },
438            )
439            .parse_complete(i)
440        }
441        map(
442            (
443                many1(map(
444                    (file, many1(pname_col_num)),
445                    |(file, pname_col_num)| DataFileBuilder {
446                        file,
447                        pname_col_num,
448                    },
449                )),
450                opt(out),
451                space_newline,
452                enddata,
453            ),
454            |(files, out, _, _)| DataFilesBuilder { files, out },
455        )
456        .parse_complete(i)
457    }
458    let name;
459    (i, name) = multiline_sep(key).parse_complete(i)?;
460    let first;
461    let first_str;
462    (i, (first_str, first)) = multiline_sep(name_str).parse_complete(i)?;
463    match first_str.to_uppercase().as_str() {
464        "MER" => {
465            return data_files.parse_complete(i).map(|(i, data_files)| {
466                (
467                    i,
468                    DataBuilder {
469                        name,
470                        values: DataValuesBuilder::MER(data_files),
471                    },
472                )
473            });
474        }
475        "LAM" => {
476            return data_files.parse_complete(i).map(|(i, data_files)| {
477                (
478                    i,
479                    DataBuilder {
480                        name,
481                        values: DataValuesBuilder::LAM(data_files),
482                    },
483                )
484            });
485        }
486        _ => {}
487    }
488    let mut params = vec![first];
489    loop {
490        match multiline_sep(float_unit).parse_complete(i) {
491            Ok((_i, first_n)) => {
492                return map(
493                    (
494                        many0_dummyfirst(multiline_sep(float_unit)),
495                        space_newline,
496                        opt(enddata),
497                    ),
498                    |(mut values, _, _)| {
499                        values[0] = first_n;
500                        values
501                    },
502                )
503                .parse_complete(_i)
504                .map(|(i, values)| {
505                    (
506                        i,
507                        DataBuilder {
508                            name,
509                            values: DataValuesBuilder::InlineNum { params, values },
510                        },
511                    )
512                });
513            }
514            Err(_) => {
515                let param;
516                let param_str;
517                (i, (param_str, param)) = multiline_sep(name_str).parse_complete(i)?;
518                if param_str.to_uppercase().as_str() == "DATAFORM" {
519                    return map(
520                        (many1(multiline_sep(value)), space_newline, opt(enddata)),
521                        |(values, _, _)| values,
522                    )
523                    .parse_complete(i)
524                    .map(|(i, values)| {
525                        (
526                            i,
527                            DataBuilder {
528                                name,
529                                values: DataValuesBuilder::InlineExpr { params, values },
530                            },
531                        )
532                    });
533                } else {
534                    params.push(param);
535                }
536            }
537        }
538    }
539}
540
541/// ``` spice
542/// .MODEL mname ModelType ([level=val]
543/// + [keyword1=val1][keyword2=val2]
544/// + [keyword3=val3][LOT distribution value]
545/// + [DEV distribution value]...)
546/// ```
547#[inline]
548pub(super) fn model(i: LocatedSpan) -> IResult<LocatedSpan, ModelBuilder> {
549    map(
550        (
551            multiline_sep(key),
552            multiline_sep(key_str),
553            alt((
554                many1(multiline_sep(key_value)),
555                map(
556                    (
557                        loss_sep,
558                        char('('),
559                        loss_sep,
560                        opt((key_value, many0_dummyfirst(multiline_sep(key_value)))),
561                        loss_sep,
562                        char(')'),
563                    ),
564                    |(_, _, _, v, _, _)| {
565                        if let Some((first, mut vec)) = v {
566                            vec[0] = first;
567                            vec
568                        } else {
569                            Vec::new()
570                        }
571                    },
572                ),
573            )),
574        ),
575        |(name, model_type_ctx, params)| ModelBuilder {
576            name,
577            model_type: model_type_ctx.into(),
578            params,
579        },
580    )
581    .parse_complete(i)
582}
583
584#[inline]
585fn endlib(i: LocatedSpan) -> IResult<LocatedSpan, ()> {
586    static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new("(?i)\\.endl").unwrap());
587    match RE.find(i.fragment()) {
588        Some(m) => Ok((i.take_from(m.end()), ())),
589        None => Err(nom::Err::Error(nom::error::Error::new(
590            i,
591            ErrorKind::RegexpCapture,
592        ))),
593    }
594}
595pub(super) fn lib(i: LocatedSpan) -> IResult<LocatedSpan, Option<(&str, String)>> {
596    alt((
597        // include lib section
598        map(
599            (path_str, space1, key_str),
600            |(path_str, _, (section_str, _))| Some((path_str, section_str.to_lowercase())),
601        ),
602        // skip to `.endl`
603        map((endlib, opt((space1, key))), |_| None),
604    ))
605    .parse_complete(i)
606}
607#[inline]
608pub(super) fn subckt<'a>(
609    i: LocatedSpan<'a>,
610    loaded: &IndexMap<FileId, Option<Pos>>,
611    manager: &Arc<ParseManager>,
612    work_dir: &Path,
613) -> IResult<LocatedSpan<'a>, SubcktBuilder> {
614    let ast_subckt = |i: LocatedSpan<'a>| -> IResult<LocatedSpan<'a>, ASTBuilder> {
615        ast(manager.clone(), loaded.clone(), work_dir.to_path_buf(), i)
616    };
617    map(
618        (space1, name, ports_params, space_newline, ast_subckt),
619        |(_, name, (ports, params), _, ast)| SubcktBuilder {
620            name,
621            ports,
622            params,
623            ast,
624        },
625    )
626    .parse_complete(i)
627}
628
629#[inline]
630pub(super) fn local_ast<'a>(
631    mut i: LocatedSpan<'a>,
632    loaded: &IndexMap<FileId, Option<Pos>>,
633    manager: &Arc<ParseManager>,
634    work_dir: &Path,
635) -> IResult<LocatedSpan<'a>, (LocalAST, EndReason<'a>)> {
636    let mut ast = LocalAST::default();
637    loop {
638        log::trace!("\n{:?}", i.fragment());
639        (i, _) = comment_space_newline(i)?;
640        if i.is_empty() {
641            return Ok((i, (ast, EndReason::End)));
642        }
643        match char('.').parse_complete(i) {
644            Err(nom::Err::Error(_)) => {
645                let inst;
646                (i, inst) = instance(i)?;
647                ast.instance.push(inst);
648            }
649            Err(e) => return Err(e),
650            Ok((_i, _)) => {
651                i = _i;
652                let cmd;
653                let cmd_str;
654                (i, (cmd_str, cmd)) = key_str(i)?;
655                match cmd_str.to_lowercase().as_str() {
656                    "lib" => {
657                        let lib_info;
658                        (i, (_, lib_info)) = (space1, lib).parse_complete(i)?;
659                        if let Some((file_name_str, section_str)) = lib_info {
660                            return Ok((
661                                i,
662                                (
663                                    ast,
664                                    EndReason::Include {
665                                        file_name: Path::new(file_name_str),
666                                        section: Some(section_str),
667                                    },
668                                ),
669                            ));
670                        }
671                    }
672                    "inc" | "include" => {
673                        let file_name;
674                        (i, (_, file_name)) = (space1, path_str).parse_complete(i)?;
675                        return Ok((
676                            i,
677                            (
678                                ast,
679                                EndReason::Include {
680                                    file_name: Path::new(file_name),
681                                    section: None,
682                                },
683                            ),
684                        ));
685                    }
686                    "model" => {
687                        let _model;
688                        (i, _model) = model(i)?;
689                        ast.model.push(_model);
690                    }
691                    "subckt" => {
692                        let _subckt;
693                        (i, _subckt) = subckt(i, loaded, manager, work_dir)?;
694                        ast.subckt.push(_subckt);
695                    }
696                    "data" => {
697                        let _data;
698                        (i, _data) = data(i)?;
699                        ast.data.push(_data);
700                    }
701                    "option" => {
702                        let options;
703                        (i, options) = many1(multiline_sep(option)).parse_complete(i)?;
704                        ast.option.extend(options);
705                    }
706                    "param" | "parameter" => {
707                        let param;
708                        (i, param) = many1(multiline_sep(key_value)).parse_complete(i)?;
709                        ast.param.extend(param);
710                    }
711                    "ic" => {
712                        let _init_condition;
713                        (i, _init_condition) = init_condition(i)?;
714                        ast.init_condition.extend(_init_condition);
715                    }
716                    "nodeset" => {
717                        let _nodeset;
718                        (i, _nodeset) = nodeset(i)?;
719                        ast.nodeset.extend(_nodeset);
720                    }
721                    "ends" => {
722                        (i, _) = opt((space1, key)).parse_complete(i)?;
723                        return Ok((i, (ast, EndReason::End)));
724                    }
725                    "end" => {
726                        (i, _) = many0((loss_sep, name)).parse_complete(i)?;
727                    }
728                    _ => {
729                        ast.errors.push(ParseErrorInner::Unknown(cmd).record(i));
730                        let tokens;
731                        (i, tokens) = many0(multiline_sep(token)).parse_complete(i)?;
732                        ast.unknwon.push(UnknwonBuilder { cmd, tokens })
733                    }
734                }
735            }
736        }
737    }
738}
739
740// #[cfg(test)]
741// mod test {
742//     macro_rules! assert_ctx {
743//         ($i_res:expr, $ctx:expr $(,)?) => {
744//             assert_eq!($ctx, $i_res.unwrap().1.ctx);
745//         };
746//     }
747//     use std::path::PathBuf;
748
749//     use crate::file::TestLocatedSpan;
750
751//     // macro_rules! assert_kv {
752//     //     ($i_res:expr, $k:expr, $v:expr $(,)?) => {{
753//     //         let kv = $i_res.unwrap().1;
754//     //         assert_eq!($k, kv.k.ctx);
755//     //         assert_eq!($v, kv.v.ctx);
756//     //     }};
757//     // }
758//     // macro_rules! assert_token {
759//     //     ($i_res:expr, $k:expr, $v:expr $(,)?) => {{
760//     //         let token = $i_res.unwrap().1;
761//     //         if let TokenBuilder::KV(kv) = token {
762//     //             assert_eq!($k, kv.k.ctx);
763//     //             assert_eq!($v, kv.v.ctx);
764//     //         } else {
765//     //             panic!("should be key-value!")
766//     //         }
767//     //     }};
768//     //     ($i_res:expr, $word:expr $(,)?) => {{
769//     //         let token = $i_res.unwrap().1;
770//     //         if let TokenBuilder::ValueBuilder(word) = token {
771//     //             assert_eq!($word, word.ctx);
772//     //         } else {
773//     //             panic!("should be word!")
774//     //         }
775//     //     }};
776//     // }
777//     use super::*;
778//     #[test]
779//     fn test_num() {
780//         assert_eq!(1.233, float_unit(span("1.233 ")).unwrap().1);
781//         assert_eq!(1.233e-6, float_unit(span("1.233u ")).unwrap().1);
782//     }
783//     #[test]
784//     fn test_key() {
785//         assert_ctx!(key(span("iw_ww ")), "iw_ww");
786//         assert_ctx!(key(span("iw.ww ")), "iw");
787//         assert_ctx!(name(span("iw.ww ")), "iw.ww");
788//         assert_ctx!(formula(span("8.00000000e-01+2")), "8.00000000e-01+2");
789//         assert_ctx!(unquote(span("'8.00000000e-01 + 2'")), "8.00000000e-01 + 2");
790//         // assert_kv!(
791//         //     key_value(span("a='8.00000000e-01 + 2'")),
792//         //     "a",
793//         //     "8.00000000e-01 + 2"
794//         // );
795//         // assert!(formula(span("'8.00000000e-01 + 2'")).is_err());
796//         // assert_token!(token(span("a")), "a");
797//         // assert_token!(
798//         //     token(span("a='8.00000000e-01 + 2'")),
799//         //     "a",
800//         //     "8.00000000e-01 + 2"
801//         // );
802//         crate::text_diff("'2+2'", value(span("2+2")).unwrap().1.to_string().as_str());
803//         crate::text_diff(
804//             "R1 node1 node2 var1='key1'",
805//             instance(span("R1 node1 node2 var1=key1"))
806//                 .unwrap()
807//                 .1
808//                 .to_string()
809//                 .as_str(),
810//         );
811//         crate::text_diff(
812//             "R1 node1 node2 var1='key1' var2='key2'",
813//             instance(span("R1 node1 node2 var1=key1 var2=key2"))
814//                 .unwrap()
815//                 .1
816//                 .to_string()
817//                 .as_str(),
818//         );
819//         let (manager, _) = ParseManager::new();
820//         let work_dir = PathBuf::new();
821//         let loaded = IndexMap::new();
822//         crate::text_diff(
823//             r#"
824// .MODEL nch_mac NMOS
825// + level=2 version=4.5
826// .SUBCKT INV0SR_12TH40 I ZN VDD VSS
827// XX0 ZN I VSS VPW NHVT11LL_CKT W=0.00000031 L=0.00000004
828// XX3 ZN I VDD VNW PHVT11LL_CKT W=0.00000027 L=0.00000004
829// .ENDS INV0SR_12TH40
830// R1 node1 node2 var1='key1' var2='key2'
831// R2 node1 node2 var1='key1' var2='key2'
832// .mode 'nch_mac' 'nmos' level=2"#,
833//             local_ast(
834//                 span(
835//                     r#"R1 node1 node2 var1=key1 var2=key2
836//         R2 node1 node2 var1=key1
837//         +var2=key2
838//         .model nch_mac nmos level=2
839//         + version=4.5
840//         .SUBCKT INV0SR_12TH40 I ZN VDD VSS
841// XX0 ZN I VSS VPW NHVT11LL_CKT W=310.00n L=40.00n
842// XX3 ZN I VDD VNW PHVT11LL_CKT W=270.00n L=40.00n
843// .ENDS INV0SR_12TH40
844// .mode 'nch_mac' 'nmos' level=2
845//         "#,
846//                 ),
847//                 &loaded,
848//                 &manager,
849//                 &work_dir,
850//             )
851//             .unwrap()
852//             .1
853//             .0
854//             .to_string()
855//             .as_str(),
856//         );
857//         crate::text_diff(
858//             r#"
859// .MODEL nch_mac NMOS
860// + level=2 version=4.5
861// .SUBCKT INV0SR_12TH40 I ZN VDD VSS
862// XX0 ZN I VSS VPW NHVT11LL_CKT W=0.00000031 L=0.00000004
863// XX3 ZN I VDD VNW PHVT11LL_CKT W=0.00000027 L=0.00000004
864// .ENDS INV0SR_12TH40
865// R1 node1 node2 var1='key1' var2='key2'
866// R2 node1 node2 var1='key1' var2='key2'
867// .mode 'nch_mac' 'nmos' level=2"#,
868//             local_ast(
869//                 span(
870//                     r#"R1 node1 node2 var1=key1 var2=key2
871//         R2 node1 node2 var1=key1
872//         +var2=key2
873//         .model nch_mac
874//         + nmos (
875//             + level=2
876//         + version=4.5
877//     + )
878//         .SUBCKT INV0SR_12TH40 I ZN VDD VSS
879// XX0 ZN I VSS VPW NHVT11LL_CKT W=310.00n L=40.00n
880// XX3 ZN I VDD VNW PHVT11LL_CKT W=270.00n L=40.00n
881// .ENDS INV0SR_12TH40
882// .mode 'nch_mac' 'nmos' level=2
883//         "#,
884//                 ),
885//                 &loaded,
886//                 &manager,
887//                 &work_dir,
888//             )
889//             .unwrap()
890//             .1
891//             .0
892//             .to_string()
893//             .as_str(),
894//         );
895//         crate::text_diff(
896//             r#"
897// .DATA SWEEP
898// + var1 var2
899// + 1 2
900// + 2 4
901// .ENDDATA"#,
902//             local_ast(
903//                 span(
904//                     r#".data SWEEP
905//                     + var1 var2
906//                     + 1    2
907//                     + 2    4
908//                     .enddata
909//         "#,
910//                 ),
911//                 &loaded,
912//                 &manager,
913//                 &work_dir,
914//             )
915//             .unwrap()
916//             .1
917//             .0
918//             .to_string()
919//             .as_str(),
920//         );
921//         crate::text_diff(
922//             r#"
923// .DATA SWEEP
924// + var1 var2 DATAFORM
925// + 1 '2+2'
926// + 2 4
927// .ENDDATA"#,
928//             local_ast(
929//                 span(
930//                     r#".data SWEEP
931//                     + var1 var2 DATAFORM
932//                     + 1 2+2
933//                     + 2 4
934//                     .enddata
935//         "#,
936//                 ),
937//                 &loaded,
938//                 &manager,
939//                 &work_dir,
940//             )
941//             .unwrap()
942//             .1
943//             .0
944//             .to_string()
945//             .as_str(),
946//         );
947//         crate::text_diff(
948//             r#"
949// .DATA inputdata MER
950// + FILE='file1' p1=1 p2=3 p3=4
951// + FILE='file2' p1=1
952// + FILE='file3' p1=1
953// .ENDDATA"#,
954//             local_ast(
955//                 span(
956//                     r#".DATA inputdata MER
957//                     + FILE='file1' p1=1 p2=3 p3=4
958//                     + FILE='file2' p1=1
959//                     + FILE='file3' p1=1
960//                  .ENDDATA
961//         "#,
962//                 ),
963//                 &loaded,
964//                 &manager,
965//                 &work_dir,
966//             )
967//             .unwrap()
968//             .1
969//             .0
970//             .to_string()
971//             .as_str(),
972//         );
973//     }
974
975//     #[test]
976//     fn libmatch() {
977//         assert_eq!(
978//             TestLocatedSpan {
979//                 offset: 10,
980//                 line: 1,
981//                 fragment: " \nlines"
982//             },
983//             lib(span("some .EndL \nlines")).unwrap().0
984//         );
985//         assert_eq!(
986//             TestLocatedSpan {
987//                 offset: 13,
988//                 line: 1,
989//                 fragment: " \nlines"
990//             },
991//             lib(span("some .EndL tt \nlines")).unwrap().0
992//         );
993//         assert_eq!(
994//             TestLocatedSpan {
995//                 offset: 12,
996//                 line: 2,
997//                 fragment: " "
998//             },
999//             lib(span("some \n .EndL ")).unwrap().0
1000//         );
1001//         println!("{:?}", lib(span("'some' tt")));
1002//     }
1003// }