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#[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 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 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 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#[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 map(
599 (path_str, space1, key_str),
600 |(path_str, _, (section_str, _))| Some((path_str, section_str.to_lowercase())),
601 ),
602 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