1use std::collections::HashMap;
2use nom::{bytes::complete::tag_no_case, error::{context, VerboseError, VerboseErrorKind}, Err};
3use nom::{character::complete::char, multi::many0};
4use crate::model::{Capacitor, Component, Diode, Inductor, Model, ModelKind, MosFET, MosFETBuilder, Resistor, BJT};
5use super::{capacitance_number, hws, identifier, inductance_number, node, number, resistance_number, NomResult, ToFailure};
6use reda_unit::Number;
7
8use nom::branch::alt;
9use nom::combinator::map;
10
11pub fn component(input: &str) -> NomResult<Component> {
12 alt((
13 map(resistor, Component::R),
14 map(capacitor, Component::C),
15 map(inductor, Component::L),
16 map(diode, Component::D),
17 map(bjt, Component::Q),
18 map(mos_fet, Component::M),
19 ))(input)
20}
21
22pub fn resistor(input: &str) -> NomResult<Resistor> {
24 context("resistor", |input| {
25 let (input, name) = context("name", hws(identifier))(input)?;
26 if !name.starts_with('R') && !name.starts_with('r') {
27 return Err(Err::Error(VerboseError {
28 errors: [(input, VerboseErrorKind::Context("should begin with R"))].into(),
29 }));
30 }
31
32 let (input, node_pos) = hws(node)(input).to_failure()?;
33 let (input, node_neg) = hws(node)(input).to_failure()?;
34 let (input, resistance) = hws(resistance_number)(input).to_failure()?;
35
36 let r = Resistor {
37 name: name[1..].to_string(),
38 node_pos: node_pos.to_string(),
39 node_neg: node_neg.to_string(),
40 resistance,
41 };
42
43 Ok((input, r))
44 })(input)
45}
46
47pub fn capacitor(input: &str) -> NomResult<Capacitor> {
49 context("capacitor", |input| {
50 let (input, name) = context("name", hws(identifier))(input)?;
51 if !name.starts_with('C') && !name.starts_with('c') {
52 return Err(Err::Error(VerboseError {
53 errors: [(input, VerboseErrorKind::Context("should begin with C"))].into(),
54 }));
55 }
56
57 let (input, node_pos) = hws(node)(input).to_failure()?;
58 let (input, node_neg) = hws(node)(input).to_failure()?;
59 let (input, value) = hws(capacitance_number)(input).to_failure()?;
60
61 Ok((
62 input,
63 Capacitor {
64 name: name[1..].to_string(),
65 node_pos: node_pos.to_string(),
66 node_neg: node_neg.to_string(),
67 capacitance: value,
68 },
69 ))
70 })(input)
71}
72
73pub fn inductor(input: &str) -> NomResult<Inductor> {
75 context("inductor", |input| {
76 let (input, name) = context("name", hws(identifier))(input)?;
77 if !name.starts_with('L') && !name.starts_with('l') {
78 return Err(Err::Error(VerboseError {
79 errors: [(input, VerboseErrorKind::Context("should begin with L"))].into(),
80 }));
81 }
82
83 let (input, node_pos) = hws(node)(input).to_failure()?;
84 let (input, node_neg) = hws(node)(input).to_failure()?;
85 let (input, value) = hws(inductance_number)(input).to_failure()?;
86
87 Ok((
88 input,
89 Inductor {
90 name: name[1..].to_string(),
91 node_pos: node_pos.to_string(),
92 node_neg: node_neg.to_string(),
93 inductance: value,
94 },
95 ))
96 })(input)
97}
98
99pub fn diode(input: &str) -> NomResult<Diode> {
101 context("diode", |input| {
102 let (input, name) = context("name", hws(identifier))(input)?;
103 if !name.starts_with('D') && !name.starts_with('d') {
104 return Err(Err::Error(VerboseError {
105 errors: [(input, VerboseErrorKind::Context("should begin with D"))].into(),
106 }));
107 }
108
109 let (input, node_pos) = hws(node)(input).to_failure()?;
110 let (input, node_neg) = hws(node)(input).to_failure()?;
111 let (input, model_name) = hws(identifier)(input).to_failure()?;
112
113 Ok((
114 input,
115 Diode {
116 name: name[1..].to_string(),
117 node_pos: node_pos.to_string(),
118 node_neg: node_neg.to_string(),
119 model_name: model_name.to_string(),
120 },
121 ))
122 })(input)
123}
124
125pub fn bjt(input: &str) -> NomResult<BJT> {
127 context("bjt", |input| {
128 let (input, name) = context("name", hws(identifier))(input)?;
129 if !name.starts_with('Q') && !name.starts_with('q') {
130 return Err(Err::Error(VerboseError {
131 errors: [(input, VerboseErrorKind::Context("should begin with Q"))].into(),
132 }));
133 }
134
135 let (input, collector) = hws(node)(input).to_failure()?;
136 let (input, base) = hws(node)(input).to_failure()?;
137 let (input, emitter) = hws(node)(input).to_failure()?;
138 let (input, model_name) = hws(identifier)(input).to_failure()?;
139
140 Ok((
141 input,
142 BJT {
143 name: name[1..].to_string(),
144 collector: collector.to_string(),
145 base: base.to_string(),
146 emitter: emitter.to_string(),
147 model_name: model_name.to_string(),
148 },
149 ))
150 })(input)
151}
152
153pub fn mos_fet(input: &str) -> NomResult<MosFET> {
155 context("mosfet", |input| {
156 let (input, name) = context("name", hws(identifier))(input)?;
157 if !name.starts_with('M') && !name.starts_with('m') {
158 return Err(Err::Error(VerboseError {
159 errors: [(input, VerboseErrorKind::Context("should begin with M"))].into(),
160 }));
161 }
162
163 let mut builder = MosFETBuilder::default();
164
165 let (input, drain) = hws(node)(input).to_failure()?;
166 let (input, gate) = hws(node)(input).to_failure()?;
167 let (input, source) = hws(node)(input).to_failure()?;
168 let (input, bulk) = hws(node)(input).to_failure()?;
169 let (input, model_name) = hws(identifier)(input).to_failure()?;
170
171 builder
172 .drain(drain)
173 .gate(gate)
174 .source(source)
175 .bulk(bulk)
176 .model_name(model_name)
177 .name(&name[1..]);
178
179 let mut parameters = HashMap::new();
180 let (input, raw_parameters) = many0(hws(parameter_pair))(input)?;
181 for (k, v) in raw_parameters {
182 match k.to_ascii_lowercase().as_str() {
183 "l" => { builder.length(v); }
184 "w" => { builder.width(v); }
185 _ => { parameters.insert(k, v); }
186 }
187 }
188 builder.parameters(parameters);
189
190 match builder.build() {
191 Ok(mos) => Ok((input, mos)),
192 Err(_) => Err(Err::Failure(VerboseError {
193 errors: [(input, VerboseErrorKind::Context("no w/l given"))].into(),
194 }))
195 }
196 })(input)
197}
198
199pub fn model(input: &str) -> NomResult<Model> {
201 let (input, _) = context("keyword", hws(tag_no_case(".model")))(input)?;
202 let (input, name) = hws(identifier)(input).to_failure()?;
203 let (input, kind) = hws(model_kind)(input).to_failure()?;
204
205 let (input, _) = hws(tag_no_case("("))(input).to_failure()?;
206 let (input, parameters) = many0(parameter_pair)(input).to_failure()?;
207 let (input, _) = hws(tag_no_case(")"))(input).to_failure()?;
208
209 Ok((input, Model {
210 name: name.to_string(),
211 kind,
212 parameters: parameters.into_iter().collect(),
213 }))
214}
215
216fn model_kind(input: &str) -> NomResult<ModelKind> {
217 map(
218 hws(alt((
219 tag_no_case("NPN"),
220 tag_no_case("D"),
221 tag_no_case("NMOS"),
222 tag_no_case("PMOS"),
223 ))),
224 |s: &str| match &s.to_ascii_uppercase()[..] {
225 "NPN" => ModelKind::NPN,
226 "D" => ModelKind::Diode,
227 "NMOS" => ModelKind::NMos,
228 "PMOS" => ModelKind::PMos,
229 _ => unreachable!(),
230 },
231 )(input)
232}
233
234fn parameter_pair(input: &str) -> NomResult<(String, Number)> {
236 let (input, key) = hws(identifier)(input)?;
237 let (input, _) = hws(char('='))(input)?;
238 let (input, val) = hws(number)(input)?;
239 Ok((input, (key.to_string(), val)))
240}
241
242#[allow(unused)]
243#[cfg(test)]
244mod test {
245 use reda_unit::{num, u};
246 use super::*;
247
248 #[test]
249 fn test_basic() {
250 let (left, r) = resistor("Rhhh 1 \n+n2 1.5k \n777").unwrap();
251 assert_eq!(left, "\n777");
252 assert_eq!(r.name, "hhh");
253 assert_eq!(r.node_pos, "1");
254 assert_eq!(r.node_neg, "n2");
255 assert_eq!(r.resistance, u!(1.5 kΩ));
256
257 let (left, c) = capacitor("C1 nplus nminus 10u extra").unwrap();
258 assert_eq!(left, "extra");
259 assert_eq!(c.name, "1");
260 assert_eq!(c.node_pos, "nplus");
261 assert_eq!(c.node_neg, "nminus");
262 assert_eq!(c.capacitance, u!(10.0 uF));
263
264 let (left, l) = inductor("Lfoo a b 5m more").unwrap();
265 assert_eq!(left, "more");
266 assert_eq!(l.name, "foo");
267 assert_eq!(l.node_pos, "a");
268 assert_eq!(l.node_neg, "b");
269 assert_eq!(l.inductance, u!(5.0 mH));
270 }
271
272 #[test]
273 fn test_basic_failed() {
274 let result = resistor("Xhhh 1 n2 1.5k 777");
275 assert!(matches!(result, Err(Err::Error(_))));
276
277 let result = resistor("Rhhh 1 n2 _");
278 assert!(matches!(result, Err(Err::Failure(_))));
279
280 let result= resistor("Rhhh 1 \r\nn2 1.5k \n777");
281 assert!(matches!(result, Err(Err::Failure(_))));
282 }
283
284 #[test]
285 fn test_mosfet_parse() {
286 let input = "M1 D G S B NM L=1u W=5u VTH=0.7 KP=20u\n";
287 let (_, mos) = mos_fet(input).unwrap();
288
289 assert_eq!(mos.name, "1");
290 assert_eq!(mos.drain, "D");
291 assert_eq!(mos.gate, "G");
292 assert_eq!(mos.source, "S");
293 assert_eq!(mos.bulk, "B");
294 assert_eq!(mos.model_name, "NM");
295 assert_eq!(mos.length, u!(1.0 um));
296 assert_eq!(mos.width, u!(5.0 um));
297 assert_eq!(mos.parameters.get("VTH"), Some(&num!(0.7)));
298 assert_eq!(mos.parameters.get("KP"), Some(&num!(20.0 u)));
299
300 let input = "M1 out in vdd vdd pmos L=1u W=2u\n";
301 let (_, mos) = mos_fet(input).unwrap();
302
303 assert_eq!(mos.name, "1");
304 assert_eq!(mos.drain, "out");
305 assert_eq!(mos.gate, "in");
306 assert_eq!(mos.source, "vdd");
307 assert_eq!(mos.bulk, "vdd");
308 assert_eq!(mos.model_name, "pmos");
309 assert_eq!(mos.length, u!(1.0 um));
310 assert_eq!(mos.width, u!(2.0 um));
311
312 let input = "M1 \n+out in vdd vdd \n+pmos L=1u W=2u\n";
313 let (_, mos) = mos_fet(input).unwrap();
314
315 assert_eq!(mos.name, "1");
316 assert_eq!(mos.drain, "out");
317 assert_eq!(mos.gate, "in");
318 assert_eq!(mos.source, "vdd");
319 assert_eq!(mos.bulk, "vdd");
320 assert_eq!(mos.model_name, "pmos");
321 assert_eq!(mos.length, u!(1.0 um));
322 assert_eq!(mos.width, u!(2.0 um));
323 }
324
325 #[test]
326 fn test_mosfet_failed() {
327 let result = mos_fet("KK D G S B NM L=1u W=5u VTH=0.7 KP=20u\n");
328 assert!(matches!(result, Err(Err::Error(_))));
329
330 let result = mos_fet("M1 D G S B NM L=1u VTH=0.7 KP=20u\n");
331 assert!(matches!(result, Err(Err::Failure(_))));
332
333 let result = mos_fet("M1 D G NM L=1u VTH=0.7 KP=20u\n");
334 assert!(matches!(result, Err(Err::Failure(_))));
335 }
336
337 #[test]
338 fn test_component_match() {
339 macro_rules! assert_component {
340 ($input:expr, $variant:path) => {{
341 let (_, c) = component($input).expect("parse failed");
342 match c {
343 $variant(_) => {}
344 _ => panic!("Expected {:?}, got {:?}", stringify!($variant), c),
345 }
346 }};
347 }
348
349 assert_component!("R1 n1 n2 10k", Component::R);
350 assert_component!("C1 n2 0 5u", Component::C);
351 assert_component!("L1 n2 n3 10n", Component::L);
352 assert_component!("D1 n1 n0 Dmod", Component::D);
353 assert_component!("Q1 c b e NPN", Component::Q);
354 assert_component!("M1 d g s b Mmod L=1u W=5u", Component::M);
355 }
356
357 #[test]
358 fn test_component_failed() {
359 let result = component("KK D G S B NM L=1u W=5u VTH=0.7 KP=20u\n");
360 assert!(matches!(result, Err(Err::Error(_))));
361
362 let result = mos_fet("M2 D G S B NM L=1u VTH=0.7 KP=20u\n");
363 assert!(matches!(result, Err(Err::Failure(_))));
364 }
365
366}