scpi/parser/expression/
numeric_list.rs

1//! # 8.3.3 Numeric Lists
2//!
3//! A numeric list is a an expression format for compactly expressing numbers and ranges of
4//! numbers in a single parameter.
5
6use crate::error::{Error, ErrorCode};
7
8type Number<'a> = crate::parser::tokenizer::Token<'a>;
9
10#[derive(Clone, PartialEq, Eq, Debug)]
11pub enum Token<'a> {
12    Numeric(Number<'a>),
13    NumericRange(Number<'a>, Number<'a>),
14}
15
16/// Numeric list expression tokenizer
17#[derive(Clone)]
18pub struct NumericList<'a> {
19    pub tokenizer: crate::parser::tokenizer::Tokenizer<'a>,
20    pub first: bool,
21}
22
23impl<'a> NumericList<'a> {
24    pub fn new(s: &'a [u8]) -> NumericList<'a> {
25        NumericList {
26            tokenizer: crate::parser::tokenizer::Tokenizer::new(s),
27            first: true,
28        }
29    }
30
31    fn read_numeric_data(&mut self) -> Result<Token<'a>, ErrorCode> {
32        let begin: Number = self.tokenizer.read_nrf()?;
33        if let Some(c) = self.tokenizer.chars.clone().next() {
34            //&& *c == b':' {
35            if *c == b':' {
36                self.tokenizer.chars.next();
37                let end = self.tokenizer.read_nrf()?;
38                return Ok(Token::NumericRange(begin, end));
39            }
40        }
41
42        Ok(Token::Numeric(begin))
43    }
44}
45
46impl<'a> Iterator for NumericList<'a> {
47    type Item = Result<Token<'a>, Error>;
48
49    fn next(&mut self) -> Option<Self::Item> {
50        //TODO: This has to be tokenizer abuse or something...
51        let char = self.tokenizer.chars.clone().next()?;
52
53        Some(match char {
54            b',' if !self.first => {
55                self.tokenizer.chars.next().unwrap();
56                self.read_numeric_data().map_err(|err| {
57                    Error::new(ErrorCode::InvalidExpression).extended(err.get_message())
58                })
59            }
60            x if x.is_ascii_digit() || *x == b'-' || *x == b'+' && self.first => {
61                self.first = false;
62                self.read_numeric_data().map_err(|err| {
63                    Error::new(ErrorCode::InvalidExpression).extended(err.get_message())
64                })
65            }
66            _ => Err(Error::new(ErrorCode::InvalidExpression).extended(b"Invalid character")),
67        })
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    extern crate std;
76
77    #[test]
78    fn test_numeric_data() {
79        let spec = NumericList::new(b"2").read_numeric_data();
80        assert_eq!(
81            spec,
82            Ok(Token::Numeric(Number::DecimalNumericProgramData(b"2")))
83        );
84        let range = NumericList::new(b"2:5").read_numeric_data();
85        assert_eq!(
86            range,
87            Ok(Token::NumericRange(
88                Number::DecimalNumericProgramData(b"2"),
89                Number::DecimalNumericProgramData(b"5")
90            ))
91        );
92        let specfail = NumericList::new(b"2::5").read_numeric_data();
93        assert_eq!(specfail, Err(ErrorCode::NumericDataError));
94    }
95
96    #[test]
97    fn test_numeric_list() {
98        let mut expr = NumericList::new(b"3.1415,1.1:3.9e6");
99        assert_eq!(
100            expr.next().unwrap(),
101            Ok(Token::Numeric(Number::DecimalNumericProgramData(b"3.1415")))
102        );
103        assert_eq!(
104            expr.next().unwrap(),
105            Ok(Token::NumericRange(
106                Number::DecimalNumericProgramData(b"1.1"),
107                Number::DecimalNumericProgramData(b"3.9e6")
108            ))
109        );
110        assert_eq!(expr.next(), None);
111    }
112
113    #[test]
114    fn test_numeric_leading() {
115        let mut expr = NumericList::new(b",1,2:5");
116        assert_eq!(
117            expr.next().unwrap(),
118            Err(Error::new(ErrorCode::InvalidExpression).extended(b"Invalid character"))
119        );
120    }
121
122    #[test]
123    fn test_numeric_repeated() {
124        let mut expr = NumericList::new(b"1,,2:5");
125        assert_eq!(
126            expr.next().unwrap(),
127            Ok(Token::Numeric(Number::DecimalNumericProgramData(b"1")))
128        );
129        assert_eq!(
130            expr.next().unwrap(),
131            Err(Error::new(ErrorCode::InvalidExpression)
132                .extended(ErrorCode::NumericDataError.get_message()))
133        );
134    }
135}