Skip to main content

rs_teststand/expression/operator/
other.rs

1//! Operators that shape an expression rather than compute a value.
2//!
3//! These are syntax: they group, select, index and separate. None of them takes
4//! two operands and returns a value the way an arithmetic operator does, so
5//! they carry no precedence here, where they bind is a property of the grammar
6//! rather than of a level in a table.
7
8/// A structural element of the expression language.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum OtherOperator {
11    /// Parentheses, `()`, force evaluation order.
12    Parentheses,
13    /// Dot, `.`, separates a property from its field.
14    FieldSeparator,
15    /// Double dot, `..`, a range of indexes inside a subscript, selecting
16    /// several elements and yielding a subarray.
17    IndexRange,
18    /// Brackets, `[]`, array subscript.
19    ///
20    /// The subscript is normally numeric. Arrays of steps or sequences also
21    /// accept the element's name as a string.
22    Subscript,
23    /// Comma, `,`, separates or terminates expressions.
24    Separator,
25    /// Conditional, `?:`, picks one of two expressions from a boolean.
26    Conditional,
27    /// Braces, `{}`, an array constant.
28    ArrayConstant,
29    /// `//`, a comment running to the end of the line.
30    LineComment,
31    /// `'`, a comment running to the end of the line, in the Basic style.
32    LineCommentBasic,
33    /// `/* */`, a comment spanning any amount of text.
34    BlockComment,
35}
36
37impl OtherOperator {
38    /// Every structural element modeled here.
39    pub const ALL: [Self; 10] = [
40        Self::Parentheses,
41        Self::FieldSeparator,
42        Self::IndexRange,
43        Self::Subscript,
44        Self::Separator,
45        Self::Conditional,
46        Self::ArrayConstant,
47        Self::LineComment,
48        Self::LineCommentBasic,
49        Self::BlockComment,
50    ];
51
52    /// How it is written. Paired forms are given as the pair.
53    #[must_use]
54    pub const fn symbol(self) -> &'static str {
55        match self {
56            Self::Parentheses => "()",
57            Self::FieldSeparator => ".",
58            Self::IndexRange => "..",
59            Self::Subscript => "[]",
60            Self::Separator => ",",
61            Self::Conditional => "?:",
62            Self::ArrayConstant => "{}",
63            Self::LineComment => "//",
64            Self::LineCommentBasic => "'",
65            Self::BlockComment => "/* */",
66        }
67    }
68
69    /// Whether it introduces a comment rather than affecting evaluation.
70    #[must_use]
71    pub const fn is_comment(self) -> bool {
72        matches!(
73            self,
74            Self::LineComment | Self::LineCommentBasic | Self::BlockComment
75        )
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::OtherOperator;
82
83    #[test]
84    fn comments_are_identified_as_such() {
85        assert!(OtherOperator::LineComment.is_comment());
86        assert!(OtherOperator::BlockComment.is_comment());
87        assert!(!OtherOperator::Subscript.is_comment());
88    }
89
90    #[test]
91    fn the_range_and_field_separators_are_distinguishable() {
92        // A single dot selects a field, two dots select a span of indexes;
93        // confusing them silently changes what an expression returns.
94        assert_eq!(OtherOperator::FieldSeparator.symbol(), ".");
95        assert_eq!(OtherOperator::IndexRange.symbol(), "..");
96        assert_ne!(
97            OtherOperator::FieldSeparator.symbol(),
98            OtherOperator::IndexRange.symbol()
99        );
100    }
101
102    #[test]
103    fn every_element_has_a_distinct_spelling() {
104        let mut symbols: Vec<&str> = OtherOperator::ALL.iter().map(|op| op.symbol()).collect();
105        symbols.sort_unstable();
106        let count = symbols.len();
107        symbols.dedup();
108        assert_eq!(symbols.len(), count);
109    }
110}