spice/parser/
mod.rs

1//! SPICE netlist parser.
2
3pub mod conv;
4pub mod shorts;
5#[cfg(test)]
6mod tests;
7
8use std::borrow::Borrow;
9use std::collections::{HashMap, HashSet};
10use std::fmt::Display;
11use std::iter::FusedIterator;
12use std::ops::{Deref, DerefMut};
13use std::path::{Path, PathBuf};
14use std::str::FromStr;
15
16use crate::Spice;
17use crate::parser::conv::convert_str_to_numeric_lit;
18use arcstr::ArcStr;
19use nom::Input;
20use nom::bytes::complete::{take_till, take_while};
21use thiserror::Error;
22
23use self::conv::ScirConverter;
24
25/// The type representing nodes in a parsed SPICE circuit.
26pub type Node = Substr;
27
28/// A substring of a file being parsed.
29#[derive(Clone, Default, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
30#[repr(transparent)]
31pub struct Substr(arcstr::Substr);
32
33/// The SPICE dialect to parse.
34#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
35pub enum Dialect {
36    /// Vanilla SPICE.
37    ///
38    /// Selected by default.
39    #[default]
40    Spice,
41    /// CDL.
42    Cdl,
43}
44
45/// Parses SPICE netlists.
46#[derive(Clone, Default, Eq, PartialEq, Debug)]
47pub struct Parser {
48    dialect: Dialect,
49    buffer: Vec<Token>,
50    ast: Ast,
51    state: ParserState,
52}
53
54#[derive(Clone, Default, Eq, PartialEq, Debug)]
55struct ParserState {
56    include_stack: Vec<PathBuf>,
57    reader_state: ReaderState,
58}
59
60#[derive(Clone, Default, Eq, PartialEq, Debug)]
61enum ReaderState {
62    #[default]
63    Top,
64    Subckt(Subckt),
65}
66
67/// An error parsing a SPICE dialect from a string.
68#[derive(Copy, Clone, Debug, Error)]
69#[error("error parsing SPICE dialect")]
70pub struct ParseDialectError;
71
72impl Display for Dialect {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        match self {
75            Self::Spice => write!(f, "spice"),
76            Self::Cdl => write!(f, "cdl"),
77        }
78    }
79}
80
81impl FromStr for Dialect {
82    type Err = ParseDialectError;
83
84    fn from_str(s: &str) -> Result<Self, Self::Err> {
85        match s.to_lowercase().as_str() {
86            "spice" | "sp" => Ok(Self::Spice),
87            "cdl" => Ok(Self::Cdl),
88            _ => Err(ParseDialectError),
89        }
90    }
91}
92
93impl Parser {
94    /// Makes a new parser for the given SPICE dialect.
95    pub fn new(dialect: Dialect) -> Self {
96        Self {
97            dialect,
98            ..Self::default()
99        }
100    }
101    /// Parse the given file.
102    pub fn parse_file(
103        dialect: Dialect,
104        path: impl AsRef<Path>,
105    ) -> Result<ParsedSpice, ParserError> {
106        let path = path.as_ref();
107        tracing::debug!("reading SPICE file: {:?}", path);
108        let s: ArcStr = std::fs::read_to_string(path).unwrap().into();
109        let s = Substr(arcstr::Substr::full(s));
110        let mut parser = Self::new(dialect);
111        parser.state.include_stack.push(path.into());
112        let name = match s.lines().next() {
113            Some(name) => ArcStr::from(name),
114            None => arcstr::format!("{:?}", path),
115        };
116        parser.parse_inner(s)?;
117
118        let parsed = ParsedSpice {
119            ast: parser.ast,
120            root: Some(path.to_path_buf()),
121            name,
122        };
123        Ok(parsed)
124    }
125
126    fn parse_file_inner(&mut self, path: impl AsRef<Path>) -> Result<(), ParserError> {
127        let path = path.as_ref();
128        let s: ArcStr = std::fs::read_to_string(path)
129            .map_err(|err| ParserError::FailedToRead {
130                path: path.into(),
131                err,
132            })?
133            .into();
134        let s = Substr(arcstr::Substr::full(s));
135        self.state.include_stack.push(path.into());
136        let res = self.parse_inner(s);
137        self.state.include_stack.pop().unwrap();
138        res?;
139        Ok(())
140    }
141
142    /// Parse the given string.
143    pub fn parse(dialect: Dialect, data: impl Into<Substr>) -> Result<ParsedSpice, ParserError> {
144        let data = data.into();
145        let mut parser = Self::new(dialect);
146        let name = match data.lines().next() {
147            Some(name) => ArcStr::from(name),
148            None => arcstr::literal!("spice_library"),
149        };
150        parser.parse_inner(data)?;
151
152        let parsed = ParsedSpice {
153            ast: parser.ast,
154            root: None,
155            name,
156        };
157        Ok(parsed)
158    }
159
160    fn parse_inner(&mut self, data: Substr) -> Result<(), ParserError> {
161        let mut tok = Tokenizer::new(self.dialect, data);
162        while let Some(line) = self.parse_line(&mut tok)? {
163            match (&mut self.state.reader_state, line) {
164                (ReaderState::Top, Line::SubcktDecl { name, ports }) => {
165                    self.state.reader_state = ReaderState::Subckt(Subckt {
166                        name,
167                        ports,
168                        components: vec![],
169                        connects: vec![],
170                    });
171                }
172                (ReaderState::Top, Line::Component(c)) => {
173                    self.ast.elems.push(Elem::Component(c));
174                }
175                (ReaderState::Top, Line::Include { path }) => {
176                    let resolved_path = Path::new::<str>(path.0.as_ref());
177                    let resolved_path = if resolved_path.is_relative() {
178                        let root = self
179                            .state
180                            .include_stack
181                            .last()
182                            .ok_or(ParserError::UnexpectedRelativePath(path.clone()))?;
183                        root.parent().unwrap().join(resolved_path)
184                    } else {
185                        resolved_path.into()
186                    };
187                    self.parse_file_inner(resolved_path)?;
188                }
189                (ReaderState::Subckt(subckt), Line::Component(c)) => {
190                    subckt.components.push(c);
191                }
192                (ReaderState::Subckt(subckt), Line::Connect { node1, node2 }) => {
193                    subckt.connects.push((node1, node2));
194                }
195                (ReaderState::Subckt(subckt), Line::EndSubckt) => {
196                    let subckt = std::mem::take(subckt);
197                    self.ast.elems.push(Elem::Subckt(subckt));
198                    self.state.reader_state = ReaderState::Top;
199                }
200                (_, line) => return Err(ParserError::UnexpectedLine(Box::new(line))),
201            }
202        }
203        Ok(())
204    }
205
206    fn parse_line(&mut self, tok: &mut Tokenizer) -> Result<Option<Line>, ParserError> {
207        while let Some(token) = tok.get()? {
208            if token == Token::LineEnd {
209                if let Some(line) = self.parse_line_inner()? {
210                    return Ok(Some(line));
211                }
212            } else {
213                self.buffer.push(token);
214            }
215        }
216
217        Ok(None)
218    }
219
220    fn parse_line_inner(&mut self) -> Result<Option<Line>, ParserError> {
221        let line = match self.buffer.first().unwrap() {
222            Token::Directive(d) => {
223                if d.eq_ignore_ascii_case(".subckt") {
224                    // TODO params
225                    let name = self.buffer[1].try_ident()?.clone();
226                    let ports = self.buffer[2..]
227                        .iter()
228                        .map(|tok| tok.try_ident().cloned())
229                        .collect::<Result<_, _>>()?;
230                    Line::SubcktDecl { name, ports }
231                } else if d.eq_ignore_ascii_case(".ends") {
232                    Line::EndSubckt
233                } else if d.eq_ignore_ascii_case(".include") {
234                    let mut path = self.buffer[1].try_ident()?.clone();
235                    // remove enclosing quotation marks, if any.
236                    if path.starts_with('"') {
237                        let mut chars = path.chars();
238                        chars.next().unwrap();
239                        chars.next_back().unwrap();
240                        path = Substr(path.substr_from(chars.as_str()));
241                    }
242                    Line::Include { path }
243                } else {
244                    return Err(ParserError::UnexpectedDirective(d.clone()));
245                }
246            }
247            Token::MetaDirective(d) => {
248                if d.eq_ignore_ascii_case("connect") {
249                    // TODO: assert buffer length is 3 (connect, node1, node2).
250                    if self.buffer.len() != 3 {
251                        return Err(ParserError::InvalidLine {
252                            line: self.buffer.clone(),
253                            reason: "CONNECT statements must specify exactly 2 nodes".to_string(),
254                        });
255                    }
256                    let node1 = self.buffer[1].try_ident()?.clone();
257                    let node2 = self.buffer[2].try_ident()?.clone();
258                    Line::Connect { node1, node2 }
259                } else {
260                    // Ignore this line: clear the buffer and return no line
261                    self.buffer.clear();
262                    return Ok(None);
263                }
264            }
265            Token::Ident(id) => {
266                let kind = id.chars().next().unwrap().to_ascii_uppercase();
267
268                match kind {
269                    'M' => {
270                        let mut params = Params::default();
271                        for i in (6..self.buffer.len()).step_by(3) {
272                            let k = self.buffer[i].try_ident()?.clone();
273                            assert!(matches!(self.buffer[i + 1], Token::Equals));
274                            let v = self.buffer[i + 2].try_ident()?.clone();
275                            params.insert(k, v);
276                        }
277                        Line::Component(Component::Mos(Mos {
278                            name: self.buffer[0].try_ident()?.clone(),
279                            d: self.buffer[1].try_ident()?.clone(),
280                            g: self.buffer[2].try_ident()?.clone(),
281                            s: self.buffer[3].try_ident()?.clone(),
282                            b: self.buffer[4].try_ident()?.clone(),
283                            model: self.buffer[5].try_ident()?.clone(),
284                            params,
285                        }))
286                    }
287                    'D' => {
288                        let mut params = Params::default();
289                        for i in (4..self.buffer.len()).step_by(3) {
290                            let k = self.buffer[i].try_ident()?.clone();
291                            assert!(matches!(self.buffer[i + 1], Token::Equals));
292                            let v = self.buffer[i + 2].try_ident()?.clone();
293                            params.insert(k, v);
294                        }
295                        Line::Component(Component::Diode(Diode {
296                            name: self.buffer[0].try_ident()?.clone(),
297                            pos: self.buffer[1].try_ident()?.clone(),
298                            neg: self.buffer[2].try_ident()?.clone(),
299                            model: self.buffer[3].try_ident()?.clone(),
300                            params,
301                        }))
302                    }
303                    'Q' => {
304                        // TODO: Does not support area factor or OFF.
305                        // TODO: this logic needs to change to support expressions
306                        // in parameter values.
307                        let pos = self.buffer.iter().position(|t| matches!(t, Token::Equals));
308                        let child_idx = pos.unwrap_or(self.buffer.len() + 1) - 2;
309                        let child = self.buffer[child_idx].try_ident()?.clone();
310                        let port_end_idx = child_idx;
311                        let ports = self.buffer[1..port_end_idx]
312                            .iter()
313                            .map(|x| x.try_ident().cloned())
314                            .collect::<Result<Vec<_>, _>>()?;
315
316                        let mut params = Params::default();
317                        for i in (child_idx + 1..self.buffer.len()).step_by(3) {
318                            let k = self.buffer[i].try_ident()?.clone();
319                            assert!(matches!(self.buffer[i + 1], Token::Equals));
320                            let v = self.buffer[i + 2].try_ident()?.clone();
321                            params.insert(k, v);
322                        }
323
324                        Line::Component(Component::Bjt(Bjt {
325                            name: self.buffer[0].try_ident()?.clone(),
326                            collector: ports[0].clone(),
327                            base: ports[1].clone(),
328                            emitter: ports[2].clone(),
329                            substrate: ports.get(3).cloned(),
330                            model: child,
331                            params,
332                        }))
333                    }
334                    'R' => {
335                        let mut params = Params::default();
336                        for i in (4..self.buffer.len()).step_by(3) {
337                            let k = self.buffer[i].try_ident()?.clone();
338                            assert!(matches!(self.buffer[i + 1], Token::Equals));
339                            let v = self.buffer[i + 2].try_ident()?.clone();
340                            params.insert(k, v);
341                        }
342                        let value = self.buffer[3].try_ident()?.clone();
343                        let value = if convert_str_to_numeric_lit(&value).is_some() {
344                            DeviceValue::Value(value)
345                        } else {
346                            DeviceValue::Model(value)
347                        };
348                        Line::Component(Component::Res(Res {
349                            name: self.buffer[0].try_ident()?.clone(),
350                            pos: self.buffer[1].try_ident()?.clone(),
351                            neg: self.buffer[2].try_ident()?.clone(),
352                            value,
353                            params,
354                        }))
355                    }
356                    'C' => Line::Component(Component::Cap(Cap {
357                        name: self.buffer[0].try_ident()?.clone(),
358                        pos: self.buffer[1].try_ident()?.clone(),
359                        neg: self.buffer[2].try_ident()?.clone(),
360                        value: self.buffer[3].try_ident()?.clone(),
361                    })),
362                    'X' => {
363                        // An X instance line looks like this:
364                        //
365                        // ```spice
366                        // Xname port0 port1 port2 child param1=value1 param2=value2
367                        // ```
368                        //
369                        // The index of "child" is the index of the first equals sign minus 2.
370                        // If there is no equal sign, it is buffer.len() - 1.
371                        //
372                        // The tokens after Xname and before `child_idx` are ports;
373                        // the tokens after `child_idx` should come in groups of 3
374                        // and represent parameter values.
375                        //
376                        // TODO: this logic needs to change to support expressions
377                        // in parameter values.
378                        let pos = self.buffer.iter().position(|t| matches!(t, Token::Equals));
379                        let child_idx = pos.unwrap_or(self.buffer.len() + 1) - 2;
380                        let child = self.buffer[child_idx].try_ident()?.clone();
381                        let port_end_idx = child_idx;
382                        let ports = self.buffer[1..port_end_idx]
383                            .iter()
384                            .map(|x| x.try_ident().cloned())
385                            .collect::<Result<Vec<_>, _>>()?;
386
387                        let ports = if self.dialect == Dialect::Cdl {
388                            ports
389                                .into_iter()
390                                .flat_map(|x| match x.as_str() {
391                                    "/" => None,
392                                    _ => Some(Substr(x.substr_from(x.trim_start_matches('/')))),
393                                })
394                                .collect::<Vec<_>>()
395                        } else {
396                            ports
397                        };
398
399                        let mut params = Params::default();
400                        for i in (child_idx + 1..self.buffer.len()).step_by(3) {
401                            let k = self.buffer[i].try_ident()?.clone();
402                            assert!(matches!(self.buffer[i + 1], Token::Equals));
403                            let v = self.buffer[i + 2].try_ident()?.clone();
404                            params.insert(k, v);
405                        }
406
407                        Line::Component(Component::Instance(Instance {
408                            name: self.buffer[0].try_ident()?.clone(),
409                            ports,
410                            child,
411                            params,
412                        }))
413                    }
414                    kind => return Err(ParserError::UnexpectedComponentType(kind)),
415                }
416            }
417            tok => return Err(ParserError::UnexpectedToken(tok.clone())),
418        };
419        self.buffer.clear();
420        Ok(Some(line))
421    }
422}
423
424/// Data associated with parsing a SPICE file.
425pub struct ParsedSpice {
426    /// The parsed contents of the spice file.
427    pub ast: Ast,
428
429    /// The file path at the root of the `include` tree.
430    pub root: Option<PathBuf>,
431
432    /// The name of the netlist.
433    ///
434    /// By default, this is the first line of the root file,
435    /// with whitespace trimmed.
436    pub name: ArcStr,
437}
438
439/// The abstract syntax tree (AST) of a parsed SPICE netlist.
440#[derive(Debug, Default, Clone, Eq, PartialEq)]
441pub struct Ast {
442    /// The list of elements in the SPICE netlist.
443    pub elems: Vec<Elem>,
444}
445
446/// A single logical line in a SPICE netlist.
447///
448/// A logical line may contain multiple lines in a file
449/// if all lines after the first are separated by the line continuation
450/// character (typically '+').
451#[derive(Debug, Clone, Eq, PartialEq)]
452pub enum Line {
453    /// A subcircuit declaration.
454    SubcktDecl {
455        /// The name of the subcircuit.
456        name: Substr,
457        /// A list of ports.
458        ///
459        /// Each port is the name of a node exposed by the subcircuit.
460        ports: Vec<Node>,
461    },
462    /// A component instantiation.
463    Component(Component),
464    /// The end of a subcircuit.
465    EndSubckt,
466    /// An include directive.
467    Include {
468        /// The path to include.
469        path: Substr,
470    },
471    /// Connect (i.e. deep short) two nodes.
472    Connect {
473        /// The first node.
474        node1: Substr,
475        /// The second node.
476        node2: Substr,
477    },
478}
479
480/// An element of a SPICE netlist AST.
481#[derive(Debug, Clone, Eq, PartialEq)]
482pub enum Elem {
483    /// A subcircuit declaration.
484    Subckt(Subckt),
485    /// A top-level component instance.
486    Component(Component),
487}
488
489/// The contents of a subcircuit.
490#[derive(Debug, Default, Clone, Eq, PartialEq)]
491pub struct Subckt {
492    /// The subcircuit name.
493    pub name: Substr,
494    /// The list of ports.
495    ///
496    /// Each port is a node exposed by this subcircuit.
497    pub ports: Vec<Node>,
498    /// List of components in the subcircuit.
499    pub components: Vec<Component>,
500
501    /// A set of deep shorted nodes.
502    ///
503    /// For example, a subcircuit containing `.CONNECT node1 node2`
504    /// and no other `.CONNECT` statements will yield
505    /// `connects = vec![("node1", "node2")]`.
506    pub connects: Vec<(Node, Node)>,
507}
508
509/// A SPICE netlist component.
510#[derive(Debug, Clone, Eq, PartialEq)]
511pub enum Component {
512    /// A MOSFET (declared with an 'M').
513    Mos(Mos),
514    /// A resistor (declared with an 'R').
515    Res(Res),
516    /// A diode (declared with a 'D').
517    Diode(Diode),
518    /// A bipolar junction transistor (BJT, declared with a 'Q').
519    Bjt(Bjt),
520    /// A capacitor (declared with a 'C').
521    Cap(Cap),
522    /// An instance of a subcircuit (declared with an 'X').
523    Instance(Instance),
524}
525
526/// A way of specifying the value of a primitive device.
527#[derive(Debug, Clone, Eq, PartialEq)]
528pub enum DeviceValue {
529    /// The value is a fixed nominal value, e.g. `10p`.
530    Value(Substr),
531    /// The value is computed by a model with the given name.
532    Model(Substr),
533}
534
535/// A resistor.
536#[derive(Debug, Clone, Eq, PartialEq)]
537pub struct Res {
538    /// The name of the resistor instance.
539    pub name: Substr,
540    /// The node connected to the positive terminal.
541    pub pos: Node,
542    /// The node connected to the negative terminal.
543    pub neg: Node,
544    /// The value or model of the resistor.
545    pub value: DeviceValue,
546    /// Parameters and their values.
547    pub params: Params,
548}
549
550/// A diode.
551#[derive(Debug, Clone, Eq, PartialEq)]
552pub struct Diode {
553    /// The name of the diode instance.
554    pub name: Substr,
555    /// The node connected to the positive terminal.
556    pub pos: Node,
557    /// The node connected to the negative terminal.
558    pub neg: Node,
559    /// The name of the associated diode model.
560    pub model: Substr,
561    /// Parameters and their values.
562    pub params: Params,
563}
564
565/// A bipolar junction transistor (BJT).
566// TODO: Area factor and OFF.
567#[derive(Debug, Clone, Eq, PartialEq)]
568pub struct Bjt {
569    /// The name of the BJT instance.
570    pub name: Substr,
571    /// The node connected to the collector.
572    pub collector: Node,
573    /// The node connected to the base.
574    pub base: Node,
575    /// The node connected to the emitter.
576    pub emitter: Node,
577    /// The node connected to the substrate.
578    pub substrate: Option<Node>,
579    /// The name of the associated BJT model.
580    pub model: Substr,
581    /// Parameters and their values.
582    pub params: Params,
583}
584
585/// A capacitor.
586#[derive(Debug, Clone, Eq, PartialEq)]
587pub struct Cap {
588    /// The name of the capacitor instance.
589    pub name: Substr,
590    /// The node connected to the positive terminal.
591    pub pos: Node,
592    /// The node connected to the negative terminal.
593    pub neg: Node,
594    /// The value of the resistor.
595    pub value: Substr,
596}
597
598/// A subcircuit instance.
599#[derive(Debug, Clone, Eq, PartialEq)]
600pub struct Instance {
601    /// The name of the instance.
602    pub name: Substr,
603    /// The list of port connections.
604    pub ports: Vec<Node>,
605    /// The name of the child cell.
606    pub child: Substr,
607    /// Instance parameters.
608    pub params: Params,
609}
610
611/// A MOSFET.
612#[derive(Debug, Clone, Eq, PartialEq)]
613pub struct Mos {
614    /// The name of the MOSFET instance.
615    pub name: Substr,
616    /// The drain.
617    pub d: Node,
618    /// The gate.
619    pub g: Node,
620    /// The source.
621    pub s: Node,
622    /// The body/substrate.
623    pub b: Node,
624    /// The name of the associated MOSFET model.
625    pub model: Substr,
626    /// Parameters and their values.
627    pub params: Params,
628}
629
630/// Parameter values.
631#[derive(Debug, Default, Clone, Eq, PartialEq)]
632pub struct Params {
633    /// A map of key-value pairs.
634    values: HashMap<Substr, Substr>,
635}
636
637#[inline]
638fn is_newline(c: char) -> bool {
639    c == '\n' || c == '\r'
640}
641
642#[inline]
643fn is_space(c: char) -> bool {
644    c == ' ' || c == '\t'
645}
646
647#[inline]
648fn is_whitespace_equivalent(c: char, ignore: &HashSet<char>) -> bool {
649    c.is_whitespace() || ignore.contains(&c)
650}
651
652#[inline]
653fn is_space_or_newline(c: char) -> bool {
654    is_space(c) || is_newline(c)
655}
656
657#[inline]
658fn is_special(c: char) -> bool {
659    is_space_or_newline(c) || c == '='
660}
661
662struct Tokenizer {
663    data: Substr,
664    rem: Substr,
665    state: TokState,
666    comments: HashSet<char>,
667    /// Characters to treat as equivalent to whitespace.
668    ignore_chars: HashSet<char>,
669    line_continuation: char,
670    /// The string used to prefix metadata SPICE directives.
671    ///
672    /// In CDL format, this is "*.".
673    meta_directive_prefix: Option<String>,
674}
675
676/// A SPICE token.
677#[derive(Debug, Clone, Eq, PartialEq)]
678pub enum Token {
679    /// A SPICE directive that starts with a leading dot.
680    ///
681    /// Examples: ".subckt", ".ends", ".include".
682    ///
683    /// The tokenizer returns tokens with case matching the input file.
684    /// No conversion to upper/lowercase is made.
685    Directive(Substr),
686    /// A SPICE identifier.
687    Ident(Substr),
688    /// A line end indicator.
689    LineEnd,
690    /// An equal sign token ('=').
691    Equals,
692    /// A metadata directive.
693    ///
694    /// Examples: "*.CONNECT", "*.PININFO".
695    MetaDirective(Substr),
696}
697
698#[derive(Copy, Clone, Default, Eq, PartialEq, Hash, Debug)]
699enum TokState {
700    /// Initial state.
701    #[default]
702    Init,
703    /// Parsing a line.
704    Line,
705}
706
707/// An error arising from parsing a SPICE netlist.
708#[derive(Debug, Error)]
709pub enum ParserError {
710    /// A tokenizer error.
711    #[error("tokenizer error: {0}")]
712    Tokenizer(#[from] TokenizerError),
713    /// Found a SPICE line in the wrong context.
714    ///
715    /// For example, a ".ends" line with no matching ".subckt" line.
716    #[error("unexpected line: {0:?}")]
717    UnexpectedLine(Box<Line>),
718    /// An unsupported or unexpected SPICE directive.
719    #[error("unexpected SPICE directive: {0}")]
720    UnexpectedDirective(Substr),
721    /// An unsupported or unexpected SPICE component type.
722    #[error("unexpected component type: {0}")]
723    UnexpectedComponentType(char),
724    /// An unsupported or unexpected token.
725    #[error("unexpected token: {0:?}")]
726    UnexpectedToken(Token),
727    /// A relative path was used in an unsupported position.
728    ///
729    /// For example, relative paths are forbidden when parsing inline spice.
730    #[error("unexpected relative path: {0:?}")]
731    UnexpectedRelativePath(Substr),
732    /// An invalid line.
733    #[error("invalid line `{line:?}`: {reason}")]
734    InvalidLine {
735        /// The tokens in the offending line.
736        line: Vec<Token>,
737        /// The reason the line is invalid.
738        reason: String,
739    },
740    /// Error trying to read the given file.
741    #[error("failed to read file at path `{path:?}`: {err:?}")]
742    FailedToRead {
743        /// The path we attempted to read.
744        path: PathBuf,
745        /// The underlying error.
746        #[source]
747        err: std::io::Error,
748    },
749}
750
751/// A tokenizer error.
752#[derive(Debug, Error)]
753#[allow(dead_code)]
754pub struct TokenizerError {
755    /// The state of the tokenizer at the time this error occurred.
756    state: TokState,
757    /// The byte offset in the file being tokenized.
758    ofs: usize,
759    /// The full contents of the file being parsed.
760    data: Substr,
761    /// The contents of `data` that have not yet been processed.
762    rem: Substr,
763    message: ArcStr,
764    token: Substr,
765}
766
767impl Tokenizer {
768    fn new(dialect: Dialect, data: impl Into<arcstr::Substr>) -> Self {
769        let data = data.into();
770        let rem = data.clone();
771        let meta_directive_prefix = match dialect {
772            Dialect::Spice => None,
773            Dialect::Cdl => Some("*.".to_string()),
774        };
775        let ignore_chars = match dialect {
776            Dialect::Spice => HashSet::new(),
777            Dialect::Cdl => HashSet::new(),
778        };
779        Self {
780            data: Substr(data),
781            rem: Substr(rem),
782            state: TokState::Init,
783            comments: HashSet::from(['*', '$']),
784            ignore_chars,
785            line_continuation: '+',
786            meta_directive_prefix,
787        }
788    }
789
790    fn next_is_meta_directive(&self) -> bool {
791        self.meta_directive_prefix
792            .as_ref()
793            .map(|s| self.rem.starts_with(s))
794            .unwrap_or_default()
795    }
796
797    fn try_meta_directive(&mut self) -> Option<Substr> {
798        if self.next_is_meta_directive() {
799            let s = self.meta_directive_prefix.as_ref().unwrap();
800            self.rem = Substr(self.rem.substr(s.len()..));
801            Some(self.take_ident())
802        } else {
803            None
804        }
805    }
806
807    pub fn get(&mut self) -> Result<Option<Token>, TokenizerError> {
808        loop {
809            self.take_ws();
810            if self.rem.is_empty() {
811                // handle EOF
812                if self.state == TokState::Line {
813                    // At EOF, but have not yet returned a final LineEnd token.
814                    self.state = TokState::Init;
815                    return Ok(Some(Token::LineEnd));
816                } else {
817                    // At EOF, no more tokens.
818                    return Ok(None);
819                }
820            }
821
822            let c = self.peek().unwrap();
823            if c == '=' {
824                self.take1();
825                return Ok(Some(Token::Equals));
826            }
827            match self.state {
828                TokState::Init => {
829                    if self.comments.contains(&c) && !self.next_is_meta_directive() {
830                        self.take_until_newline();
831                    } else if is_whitespace_equivalent(c, &self.ignore_chars) {
832                        self.take1();
833                    } else if c == self.line_continuation {
834                        self.err("unexpected line continuation", c)?;
835                    } else {
836                        self.state = TokState::Line;
837                    }
838                }
839                TokState::Line => {
840                    if let Some(md) = self.try_meta_directive() {
841                        return Ok(Some(Token::MetaDirective(md)));
842                    } else if is_newline(c) {
843                        self.take1();
844                        self.take_ws();
845                        if self.peek().unwrap_or(self.line_continuation) != self.line_continuation {
846                            self.state = TokState::Init;
847                            return Ok(Some(Token::LineEnd));
848                        }
849                    } else if c == self.line_continuation || self.ignore_chars.contains(&c) {
850                        self.take1();
851                    } else if self.comments.contains(&c) {
852                        self.take_until_newline();
853                    } else if c == '.' {
854                        let word = self.take_ident();
855                        return Ok(Some(Token::Directive(word)));
856                    } else {
857                        let word = self.take_ident();
858                        return Ok(Some(Token::Ident(word)));
859                    }
860                }
861            }
862        }
863    }
864
865    fn err(
866        &self,
867        message: impl Into<ArcStr>,
868        token: impl Into<Substr>,
869    ) -> Result<(), TokenizerError> {
870        Err(TokenizerError {
871            state: self.state,
872            ofs: self.rem.range().start,
873            data: self.data.clone(),
874            rem: self.rem.clone(),
875            message: message.into(),
876            token: token.into(),
877        })
878    }
879
880    fn take1(&mut self) -> Option<char> {
881        let c = self.rem.chars().next()?;
882        self.rem = Substr(self.rem.substr(1..));
883        Some(c)
884    }
885
886    fn take_until_newline(&mut self) -> Substr {
887        let (rest, comment) = take_till::<_, _, ()>(is_newline)(self.rem.clone()).unwrap();
888        self.rem = rest;
889        comment
890    }
891
892    fn take_ident(&mut self) -> Substr {
893        let (rest, value) = take_till::<_, _, ()>(is_special)(self.rem.clone()).unwrap();
894        self.rem = rest;
895        value
896    }
897
898    fn take_ws(&mut self) {
899        let (rest, _) = take_while::<_, _, ()>(is_space)(self.rem.clone()).unwrap();
900        self.rem = rest;
901    }
902
903    fn peek(&self) -> Option<char> {
904        self.rem.chars().next()
905    }
906}
907
908struct Tokens {
909    tok: Tokenizer,
910}
911
912impl Iterator for Tokens {
913    type Item = Result<Token, TokenizerError>;
914    fn next(&mut self) -> Option<Self::Item> {
915        self.tok.get().transpose()
916    }
917}
918
919impl IntoIterator for Tokenizer {
920    type Item = Result<Token, TokenizerError>;
921    type IntoIter = Tokens;
922    fn into_iter(self) -> Self::IntoIter {
923        Tokens { tok: self }
924    }
925}
926
927impl Deref for Substr {
928    type Target = arcstr::Substr;
929    fn deref(&self) -> &Self::Target {
930        &self.0
931    }
932}
933
934impl DerefMut for Substr {
935    fn deref_mut(&mut self) -> &mut Self::Target {
936        &mut self.0
937    }
938}
939
940impl Input for Substr {
941    type Item = char;
942    type Iter = SubstrChars;
943    type IterIndices = SubstrCharIndices;
944
945    fn input_len(&self) -> usize {
946        <&str as Input>::input_len(&&***self)
947    }
948
949    fn take(&self, index: usize) -> Self {
950        Substr(self.0.substr_from(<&str as Input>::take(&&***self, index)))
951    }
952
953    fn take_from(&self, index: usize) -> Self {
954        Substr(
955            self.0
956                .substr_from(<&str as Input>::take_from(&&***self, index)),
957        )
958    }
959
960    fn take_split(&self, index: usize) -> (Self, Self) {
961        let (a, b) = <&str as Input>::take_split(&&***self, index);
962        (Substr(self.0.substr_from(a)), Substr(self.0.substr_from(b)))
963    }
964
965    fn position<P>(&self, predicate: P) -> Option<usize>
966    where
967        P: Fn(Self::Item) -> bool,
968    {
969        <&str as Input>::position(&&***self, predicate)
970    }
971
972    fn iter_elements(&self) -> Self::Iter {
973        SubstrChars {
974            substr: self.clone(),
975        }
976    }
977
978    fn iter_indices(&self) -> Self::IterIndices {
979        SubstrCharIndices {
980            substr: self.clone(),
981        }
982    }
983
984    fn slice_index(&self, count: usize) -> Result<usize, nom::Needed> {
985        <&str as Input>::slice_index(&&***self, count)
986    }
987}
988
989/// An iterator over the chars of a [`Substr`].
990pub struct SubstrChars {
991    substr: Substr,
992}
993
994impl Iterator for SubstrChars {
995    type Item = char;
996    fn next(&mut self) -> Option<Self::Item> {
997        let mut chars = self.substr.chars();
998        let c = chars.next();
999        self.substr = Substr(self.substr.0.substr_from(chars.as_str()));
1000        c
1001    }
1002}
1003
1004impl FusedIterator for SubstrChars {}
1005
1006/// An iterator over the chars of a [`Substr`], and their positions.
1007pub struct SubstrCharIndices {
1008    substr: Substr,
1009}
1010
1011impl Iterator for SubstrCharIndices {
1012    type Item = (usize, char);
1013    fn next(&mut self) -> Option<Self::Item> {
1014        let mut chars = self.substr.char_indices();
1015        let c = chars.next();
1016        self.substr = Substr(self.substr.0.substr_from(chars.as_str()));
1017        c
1018    }
1019}
1020
1021impl FusedIterator for SubstrCharIndices {}
1022
1023impl Display for Substr {
1024    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1025        write!(f, "{}", self.0)
1026    }
1027}
1028
1029impl From<Substr> for arcstr::Substr {
1030    fn from(value: Substr) -> Self {
1031        value.0
1032    }
1033}
1034
1035impl From<&str> for Substr {
1036    fn from(value: &str) -> Self {
1037        Self(arcstr::Substr::from(value))
1038    }
1039}
1040
1041impl From<arcstr::Substr> for Substr {
1042    fn from(value: arcstr::Substr) -> Self {
1043        Self(value)
1044    }
1045}
1046
1047impl From<ArcStr> for Substr {
1048    fn from(value: ArcStr) -> Self {
1049        Self(arcstr::Substr::full(value))
1050    }
1051}
1052
1053impl From<char> for Substr {
1054    fn from(value: char) -> Self {
1055        Self(arcstr::Substr::from(value.to_string()))
1056    }
1057}
1058
1059impl Token {
1060    fn try_ident(&self) -> Result<&Substr, ParserError> {
1061        match self {
1062            Self::Ident(x) => Ok(x),
1063            _ => Err(ParserError::UnexpectedToken(self.clone())),
1064        }
1065    }
1066}
1067
1068impl Params {
1069    /// Create a new, empty parameter set.
1070    #[inline]
1071    pub fn new() -> Self {
1072        Self::default()
1073    }
1074
1075    /// Insert a key-value pair into the parameter set.
1076    pub fn insert(&mut self, k: impl Into<Substr>, v: impl Into<Substr>) {
1077        self.values.insert(k.into(), v.into());
1078    }
1079
1080    /// Get the value corresponding to the given key.
1081    pub fn get(&self, k: &str) -> Option<&Substr> {
1082        self.values.get(k)
1083    }
1084
1085    /// An iterator over all key-value pairs.
1086    pub fn iter(&self) -> impl Iterator<Item = (&Substr, &Substr)> {
1087        self.values.iter()
1088    }
1089}
1090
1091impl Borrow<str> for Substr {
1092    fn borrow(&self) -> &str {
1093        &self.0
1094    }
1095}
1096
1097impl Display for TokenizerError {
1098    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1099        write!(
1100            f,
1101            "{} (token {} at offset {})",
1102            self.message, self.token, self.ofs
1103        )
1104    }
1105}
1106
1107impl ParsedSpice {
1108    /// Convert this SPICE netlist to a SCIR library.
1109    pub fn to_scir(&self) -> conv::ConvResult<scir::Library<Spice>> {
1110        let conv = ScirConverter::new(&self.ast);
1111        conv.convert()
1112    }
1113}