Skip to main content

wp_primitives/
symbol.rs

1use winnow::ascii::multispace0;
2use winnow::combinator::alt;
3use winnow::error::{StrContext, StrContextValue};
4use winnow::token::literal;
5use winnow::{ModalResult as WResult, Parser};
6
7#[derive(Debug, PartialEq, Clone)]
8pub enum LogicSymbol {
9    And,
10    Or,
11    Not,
12}
13
14#[derive(Debug, PartialEq, Clone, Copy)]
15pub enum CmpSymbol {
16    // width match =*
17    We,
18    Eq,
19    Ne,
20    Gt,
21    Ge,
22    Lt,
23    Le,
24}
25
26// ============================================================================
27// Macros for reducing boilerplate code
28// ============================================================================
29
30/// Macro to define simple symbol parsers that return `()`
31macro_rules! define_unit_symbol {
32    ($name:ident, $lit:expr, $desc:expr) => {
33        #[doc = concat!("Parses the `", $lit, "` symbol.")]
34        pub fn $name(data: &mut &str) -> WResult<()> {
35            multispace0.parse_next(data)?;
36            literal($lit)
37                .context(StrContext::Label("symbol"))
38                .context(StrContext::Expected(StrContextValue::Description($desc)))
39                .parse_next(data)?;
40            Ok(())
41        }
42    };
43}
44
45/// Macro to define logic symbol parsers that return `LogicSymbol`
46macro_rules! define_logic_symbol {
47    ($name:ident, $lit:expr, $desc:expr, $variant:expr) => {
48        #[doc = concat!("Parses the `", $lit, "` logic operator.")]
49        pub fn $name(data: &mut &str) -> WResult<LogicSymbol> {
50            multispace0.parse_next(data)?;
51            literal($lit)
52                .context(StrContext::Label("symbol"))
53                .context(StrContext::Expected(StrContextValue::Description($desc)))
54                .parse_next(data)?;
55            Ok($variant)
56        }
57    };
58}
59
60/// Macro to define comparison symbol parsers that return `CmpSymbol`
61macro_rules! define_cmp_symbol {
62    ($name:ident, $lit:expr, $label:expr, $desc:expr, $variant:expr) => {
63        #[doc = concat!("Parses the `", $lit, "` comparison operator.")]
64        pub fn $name(data: &mut &str) -> WResult<CmpSymbol> {
65            multispace0.parse_next(data)?;
66            literal($lit)
67                .context(StrContext::Label($label))
68                .context(StrContext::Expected(StrContextValue::Description($desc)))
69                .parse_next(data)?;
70            Ok($variant)
71        }
72    };
73}
74
75// ============================================================================
76// Logic Operators
77// ============================================================================
78
79define_logic_symbol!(symbol_logic_and, "&&", "need '&&'", LogicSymbol::And);
80define_logic_symbol!(symbol_logic_or, "||", "need '||'", LogicSymbol::Or);
81define_logic_symbol!(symbol_logic_not, "!", "need '!'", LogicSymbol::Not);
82
83// ============================================================================
84// Punctuation and Delimiters
85// ============================================================================
86
87define_unit_symbol!(symbol_match_to, "=>", "need '=>'");
88define_unit_symbol!(symbol_var, "var", "need 'var'");
89define_unit_symbol!(symbol_comma, ",", "need ','");
90define_unit_symbol!(symbol_bracket_beg, "(", "need '('");
91define_unit_symbol!(symbol_bracket_end, ")", "need ')'");
92define_unit_symbol!(symbol_brace_beg, "{", "need '{'");
93define_unit_symbol!(symbol_brace_end, "}", "need '}'");
94define_unit_symbol!(symbol_under_line, "_", "need '_'");
95define_unit_symbol!(symbol_marvel, "!", "need '!'");
96define_unit_symbol!(symbol_brackets_beg, "[", "need '['");
97define_unit_symbol!(symbol_brackets_end, "]", "need ']'");
98define_unit_symbol!(symbol_colon, ":", "need ':'");
99define_unit_symbol!(symbol_semicolon, ";", "need ';'");
100define_unit_symbol!(symbol_pipe, "|", "need '|' pipe symbol");
101define_unit_symbol!(symbol_assign, "=", "need '='");
102define_unit_symbol!(symbol_dollar, "$", "need '$'");
103
104// ============================================================================
105// Comparison Operators
106// ============================================================================
107
108define_cmp_symbol!(symbol_cmp_eq, "==", "symbol", "need '=='", CmpSymbol::Eq);
109define_cmp_symbol!(symbol_cmp_we, "=*", "symbol", "need '=*'", CmpSymbol::We);
110define_cmp_symbol!(symbol_cmp_ne, "!=", "symbol", "need '!='", CmpSymbol::Ne);
111define_cmp_symbol!(symbol_cmp_ge, ">=", "symbol ge", "need '>='", CmpSymbol::Ge);
112define_cmp_symbol!(symbol_cmp_gt, ">", "symbol gt", "need '>'", CmpSymbol::Gt);
113define_cmp_symbol!(symbol_cmp_le, "<=", "symbol ge", "need '<='", CmpSymbol::Le);
114define_cmp_symbol!(symbol_cmp_lt, "<", "symbol gt", "need '<'", CmpSymbol::Lt);
115
116// ============================================================================
117// Combined Parsers
118// ============================================================================
119
120/// Parses any comparison operator and returns the corresponding `CmpSymbol`.
121///
122/// Tries operators in a specific order to handle multi-character operators correctly.
123/// For example, `>=` is tried before `>` to avoid parsing it as `>` followed by `=`.
124pub fn symbol_cmp(data: &mut &str) -> WResult<CmpSymbol> {
125    alt((
126        symbol_cmp_eq,
127        symbol_cmp_ne,
128        symbol_cmp_we,
129        symbol_cmp_le,
130        symbol_cmp_ge,
131        symbol_cmp_lt,
132        symbol_cmp_gt,
133    ))
134    .parse_next(data)
135}
136
137/// Parses any logic operator and returns the corresponding `LogicSymbol`.
138pub fn symbol_logic(data: &mut &str) -> WResult<LogicSymbol> {
139    alt((symbol_logic_and, symbol_logic_or, symbol_logic_not)).parse_next(data)
140}
141
142// ============================================================================
143// Helper Functions for Error Context
144// ============================================================================
145
146/// Creates a label context for winnow error reporting.
147#[inline(always)]
148pub fn ctx_label(label: &'static str) -> StrContext {
149    StrContext::Label(label)
150}
151
152/// Creates a string literal context for winnow error reporting.
153#[inline(always)]
154pub fn ctx_literal(lit: &'static str) -> StrContext {
155    StrContext::Expected(StrContextValue::StringLiteral(lit))
156}
157
158/// Creates a description context for winnow error reporting.
159#[inline(always)]
160pub fn ctx_desc(desc: &'static str) -> StrContext {
161    StrContext::Expected(StrContextValue::Description(desc))
162}