1pub 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
25pub type Node = Substr;
27
28#[derive(Clone, Default, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
30#[repr(transparent)]
31pub struct Substr(arcstr::Substr);
32
33#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)]
35pub enum Dialect {
36 #[default]
40 Spice,
41 Cdl,
43}
44
45#[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#[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 pub fn new(dialect: Dialect) -> Self {
96 Self {
97 dialect,
98 ..Self::default()
99 }
100 }
101 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 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 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 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 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 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 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 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
424pub struct ParsedSpice {
426 pub ast: Ast,
428
429 pub root: Option<PathBuf>,
431
432 pub name: ArcStr,
437}
438
439#[derive(Debug, Default, Clone, Eq, PartialEq)]
441pub struct Ast {
442 pub elems: Vec<Elem>,
444}
445
446#[derive(Debug, Clone, Eq, PartialEq)]
452pub enum Line {
453 SubcktDecl {
455 name: Substr,
457 ports: Vec<Node>,
461 },
462 Component(Component),
464 EndSubckt,
466 Include {
468 path: Substr,
470 },
471 Connect {
473 node1: Substr,
475 node2: Substr,
477 },
478}
479
480#[derive(Debug, Clone, Eq, PartialEq)]
482pub enum Elem {
483 Subckt(Subckt),
485 Component(Component),
487}
488
489#[derive(Debug, Default, Clone, Eq, PartialEq)]
491pub struct Subckt {
492 pub name: Substr,
494 pub ports: Vec<Node>,
498 pub components: Vec<Component>,
500
501 pub connects: Vec<(Node, Node)>,
507}
508
509#[derive(Debug, Clone, Eq, PartialEq)]
511pub enum Component {
512 Mos(Mos),
514 Res(Res),
516 Diode(Diode),
518 Bjt(Bjt),
520 Cap(Cap),
522 Instance(Instance),
524}
525
526#[derive(Debug, Clone, Eq, PartialEq)]
528pub enum DeviceValue {
529 Value(Substr),
531 Model(Substr),
533}
534
535#[derive(Debug, Clone, Eq, PartialEq)]
537pub struct Res {
538 pub name: Substr,
540 pub pos: Node,
542 pub neg: Node,
544 pub value: DeviceValue,
546 pub params: Params,
548}
549
550#[derive(Debug, Clone, Eq, PartialEq)]
552pub struct Diode {
553 pub name: Substr,
555 pub pos: Node,
557 pub neg: Node,
559 pub model: Substr,
561 pub params: Params,
563}
564
565#[derive(Debug, Clone, Eq, PartialEq)]
568pub struct Bjt {
569 pub name: Substr,
571 pub collector: Node,
573 pub base: Node,
575 pub emitter: Node,
577 pub substrate: Option<Node>,
579 pub model: Substr,
581 pub params: Params,
583}
584
585#[derive(Debug, Clone, Eq, PartialEq)]
587pub struct Cap {
588 pub name: Substr,
590 pub pos: Node,
592 pub neg: Node,
594 pub value: Substr,
596}
597
598#[derive(Debug, Clone, Eq, PartialEq)]
600pub struct Instance {
601 pub name: Substr,
603 pub ports: Vec<Node>,
605 pub child: Substr,
607 pub params: Params,
609}
610
611#[derive(Debug, Clone, Eq, PartialEq)]
613pub struct Mos {
614 pub name: Substr,
616 pub d: Node,
618 pub g: Node,
620 pub s: Node,
622 pub b: Node,
624 pub model: Substr,
626 pub params: Params,
628}
629
630#[derive(Debug, Default, Clone, Eq, PartialEq)]
632pub struct Params {
633 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 ignore_chars: HashSet<char>,
669 line_continuation: char,
670 meta_directive_prefix: Option<String>,
674}
675
676#[derive(Debug, Clone, Eq, PartialEq)]
678pub enum Token {
679 Directive(Substr),
686 Ident(Substr),
688 LineEnd,
690 Equals,
692 MetaDirective(Substr),
696}
697
698#[derive(Copy, Clone, Default, Eq, PartialEq, Hash, Debug)]
699enum TokState {
700 #[default]
702 Init,
703 Line,
705}
706
707#[derive(Debug, Error)]
709pub enum ParserError {
710 #[error("tokenizer error: {0}")]
712 Tokenizer(#[from] TokenizerError),
713 #[error("unexpected line: {0:?}")]
717 UnexpectedLine(Box<Line>),
718 #[error("unexpected SPICE directive: {0}")]
720 UnexpectedDirective(Substr),
721 #[error("unexpected component type: {0}")]
723 UnexpectedComponentType(char),
724 #[error("unexpected token: {0:?}")]
726 UnexpectedToken(Token),
727 #[error("unexpected relative path: {0:?}")]
731 UnexpectedRelativePath(Substr),
732 #[error("invalid line `{line:?}`: {reason}")]
734 InvalidLine {
735 line: Vec<Token>,
737 reason: String,
739 },
740 #[error("failed to read file at path `{path:?}`: {err:?}")]
742 FailedToRead {
743 path: PathBuf,
745 #[source]
747 err: std::io::Error,
748 },
749}
750
751#[derive(Debug, Error)]
753#[allow(dead_code)]
754pub struct TokenizerError {
755 state: TokState,
757 ofs: usize,
759 data: Substr,
761 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 if self.state == TokState::Line {
813 self.state = TokState::Init;
815 return Ok(Some(Token::LineEnd));
816 } else {
817 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
989pub 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
1006pub 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 #[inline]
1071 pub fn new() -> Self {
1072 Self::default()
1073 }
1074
1075 pub fn insert(&mut self, k: impl Into<Substr>, v: impl Into<Substr>) {
1077 self.values.insert(k.into(), v.into());
1078 }
1079
1080 pub fn get(&self, k: &str) -> Option<&Substr> {
1082 self.values.get(k)
1083 }
1084
1085 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 pub fn to_scir(&self) -> conv::ConvResult<scir::Library<Spice>> {
1110 let conv = ScirConverter::new(&self.ast);
1111 conv.convert()
1112 }
1113}