1use nom::{bytes::complete::tag_no_case, error::{context, VerboseError, VerboseErrorKind}, multi::many0, Err};
2
3use crate::model::{Instance, Subckt};
4use super::{comment, component, hws, identifier, node, NomResult, ToFailure};
5
6pub fn subckt<'a>(input: &'a str) -> NomResult<'a, Subckt> {
8 context("subckt", |mut input: &'a str| {
9 let (i, _) = context("keyword", hws(tag_no_case(".SUBCKT")))(input)?;
11 let (i, (name, ports)) = context("declaration", hws(subckt_decl))(i).to_failure()?;
12 input = i;
13
14 let mut components = vec![];
15 let mut instances = vec![];
16
17 loop {
19 input = input.trim_start();
20 if let Ok((rest, _)) = comment(input) {
21 input = rest;
22 continue;
23 }
24
25 if input.to_ascii_lowercase().starts_with(".ends") {
27 if let Some(pos) = input.find('\n') {
29 input = &input[pos + 1..];
30 } else {
31 input = "";
32 }
33 break;
34 }
35
36 match hws(component)(input) {
38 Ok((rest, c)) => {
39 components.push(c);
40 input = rest;
41 continue;
42 }
43 Err(Err::Error(_)) => {
44 match hws(instance)(input) {
46 Ok((rest, inst)) => {
47 instances.push(inst);
48 input = rest;
49 continue;
50 }
51 Err(Err::Error(_)) => {
52 return Err(Err::Failure(VerboseError {
53 errors: [(input, VerboseErrorKind::Context("unknown line in subckt"))].into(),
54 }));
55 }
56 Err(e @ Err::Failure(_)) | Err(e @ Err::Incomplete(_)) => return Err(e),
57 }
58 }
59 Err(e @ Err::Failure(_)) | Err(e @ Err::Incomplete(_)) => return Err(e),
60 }
61 }
62
63 Ok((
64 input,
65 Subckt {
66 name,
67 ports,
68 components,
69 instances,
70 },
71 ))
72 })(input)
73}
74
75fn subckt_decl(input: &str) -> NomResult<(String, Vec<String>)> {
76 context("subckt_decl", |input| {
77 let (input, name) = context("name", hws(identifier))(input)?;
78 let (input, ports) = context("ports", many0(hws(node)))(input)?;
79 Ok((
80 input,
81 (
82 name.to_string(),
83 ports.iter().map(|s| s.to_string()).collect(),
84 ),
85 ))
86 })(input)
87}
88
89pub fn instance(input: &str) -> NomResult<Instance> {
91 context("instance", |input| {
92 let (input, name) = context("name", hws(identifier))(input)?;
93
94 if !name.starts_with('X') && !name.starts_with('x') {
95 return Err(Err::Error(VerboseError {
96 errors: [(input, VerboseErrorKind::Context("should begin with X"))].into(),
97 }));
98 }
99
100 let (input, args) = context("args", many0(hws(node)))(input)?;
101
102 if args.is_empty() {
103 return Err(Err::Failure(VerboseError {
104 errors: [(input, VerboseErrorKind::Context("missing subckt name"))].into(),
105 }));
106 }
107
108 let subckt_name = args.last().unwrap().to_string();
109 let pins = args[..args.len() - 1]
110 .iter()
111 .map(|s| s.to_string())
112 .collect();
113
114 Ok((
115 input,
116 Instance {
117 name: name.to_string(),
118 pins,
119 subckt_name,
120 },
121 ))
122 })(input)
123}
124
125#[allow(unused)]
126#[cfg(test)]
127mod test {
128 use super::*;
129 use nom::{Err, error::convert_error};
130
131 #[test]
132 fn test_parse_subckt_and_instance() {
133 let input =
134 r#".SUBCKT inverter in out vdd gnd
135 M1 out in vdd vdd pmos L=1u W=2u
136 M2 out in gnd gnd nmos L=1u W=1u
137 .ENDS
138 Xinv a b vdd gnd inverter
139 "#;
140
141 let (rest, subckt) = subckt(input).unwrap();
142
143 assert_eq!(subckt.name, "inverter");
144 assert_eq!(subckt.ports, ["in", "out", "vdd", "gnd"]);
145 assert_eq!(subckt.components.len(), 2);
146
147 let (_, inst) = instance(rest.trim()).unwrap();
148 assert_eq!(inst.name, "Xinv");
149 assert_eq!(inst.pins, ["a", "b", "vdd", "gnd"]);
150 assert_eq!(inst.subckt_name, "inverter");
151 }
152
153 #[test]
154 fn test_instance_bad_prefix_error() {
155 let input = "Y1 in out myblk";
156 let res = instance(input);
157 assert!(matches!(res, Err(Err::Error(_))));
158 }
159
160 #[test]
161 fn test_subckt_unknown_line_failure() {
162 let input = r#".SUBCKT foo in out
163 ??? bad line
164 .ENDS
165 "#;
166
167 let res = subckt(input);
168 assert!(matches!(res, Err(Err::Failure(_))));
169 }
170}