wowlab_parsers/parsers/spell_desc/parser/
segment.rs1use super::{
4 super::{
5 lexer::{Token, lex_expr},
6 types::{
7 ColorCodeNode, ConditionalBranchNode, ConditionalNode, ConditionalPredicateNode,
8 ExpressionBlockNode, MiscVariableNode, SingleConditionNode, SpellDescriptionNode,
9 TextNode, VariableNode,
10 },
11 },
12 ParseError,
13 expr::{ExprParser, parse_cond_func_call},
14 variables::{
15 parse_at_var, parse_condition_type, parse_cross_spell_ref, parse_custom_var,
16 parse_effect_var, parse_enchant_var, parse_gender, parse_misc_var, parse_player_var,
17 parse_pluralization, parse_spell_level_var,
18 },
19};
20use crate::parsers::TokenStream;
21
22pub(super) struct Parser<'a> {
23 stream: TokenStream<'a, Token<'a>>,
24 pub(super) errors: Vec<ParseError>,
25}
26
27impl<'a> Parser<'a> {
28 pub(super) fn new(tokens: &'a [Token<'a>]) -> Self {
29 Self {
30 stream: TokenStream::new(tokens),
31 errors: Vec::new(),
32 }
33 }
34
35 pub(super) fn parse_description(&mut self) -> Vec<SpellDescriptionNode> {
36 let mut nodes = Vec::new();
37
38 while !self.stream.is_empty() {
39 if let Some(node) = self.parse_segment() {
40 nodes.push(node);
41 }
42 }
43
44 nodes
45 }
46
47 fn peek(&self) -> Option<&Token<'a>> {
48 self.stream.peek()
49 }
50
51 fn advance(&mut self) -> Option<&Token<'a>> {
52 self.stream.advance()
53 }
54
55 fn parse_segment(&mut self) -> Option<SpellDescriptionNode> {
57 let token = self.peek()?.clone();
58
59 self.advance();
60
61 match token {
62 Token::Text(s) => Some(SpellDescriptionNode::Text(TextNode::new(s.to_string()))),
63
64 Token::ExpressionBlock(content) => self.parse_expression_block(content),
65
66 Token::CustomVariable(s) => Some(SpellDescriptionNode::Variable(parse_custom_var(s))),
67
68 Token::AtVariable(s) => Some(SpellDescriptionNode::Variable(parse_at_var(s))),
69
70 Token::EffectVariable(s) => Some(SpellDescriptionNode::Variable(parse_effect_var(s))),
71
72 Token::SpellLevelVariable(s) => {
73 Some(SpellDescriptionNode::Variable(parse_spell_level_var(s)))
74 }
75
76 Token::PlayerVariable(s) => Some(SpellDescriptionNode::Variable(parse_player_var(s))),
77
78 Token::EnchantVariable(s) => Some(SpellDescriptionNode::Variable(parse_enchant_var(s))),
79
80 Token::MiscVariable(s) => Some(SpellDescriptionNode::Variable(parse_misc_var(s))),
81
82 Token::CrossSpellRef(s) => {
83 Some(SpellDescriptionNode::Variable(parse_cross_spell_ref(s)))
84 }
85
86 Token::SimpleVariable(s) => {
87 Some(SpellDescriptionNode::Variable(VariableNode::Misc(
89 MiscVariableNode {
90 var_name: s[1..].to_string(),
91 id: None,
92 },
93 )))
94 }
95
96 Token::ConditionalStart => Some(self.parse_conditional()),
97
98 Token::Pluralization((content, capitalized)) => Some(
99 SpellDescriptionNode::Pluralization(parse_pluralization(content, capitalized)),
100 ),
101
102 Token::Gender((content, capitalized)) => Some(SpellDescriptionNode::Gender(
103 parse_gender(content, capitalized),
104 )),
105
106 Token::ColorCode(s) => Some(SpellDescriptionNode::ColorCode(ColorCodeNode::new(
108 s[1..].to_string(),
109 ))),
110
111 Token::Dollar => Some(SpellDescriptionNode::Text(TextNode::new("$".to_string()))),
112
113 Token::Pipe => Some(SpellDescriptionNode::Text(TextNode::new("|".to_string()))),
114
115 Token::LBracket => Some(SpellDescriptionNode::Text(TextNode::new("[".to_string()))),
116
117 Token::RBracket => Some(SpellDescriptionNode::Text(TextNode::new("]".to_string()))),
118
119 Token::Question => Some(SpellDescriptionNode::Text(TextNode::new("?".to_string()))),
120
121 Token::CondFuncCall(s) => {
122 Some(SpellDescriptionNode::Text(TextNode::new(s.to_string())))
123 }
124 }
125 }
126
127 fn parse_expression_block(&mut self, content: &str) -> Option<SpellDescriptionNode> {
129 let (expr_content, decimal_places) = if let Some(brace_idx) = content.find('}') {
130 let expr = &content[..brace_idx];
131 let after = &content[brace_idx + 1..];
132
133 let decimals = if let Some(after_dot) = after.strip_prefix('.') {
134 after_dot
135 .chars()
136 .take_while(char::is_ascii_digit)
137 .collect::<String>()
138 .parse::<u8>()
139 .ok()
140 } else {
141 None
142 };
143
144 (expr, decimals)
145 } else {
146 (content, None)
147 };
148
149 let expr_tokens: Vec<_> = lex_expr(expr_content).filter_map(Result::ok).collect();
150 let mut expr_parser = ExprParser::new(&expr_tokens);
151 let (expression, inline_decimals) = expr_parser.parse();
152
153 self.errors.extend(expr_parser.errors);
154
155 let final_decimals = decimal_places.or(inline_decimals);
156
157 expression.map(|expr| {
158 SpellDescriptionNode::ExpressionBlock(ExpressionBlockNode {
159 expression: expr,
160 decimal_places: final_decimals,
161 })
162 })
163 }
164
165 fn parse_conditional(&mut self) -> SpellDescriptionNode {
166 let mut conditions = Vec::new();
167 let mut else_branch = None;
168
169 let predicate = self.parse_condition_predicate();
170
171 let content = self.parse_branch_content();
172
173 conditions.push(ConditionalBranchNode { predicate, content });
174
175 loop {
176 match self.peek() {
177 Some(Token::Question) => {
178 self.advance();
179 let pred = self.parse_condition_predicate();
180 let cont = self.parse_branch_content();
181
182 conditions.push(ConditionalBranchNode {
183 predicate: pred,
184 content: cont,
185 });
186 }
187 Some(Token::LBracket) => {
188 else_branch = Some(self.parse_branch_content());
189 break;
190 }
191 _ => break,
192 }
193 }
194
195 SpellDescriptionNode::Conditional(ConditionalNode {
196 conditions,
197 else_branch,
198 })
199 }
200
201 fn parse_condition_predicate(&mut self) -> ConditionalPredicateNode {
202 let mut conditions = Vec::new();
203
204 if let Some(cond) = self.parse_single_condition() {
205 conditions.push(cond);
206 }
207
208 while matches!(self.peek(), Some(Token::Pipe)) {
209 self.advance();
210
211 if let Some(cond) = self.parse_single_condition() {
212 conditions.push(cond);
213 }
214 }
215
216 ConditionalPredicateNode { conditions }
217 }
218
219 fn parse_single_condition(&mut self) -> Option<SingleConditionNode> {
220 match self.peek() {
221 Some(Token::CondFuncCall(s)) => {
222 let s = *s;
223
224 self.advance();
225
226 Some(parse_cond_func_call(s))
227 }
228 Some(Token::Text(s) | Token::SimpleVariable(s)) => {
229 let s = *s;
230
231 self.advance();
232
233 parse_condition_type(s)
234 }
235 _ => None,
236 }
237 }
238
239 fn parse_branch_content(&mut self) -> Vec<SpellDescriptionNode> {
240 let mut nodes = Vec::new();
241
242 if !matches!(self.peek(), Some(Token::LBracket)) {
243 return nodes;
244 }
245
246 self.advance();
247
248 let mut depth = 1;
249
250 while depth > 0 && !self.stream.is_empty() {
251 match self.peek() {
252 Some(Token::LBracket) => {
253 depth += 1;
254 self.advance();
255 nodes.push(SpellDescriptionNode::Text(TextNode::new("[".to_string())));
256 }
257 Some(Token::RBracket) => {
258 depth -= 1;
259 self.advance();
260
261 if depth > 0 {
262 nodes.push(SpellDescriptionNode::Text(TextNode::new("]".to_string())));
263 }
264 }
265 Some(Token::ConditionalStart) => {
266 self.advance();
267 nodes.push(self.parse_conditional());
268 }
269 _ => {
270 if let Some(node) = self.parse_segment() {
271 nodes.push(node);
272 }
273 }
274 }
275 }
276
277 nodes
278 }
279}