1use nom::{
2 branch::alt, bytes::complete::{tag, tag_no_case, take_until}, combinator::map, error::context, sequence::{delimited, preceded}
3};
4
5use crate::model::{AnalysisType, FindWhenCondition, EdgeType, MeasureBasicStat, MeasureCommand, MeasureFindWhen, MeasureFunction, MeasureRise, OutputSuffix, OutputVariable, TrigTargCondition};
6use super::{hws, identifier, number, time_number, unsigned_int, NomResult, ToFailure};
7
8pub fn measure_command(input: &str) -> NomResult<MeasureCommand> {
10 context("measure_command", |input| {
11 let (input, _) = context("keyword", hws(tag_no_case(".MEAS")))(input)?;
12 let (input, analysis) = context("analysis_type", hws(analysis_type))(input).to_failure()?;
13 let (input, name) = context("measure_name", hws(identifier))(input).to_failure()?;
14
15 let x = alt((
16 context("measure_rise", map(|i| measure_rise(i, name, analysis), MeasureCommand::Rise)),
17 context("measure_basic_stat", map(|i| measure_basic_stat(i, name, analysis), MeasureCommand::BasicStat)),
18 context("measure_find_when", map(|i| measure_find_when(i, name, analysis), MeasureCommand::FindWhen)),
19 ))(input).to_failure();
20 x
21 })(input)
22}
23
24fn measure_rise<'a>(input: &'a str, name: &'a str, analysis: AnalysisType) -> NomResult<'a, MeasureRise> {
27 let (input, _) = context("TRIG keyword", hws(tag_no_case("TRIG")))(input)?;
28 let (input, trig) = context("trigger_condition", hws(trig_targ_condition))(input).to_failure()?;
29 let (input, _) = context("TARG keyword", hws(tag_no_case("TARG")))(input).to_failure()?;
30 let (input, targ) = context("target_condition", hws(trig_targ_condition))(input).to_failure()?;
31
32 Ok((input, MeasureRise { name: name.to_string(), analysis, trig, targ }))
33}
34
35fn measure_basic_stat<'a>(input: &'a str, name: &'a str, analysis: AnalysisType) -> NomResult<'a, MeasureBasicStat> {
37 let (input, stat) = context("stat_function", hws(measure_function))(input)?;
38 let (input, variable) = context("variable", hws(output_variable))(input).to_failure()?;
39 let (input, from) = context("FROM value", preceded(hws(tag_no_case("FROM=")), hws(time_number)))(input).to_failure()?;
40 let (input, to) = context("TO value", preceded(hws(tag_no_case("TO=")), hws(time_number)))(input).to_failure()?;
41
42 Ok((input, MeasureBasicStat { name: name.to_string(), analysis, stat, variable, from, to }))
43}
44
45fn measure_find_when<'a>(input: &'a str, name: &'a str, analysis: AnalysisType) -> NomResult<'a, MeasureFindWhen> {
47 let (input, _) = context("FIND keyword", hws(tag_no_case("FIND")))(input)?;
48 let (input, variable) = context("variable", hws(output_variable))(input).to_failure()?;
49 let (input, _) = context("WHEN keyword", hws(tag_no_case("WHEN")))(input).to_failure()?;
50 let (input, condition) = context("condition", hws(finwhen_condition))(input).to_failure()?;
51
52 Ok((input, MeasureFindWhen { name: name.to_string(), analysis, variable, when: condition }))
53}
54
55
56fn trig_targ_condition(input: &str) -> NomResult<TrigTargCondition> {
58 let (input, variable) = hws(output_variable)(input)?;
59 let (input, _) = hws(tag_no_case("VAL="))(input)?;
60 let (input, value) = hws(number)(input)?;
61 let (input, edge) = hws(alt((
62 map(tag_no_case("RISE"), |_| EdgeType::Rise),
63 map(tag_no_case("FALL"), |_| EdgeType::Fall),
64 )))(input)?;
65 let (input, _) = hws(tag("="))(input)?;
66 let (input, num) = hws(unsigned_int)(input)?;
67
68 Ok((
69 input,
70 TrigTargCondition {
71 variable,
72 value,
73 edge,
74 number: num as usize,
75 },
76 ))
77}
78
79fn measure_function(input: &str) -> NomResult<MeasureFunction> {
80 map(
81 hws(alt((
82 tag_no_case("AVG"),
83 tag_no_case("RMS"),
84 tag_no_case("MIN"),
85 tag_no_case("MAX"),
86 tag_no_case("PP"),
87 tag_no_case("DERIV"),
88 tag_no_case("INTEGRATE"),
89 ))),
90 |s: &str| match &s.to_ascii_uppercase()[..] {
91 "AVG" => MeasureFunction::Avg,
92 "RMS" => MeasureFunction::Rms,
93 "MIN" => MeasureFunction::Min,
94 "MAX" => MeasureFunction::Max,
95 "PP" => MeasureFunction::Pp,
96 "DERIV" => MeasureFunction::Deriv,
97 "INTEGRATE" => MeasureFunction::Integrate,
98 _ => unreachable!(),
99 },
100 )(input)
101}
102
103fn finwhen_condition(input: &str) -> NomResult<FindWhenCondition> {
104 let (input, variable) = hws(output_variable)(input)?;
105 let (input, _) = hws(tag("="))(input)?;
106 let (input, value) = hws(number)(input)?;
107
108 Ok((input, FindWhenCondition { variable, value }))
109}
110
111fn output_variable(input: &str) -> NomResult<OutputVariable> {
112 let (input, kind) = hws(alt((tag_no_case("V"), tag_no_case("I"))))(input)?;
113
114 let (input, var) = hws(delimited(
115 hws(tag("(")),
116 take_until(")"),
117 tag(")"),
118 ))(input)?;
119
120 let suffix = if var.ends_with("M") {
121 Some(OutputSuffix::Magnitude)
122 } else if var.ends_with("DB") {
123 Some(OutputSuffix::Decibel)
124 } else if var.ends_with("P") {
125 Some(OutputSuffix::Phase)
126 } else if var.ends_with("R") {
127 Some(OutputSuffix::Real)
128 } else if var.ends_with("I") {
129 Some(OutputSuffix::Imag)
130 } else {
131 None
132 };
133
134 if kind.eq_ignore_ascii_case("V") {
135 let parts = var.split(',').map(|s| s.trim()).collect::<Vec<_>>();
136 let node1 = parts.get(0).unwrap_or(&"").to_string();
137 let node2 = parts.get(1).map(|s| s.to_string());
138 Ok((input, OutputVariable::Voltage { node1, node2, suffix }))
139 } else {
140 Ok((input, OutputVariable::Current {
141 element_name: var.to_string(),
142 suffix,
143 }))
144 }
145}
146
147fn analysis_type(input: &str) -> NomResult<AnalysisType> {
148 map(
149 hws(alt((tag_no_case("TRAN"), tag_no_case("AC"), tag_no_case("DC")))),
150 |s: &str| match &s.to_ascii_uppercase()[..] {
151 "TRAN" => AnalysisType::Tran,
152 "AC" => AnalysisType::Ac,
153 "DC" => AnalysisType::Dc,
154 _ => unreachable!(),
155 },
156 )(input)
157}
158
159#[cfg(test)]
160mod tests {
161 use nom::{error::convert_error, Err};
162
163 use super::*;
164 use reda_unit::{num, u};
165
166 #[test]
167 fn test_measure_rise() {
168 let input = ".MEAS TRAN rise1 TRIG V(n1) VAL=0.2 RISE=1 TARG V(n1) VAL=0.8 RISE=1";
169 let (_, meas) = measure_command(input).unwrap();
170
171 if let MeasureCommand::Rise(m) = meas {
172 assert_eq!(m.name, "rise1");
173 assert_eq!(m.analysis, AnalysisType::Tran);
174 assert_eq!(m.trig.value, num!(0.2));
175 assert_eq!(m.trig.edge, EdgeType::Rise);
176 assert_eq!(m.trig.number, 1);
177 assert_eq!(m.targ.value, num!(0.8));
178 assert_eq!(m.targ.edge, EdgeType::Rise);
179 assert_eq!(m.targ.number, 1);
180 } else {
181 panic!("Expected MeasureCommand::Rise");
182 }
183 }
184
185 #[test]
186 fn test_measure_basic_stat() {
187 let input = ".MEAS TRAN avgval AVG V(n1) FROM=10u TO=55u";
188 let (_, meas) = measure_command(input).unwrap();
189
190 if let MeasureCommand::BasicStat(m) = meas {
191 assert_eq!(m.name, "avgval");
192 assert_eq!(m.analysis, AnalysisType::Tran);
193 assert_eq!(m.stat, MeasureFunction::Avg);
194 assert!(matches!(m.variable, OutputVariable::Voltage { .. }));
195 assert_eq!(m.from, u!(10. us));
196 assert_eq!(m.to, u!(55. us));
197 } else {
198 panic!("Expected MeasureCommand::BasicStat");
199 }
200 }
201
202 #[test]
203 fn test_measure_find_when() {
204 let input = ".MEAS TRAN DesiredCurr FIND I(Vmeas) WHEN V(n1)=1V";
205 let (_, meas) = measure_command(input).unwrap();
206
207 if let MeasureCommand::FindWhen(m) = meas {
208 assert_eq!(m.name, "DesiredCurr");
209 assert_eq!(m.analysis, AnalysisType::Tran);
210 assert!(matches!(m.variable, OutputVariable::Current { .. }));
211 assert_eq!(m.when.value, num!(1));
212 } else {
213 panic!("Expected MeasureCommand::FindWhen");
214 }
215 }
216
217 #[test]
218 fn test_measure_bad_prefix() {
219 let input = ".XXX TRAN AVG V(1) FROM=0 TO=1";
220 let result = measure_command(input);
221 assert!(matches!(result, Err(Err::Error(_))));
222 }
223
224 #[test]
225 fn test_measure_unknown_type() {
226 let input = ".MEAS TRAN BOGUS V(1) FROM=0 TO=1";
227 let result = measure_command(input);
228 assert!(matches!(result, Err(Err::Failure(_))));
229 }
230
231 #[test]
232 fn test_measure_stat_missing_to() {
233 let input = ".MEAS TRAN result AVG V(1) FROM=1";
234 let result = measure_command(input);
235 assert!(matches!(result, Err(Err::Failure(_))));
236 }
237
238 #[test]
239 fn test_measure_rise_missing_targ() {
240 let input = ".MEAS TRAN rise TRIG V(1) VAL=.2 RISE=1";
241 let result = measure_command(input);
242 assert!(matches!(result, Err(Err::Failure(_))));
243 }
244
245 #[test]
246 fn test_measure_find_when_invalid_condition() {
247 let input = ".MEAS TRAN result FIND I(R1) WHEN V(1) == 1";
248 let result = measure_command(input);
249 assert!(matches!(result, Err(Err::Failure(_))));
250 }
251
252 #[test]
253 fn test_measure_debug_context() {
254 let input = ".MEAS TRAN avgval AVG V(1) FROM=10ns TO=abc";
255 let result = measure_command(input);
256 if let Err(Err::Failure(e)) = result {
257 println!("{}", convert_error(input, e));
258 }
259 }
260}