reda_sp/parse/
components.rs

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
22/// Rname N+ N- Value
23pub 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
47/// Cname N+ N- Value <IC=Initial Condition>
48pub 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
73/// Lname N+ N- Value <IC=Initial Condition>
74pub 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
99/// Dname N+ N- MODName
100pub 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
125/// Qname NC NB NE Model
126pub 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
153/// Mname ND NG NS NB ModelName [params]
154pub 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
199/// .model <name> <type> (<param1=val1 param2=val2 ...>)
200pub 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
234/// Parse a key=value pair where key is identifier and value is Number
235fn 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}