sieve/compiler/grammar/actions/
action_set.rs1use serde::{Deserialize, Serialize};
25
26use crate::{
27 compiler::{
28 grammar::{
29 expr::Expression,
30 instruction::{CompilerState, Instruction},
31 },
32 lexer::{tokenizer::TokenInfo, word::Word, Token},
33 CompileError, ErrorType, Value, VariableType,
34 },
35 Envelope,
36};
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub enum Modifier {
40 Lower,
41 Upper,
42 LowerFirst,
43 UpperFirst,
44 QuoteWildcard,
45 QuoteRegex,
46 EncodeUrl,
47 Length,
48 Replace { find: Value, replace: Value },
49}
50
51impl Modifier {
52 pub fn order(&self) -> usize {
53 match self {
54 Modifier::Lower => 41,
55 Modifier::Upper => 40,
56 Modifier::LowerFirst => 31,
57 Modifier::UpperFirst => 30,
58 Modifier::QuoteWildcard => 20,
59 Modifier::QuoteRegex => 21,
60 Modifier::EncodeUrl => 15,
61 Modifier::Length => 10,
62 Modifier::Replace { .. } => 40,
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
68pub struct Set {
69 pub modifiers: Vec<Modifier>,
70 pub name: VariableType,
71 pub value: Value,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
75pub(crate) struct Let {
76 pub name: VariableType,
77 pub expr: Vec<Expression>,
78}
79
80impl<'x> CompilerState<'x> {
81 pub(crate) fn parse_set(&mut self) -> Result<(), CompileError> {
82 let mut modifiers = Vec::new();
83 let mut name = None;
84 let mut is_local = false;
85 let value;
86
87 loop {
88 let token_info = self.tokens.unwrap_next()?;
89 match token_info.token {
90 Token::Tag(
91 word @ (Word::Lower
92 | Word::Upper
93 | Word::LowerFirst
94 | Word::UpperFirst
95 | Word::QuoteWildcard
96 | Word::QuoteRegex
97 | Word::Length
98 | Word::EncodeUrl),
99 ) => {
100 let modifier = word.into();
101 if !modifiers.contains(&modifier) {
102 modifiers.push(modifier);
103 }
104 }
105 Token::Tag(Word::Replace) => {
106 let find = self.tokens.unwrap_next()?;
107 let replace = self.tokens.unwrap_next()?;
108 modifiers.push(Modifier::Replace {
109 find: self.parse_string_token(find)?,
110 replace: self.parse_string_token(replace)?,
111 });
112 }
113 Token::Tag(Word::Local) => {
114 is_local = true;
115 }
116 _ => {
117 if name.is_none() {
118 name = self.parse_variable_name(token_info, is_local)?.into();
119 } else {
120 value = self.parse_string_token(token_info)?;
121 break;
122 }
123 }
124 }
125 }
126
127 modifiers.sort_unstable_by_key(|m| std::cmp::Reverse(m.order()));
128
129 self.instructions.push(Instruction::Set(Set {
130 modifiers,
131 name: name.unwrap(),
132 value,
133 }));
134 Ok(())
135 }
136
137 pub(crate) fn parse_let(&mut self) -> Result<(), CompileError> {
138 let name = self.tokens.unwrap_next()?;
139 let name = self.parse_variable_name(name, false)?;
140 let expr = self.parse_expr()?;
141
142 self.instructions.push(Instruction::Let(Let { name, expr }));
143 Ok(())
144 }
145
146 pub(crate) fn parse_variable_name(
147 &mut self,
148 token_info: TokenInfo,
149 register_as_local: bool,
150 ) -> Result<VariableType, CompileError> {
151 match token_info.token {
152 Token::StringConstant(value) => self
153 .register_variable(value.into_string(), register_as_local)
154 .map_err(|error_type| CompileError {
155 line_num: token_info.line_num,
156 line_pos: token_info.line_pos,
157 error_type,
158 }),
159 _ => Err(token_info.custom(ErrorType::ExpectedConstantString)),
160 }
161 }
162
163 pub(crate) fn register_variable(
164 &mut self,
165 name: String,
166 register_as_local: bool,
167 ) -> Result<VariableType, ErrorType> {
168 let name = name.to_lowercase();
169 if let Some((namespace, part)) = name.split_once('.') {
170 match namespace {
171 "global" | "t" => Ok(VariableType::Global(part.to_string())),
172 "envelope" => Envelope::try_from(part)
173 .map(VariableType::Envelope)
174 .map_err(|_| ErrorType::InvalidNamespace(namespace.to_string())),
175 _ => Err(ErrorType::InvalidNamespace(namespace.to_string())),
176 }
177 } else {
178 Ok(if !self.is_var_global(&name) {
179 VariableType::Local(self.register_local_var(name, register_as_local))
180 } else {
181 VariableType::Global(name)
182 })
183 }
184 }
185}
186
187impl From<Word> for Modifier {
188 fn from(word: Word) -> Self {
189 match word {
190 Word::Lower => Modifier::Lower,
191 Word::Upper => Modifier::Upper,
192 Word::LowerFirst => Modifier::LowerFirst,
193 Word::UpperFirst => Modifier::UpperFirst,
194 Word::QuoteWildcard => Modifier::QuoteWildcard,
195 Word::QuoteRegex => Modifier::QuoteRegex,
196 Word::Length => Modifier::Length,
197 Word::EncodeUrl => Modifier::EncodeUrl,
198 _ => unreachable!(),
199 }
200 }
201}