wowlab_parsers/parsers/spell_desc/parser/
expr.rs1use super::{
4 super::{
5 lexer::{ExprToken, lex_expr},
6 types::{
7 BinaryExpressionNode, BinaryOperator, ExpressionConditionNode, ExpressionNode,
8 FunctionCallNode, MiscVariableNode, NumberLiteralNode, ParenExpressionNode,
9 SingleConditionNode, UnaryExpressionNode, UnaryOperator, VariableNode,
10 },
11 },
12 ParseError,
13 variables::{
14 parse_at_var, parse_cross_spell_ref, parse_custom_var, parse_effect_var, parse_enchant_var,
15 parse_misc_var, parse_player_var, parse_spell_level_var,
16 },
17};
18use crate::parsers::TokenStream;
19
20pub(super) struct ExprParser<'a> {
21 stream: TokenStream<'a, ExprToken<'a>>,
22 decimal_places: Option<u8>,
23 pub(super) errors: Vec<ParseError>,
24}
25
26impl<'a> ExprParser<'a> {
27 pub(super) fn new(tokens: &'a [ExprToken<'a>]) -> Self {
28 Self {
29 stream: TokenStream::new(tokens),
30 decimal_places: None,
31 errors: Vec::new(),
32 }
33 }
34
35 pub(super) fn parse(&mut self) -> (Option<ExpressionNode>, Option<u8>) {
36 let expr = self.parse_expression();
37
38 if let Some(ExprToken::DecimalFormat(d)) = self.peek() {
39 self.decimal_places = Some(*d);
40 self.advance();
41 }
42
43 (expr, self.decimal_places)
44 }
45
46 fn peek(&self) -> Option<&ExprToken<'a>> {
47 self.stream.peek()
48 }
49
50 fn advance(&mut self) -> Option<&ExprToken<'a>> {
51 self.stream.advance()
52 }
53
54 fn parse_expression(&mut self) -> Option<ExpressionNode> {
55 self.parse_additive()
56 }
57
58 fn parse_additive(&mut self) -> Option<ExpressionNode> {
59 let mut left = self.parse_multiplicative()?;
60
61 loop {
62 let op = match self.peek() {
63 Some(ExprToken::Plus) => BinaryOperator::Add,
64 Some(ExprToken::Minus) => BinaryOperator::Sub,
65 _ => break,
66 };
67
68 self.advance();
69
70 let right = self.parse_multiplicative()?;
71
72 left = ExpressionNode::Binary(BinaryExpressionNode {
73 left: Box::new(left),
74 operator: op,
75 right: Box::new(right),
76 });
77 }
78
79 Some(left)
80 }
81
82 fn parse_multiplicative(&mut self) -> Option<ExpressionNode> {
83 let mut left = self.parse_unary()?;
84
85 loop {
86 let op = match self.peek() {
87 Some(ExprToken::Star) => BinaryOperator::Mul,
88 Some(ExprToken::Slash) => BinaryOperator::Div,
89 _ => break,
90 };
91
92 self.advance();
93
94 let right = self.parse_unary()?;
95
96 left = ExpressionNode::Binary(BinaryExpressionNode {
97 left: Box::new(left),
98 operator: op,
99 right: Box::new(right),
100 });
101 }
102
103 Some(left)
104 }
105
106 fn parse_unary(&mut self) -> Option<ExpressionNode> {
107 if matches!(self.peek(), Some(ExprToken::Minus)) {
108 self.advance();
109 let operand = self.parse_atomic()?;
110
111 return Some(ExpressionNode::Unary(UnaryExpressionNode {
112 operator: UnaryOperator::Neg,
113 operand: Box::new(operand),
114 }));
115 }
116
117 self.parse_atomic()
118 }
119
120 fn parse_atomic(&mut self) -> Option<ExpressionNode> {
122 match self.peek() {
123 Some(ExprToken::LParen) => {
124 self.advance();
125 let expr = self.parse_expression()?;
126
127 if matches!(self.peek(), Some(ExprToken::RParen)) {
128 self.advance();
129 }
130
131 Some(ExpressionNode::Paren(ParenExpressionNode {
132 expression: Box::new(expr),
133 }))
134 }
135
136 Some(ExprToken::DollarFunc(s)) => {
138 let func_name = s[1..].to_string();
139
140 self.advance();
141
142 Some(self.parse_function_args(func_name))
143 }
144
145 Some(ExprToken::Ident(s)) => {
146 let func_name = (*s).to_string();
147
148 self.advance();
149
150 if matches!(self.peek(), Some(ExprToken::LParen)) {
151 Some(self.parse_function_args(func_name))
152 } else {
153 self.errors.push(ParseError::new(format!(
154 "Unexpected identifier: {func_name}"
155 )));
156
157 None
158 }
159 }
160
161 Some(ExprToken::Number(n)) => {
162 let value = *n;
163
164 self.advance();
165
166 Some(ExpressionNode::Number(NumberLiteralNode::new(value)))
167 }
168
169 Some(ExprToken::CustomVar(s)) => {
170 let var = parse_custom_var(s);
171
172 self.advance();
173
174 Some(ExpressionNode::Variable(Box::new(var)))
175 }
176
177 Some(ExprToken::EffectVar(s)) => {
178 let var = parse_effect_var(s);
179
180 self.advance();
181
182 Some(ExpressionNode::Variable(Box::new(var)))
183 }
184
185 Some(ExprToken::SpellLevelVar(s)) => {
186 let var = parse_spell_level_var(s);
187
188 self.advance();
189
190 Some(ExpressionNode::Variable(Box::new(var)))
191 }
192
193 Some(ExprToken::PlayerVar(s)) => {
194 let var = parse_player_var(s);
195
196 self.advance();
197
198 Some(ExpressionNode::Variable(Box::new(var)))
199 }
200
201 Some(ExprToken::EnchantVar(s)) => {
202 let var = parse_enchant_var(s);
203
204 self.advance();
205
206 Some(ExpressionNode::Variable(Box::new(var)))
207 }
208
209 Some(ExprToken::MiscVar(s)) => {
210 let var = parse_misc_var(s);
211
212 self.advance();
213
214 Some(ExpressionNode::Variable(Box::new(var)))
215 }
216
217 Some(ExprToken::AtVar(s)) => {
218 let var = parse_at_var(s);
219
220 self.advance();
221
222 Some(ExpressionNode::Variable(Box::new(var)))
223 }
224
225 Some(ExprToken::CrossSpellRef(s)) => {
226 let var = parse_cross_spell_ref(s);
227
228 self.advance();
229
230 Some(ExpressionNode::Variable(Box::new(var)))
231 }
232
233 Some(ExprToken::SimpleVar(s)) => {
235 let var = VariableNode::Misc(MiscVariableNode {
236 var_name: s[1..].to_string(),
237 id: None,
238 });
239
240 self.advance();
241
242 Some(ExpressionNode::Variable(Box::new(var)))
243 }
244
245 _ => None,
246 }
247 }
248
249 fn parse_function_args(&mut self, func_name: String) -> ExpressionNode {
250 if !matches!(self.peek(), Some(ExprToken::LParen)) {
251 return ExpressionNode::FunctionCall(FunctionCallNode {
252 func_name,
253 args: Vec::new(),
254 });
255 }
256
257 self.advance();
258
259 let mut args = Vec::new();
260
261 if !matches!(self.peek(), Some(ExprToken::RParen)) {
262 if let Some(arg) = self.parse_expression() {
263 args.push(arg);
264 }
265
266 while matches!(self.peek(), Some(ExprToken::Comma)) {
267 self.advance();
268
269 if let Some(arg) = self.parse_expression() {
270 args.push(arg);
271 }
272 }
273 }
274
275 if matches!(self.peek(), Some(ExprToken::RParen)) {
276 self.advance();
277 }
278
279 ExpressionNode::FunctionCall(FunctionCallNode { func_name, args })
280 }
281}
282
283pub(super) fn parse_cond_func_call(s: &str) -> SingleConditionNode {
284 let rest = s.strip_prefix('$').unwrap_or(s);
285 let paren_idx = rest.find('(').unwrap_or(rest.len());
286 let func_name = rest[..paren_idx].to_string();
287
288 let args_str = if paren_idx < rest.len() {
289 let inner = &rest[paren_idx + 1..];
290
291 inner.strip_suffix(')').unwrap_or(inner)
292 } else {
293 ""
294 };
295
296 let mut args = Vec::new();
297
298 if !args_str.is_empty() {
299 let args_tokens: Vec<_> = lex_expr(args_str).filter_map(Result::ok).collect();
300 let mut expr_parser = ExprParser::new(&args_tokens);
301
302 if let Some(expr) = expr_parser.parse_expression() {
303 args.push(expr);
304 }
305
306 while matches!(expr_parser.peek(), Some(ExprToken::Comma)) {
307 expr_parser.advance();
308
309 if let Some(expr) = expr_parser.parse_expression() {
310 args.push(expr);
311 }
312 }
313 }
314
315 SingleConditionNode::Expression(ExpressionConditionNode { func_name, args })
316}