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
17pub 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
25pub 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
43pub 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
72pub 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}