reda_sp/parse/
simulate.rs

1use nom::error::{context, VerboseError, VerboseErrorKind};
2use nom::Err;
3use nom::{branch::alt, bytes::complete::tag_no_case, combinator::opt};
4use nom::combinator::map;
5use crate::model::{AcCommand, AcSweepType, DcCommand, SimCommand, TranCommand};
6use super::{frequency_number, hws, identifier, time_number, unsigned_int, voltage_number, NomResult, ToFailure};
7
8pub fn sim_command(input: &str) -> NomResult<SimCommand> {
9    alt((
10        map(op_command, |_| SimCommand::Op),
11        map(dc_command, SimCommand::Dc),
12        map(ac_command, SimCommand::Ac),
13        map(tran_command, SimCommand::Tran),
14    ))(input)
15}
16
17/// .OP
18pub fn op_command(input: &str) -> NomResult<()> {
19    context("dc_command", |input| -> NomResult<()> {
20        let (input, _) = context("keyword", hws(tag_no_case(".OP")))(input)?;
21        Ok((input, ()))
22    })(input)
23}
24
25/// .DC SRCname START STOP STEP
26pub fn dc_command(input: &str) -> NomResult<DcCommand> {
27    context("dc_command", |input| {
28        let (input, _) = context("keyword", hws(tag_no_case(".DC")))(input)?;
29        let (input, src_name) = context("source_name", hws(identifier))(input).to_failure()?;
30        let (input, start) = context("start_value", hws(voltage_number))(input).to_failure()?;
31        let (input, stop) = context("stop_value", hws(voltage_number))(input).to_failure()?;
32        let (input, step) = context("step_value", hws(voltage_number))(input).to_failure()?;
33
34        Ok((input, DcCommand {
35            src_name: src_name.to_string(),
36            start,
37            stop,
38            step,
39        }))
40    })(input)
41}
42
43/// .AC LIN NP FSTART FSTOP
44pub fn ac_command(input: &str) -> NomResult<AcCommand> {
45    context("ac_command", |input| {
46        let (input, _) = context("keyword", hws(tag_no_case(".AC")))(input)?;
47        let (input, sweep_type_str) = context("sweep_type", hws(alt((
48            tag_no_case("LIN"),
49            tag_no_case("DEC"),
50            tag_no_case("OCT"),
51        ))))(input).to_failure()?;
52        let sweep_type = match &sweep_type_str.to_ascii_uppercase()[..] {
53            "LIN" => AcSweepType::Lin,
54            "DEC" => AcSweepType::Dec,
55            "OCT" => AcSweepType::Oct,
56            _ => unreachable!(),
57        };
58
59        let (input, points) = context("points", hws(unsigned_int))(input).to_failure()?;
60        let (input, f_start) = context("f_start", hws(frequency_number))(input).to_failure()?;
61        let (input, f_stop) = context("f_stop", hws(frequency_number))(input).to_failure()?;
62
63        Ok((input, AcCommand {
64            sweep_type,
65            points: points as usize,
66            f_start,
67            f_stop,
68        }))
69    })(input)
70}
71
72/// .TRAN TSTEP TSTOP <TSTART <TMAX>> <UIC>
73pub fn tran_command(input: &str) -> NomResult<TranCommand> {
74    context("tran_command", |input| {
75        let (input, _) = context("keyword", hws(tag_no_case(".TRAN")))(input)?;
76        let (input, t_step) = context("t_step", hws(time_number))(input).to_failure()?;
77        let (input, t_stop) = context("t_stop", hws(time_number))(input).to_failure()?;
78        let (input, t_start) = context("t_start", opt(hws(time_number)))(input).to_failure()?;
79        let (input, t_max) = context("t_max", opt(hws(time_number)))(input).to_failure()?;
80        let (input, uic) = context("UIC_flag", opt(hws(identifier)))(input).to_failure()?;
81        if let Some(uic) = uic {
82            if !uic.eq_ignore_ascii_case("UIC") {
83                return Err(Err::Failure(VerboseError {
84                    errors: [(input, VerboseErrorKind::Context("expected UIC or end of line"))].into(),
85                }));
86            }
87        }
88
89        Ok((input, TranCommand {
90            t_step,
91            t_stop,
92            t_start,
93            t_max,
94            uic: uic.is_some(),
95        }))
96    })(input)
97}
98
99#[allow(unused)]
100#[cfg(test)]
101mod test {
102    use nom::{error::convert_error, Err};
103
104    use reda_unit::{num, u};
105    use super::*;
106
107    #[test]
108    fn test_op_command() {
109        let (left, dc) = op_command(".OP xx").unwrap();
110        assert_eq!("xx", left);
111    }
112
113    #[test]
114    fn test_dc_command() {
115        let (_, dc) = dc_command(".DC V1 0 5 0.1").unwrap();
116        assert_eq!(dc.src_name, "V1");
117        assert_eq!(dc.step, u!(0.1 V));
118    }
119
120    #[test]
121    fn test_ac_command() {
122        let (_, ac) = ac_command(".AC DEC 10 1 1000").unwrap();
123        assert_eq!(ac.sweep_type, AcSweepType::Dec);
124        assert_eq!(ac.f_stop, u!(1000.0 Hz));
125    }
126
127    #[test]
128    fn test_tran_command_with_uic() {
129        let (_, tran) = tran_command(".TRAN 1n 100n 0 10n UIC").unwrap();
130        assert_eq!(tran.t_step, u!(1. ns));
131        assert_eq!(tran.t_max, Some(u!(10. ns)));
132        assert!(tran.uic);
133    }
134
135    #[test]
136    fn test_dc_command_invalid_number() {
137        let input = ".DC V1 0 5 xyz";
138        let result = sim_command(input);
139        if let Err(Err::Failure(e)) = result.clone() {
140            println!("{}", convert_error(input, e));
141        }
142        assert!(matches!(result, Err(Err::Failure(_))));
143    }
144
145    #[test]
146    fn test_ac_command_bad_sweep() {
147        let input = ".AC XXX 10 1k 10k";
148        let result = sim_command(input);
149        assert!(matches!(result, Err(Err::Failure(_))));
150    }
151
152    #[test]
153    fn test_tran_command_missing_stop() {
154        let input = ".TRAN 0.1n";
155        let result = sim_command(input);
156        assert!(matches!(result, Err(Err::Failure(_))));
157    }
158
159    #[test]
160    fn test_tran_command_invalid_uic() {
161        let input = ".TRAN 1n 10n 0n 1n unknownflag";
162        let result = sim_command(input);
163        if let Err(Err::Failure(e)) = &result {
164            println!("{}", convert_error(input, e.clone()));
165        }
166        assert!(matches!(result, Err(Err::Failure(_))));
167    }
168}