ferroui_base/rendering/composition/expressions/
expression_parser.rs1use super::expression::{
4 BinaryExpression, ConditionalExpression, ConstantExpression, Expression, ExpressionKeyword, ExpressionKeywords,
5 ExpressionType, FunctionCallExpression, KeywordExpression, MemberAccessExpression, ParameterExpression,
6 UnaryExpression,
7};
8use super::expression_parse_exception::ExpressionParseException;
9use super::token_parser::TokenParser;
10
11pub struct ExpressionParser;
13
14impl ExpressionParser {
15 pub fn parse(s: &str) -> Result<Expression, ExpressionParseException> {
17 let mut p = TokenParser::new(s);
18 let (parsed, _) = parse_till_terminator(&mut p, "", false, false)?;
19 p.skip_whitespace();
20 if p.length() != 0 {
21 return Err(ExpressionParseException::new("Unexpected data ", p.position()));
22 }
23 Ok(parsed)
24 }
25}
26
27fn try_parse_atomic(parser: &mut TokenParser<'_>) -> Option<Expression> {
28 let expr: Option<Expression> = if parser.try_parse_keyword_lower_case(ExpressionKeywords::STARTING_VALUE) {
30 Some(KeywordExpression::new(ExpressionKeyword::StartingValue).into())
31 } else if parser.try_parse_keyword_lower_case(ExpressionKeywords::CURRENT_VALUE) {
32 Some(KeywordExpression::new(ExpressionKeyword::CurrentValue).into())
33 } else if parser.try_parse_keyword_lower_case(ExpressionKeywords::FINAL_VALUE) {
34 Some(KeywordExpression::new(ExpressionKeyword::FinalValue).into())
35 } else if parser.try_parse_keyword_lower_case(ExpressionKeywords::PI) {
36 Some(KeywordExpression::new(ExpressionKeyword::Pi).into())
37 } else if parser.try_parse_keyword_lower_case(ExpressionKeywords::TRUE) {
38 Some(KeywordExpression::new(ExpressionKeyword::True).into())
39 } else if parser.try_parse_keyword_lower_case(ExpressionKeywords::FALSE) {
40 Some(KeywordExpression::new(ExpressionKeyword::False).into())
41 } else if parser.try_parse_keyword_lower_case(ExpressionKeywords::TARGET) {
42 Some(KeywordExpression::new(ExpressionKeyword::Target).into())
43 } else if parser.try_parse_keyword_lower_case("relativeunit.relative") {
44 Some(FunctionCallExpression::new("RelativeUnit.Relative", Vec::new()).into())
45 } else if parser.try_parse_keyword_lower_case("relativeunit.absolute") {
46 Some(FunctionCallExpression::new("RelativeUnit.Absolute", Vec::new()).into())
47 } else {
48 None
49 };
50 if expr.is_some() {
51 return expr;
52 }
53
54 if let Some(identifier) = parser.try_parse_identifier() {
55 return Some(ParameterExpression::new(identifier).into());
56 }
57
58 if let Some(scalar) = parser.try_parse_float() {
59 return Some(ConstantExpression::new(scalar).into());
60 }
61
62 None
63}
64
65fn try_parse_operator(parser: &mut TokenParser<'_>) -> Option<ExpressionType> {
66 if parser.try_consume_str("||") {
67 Some(ExpressionType::LogicalOr)
68 } else if parser.try_consume_str("&&") {
69 Some(ExpressionType::LogicalAnd)
70 } else if parser.try_consume_str(">=") {
71 Some(ExpressionType::MoreThanOrEqual)
72 } else if parser.try_consume_str("<=") {
73 Some(ExpressionType::LessThanOrEqual)
74 } else if parser.try_consume_str("==") {
75 Some(ExpressionType::Equals)
76 } else if parser.try_consume_str("!=") {
77 Some(ExpressionType::NotEquals)
78 } else {
79 Some(match parser.try_consume_any("+-/*><%")? {
80 '+' => ExpressionType::Add,
81 '-' => ExpressionType::Subtract,
82 '/' => ExpressionType::Divide,
83 '*' => ExpressionType::Multiply,
84 '<' => ExpressionType::LessThan,
85 '>' => ExpressionType::MoreThan,
86 _ => ExpressionType::Remainder,
87 })
88 }
89}
90
91#[derive(Default)]
94struct ExpressionOperatorGroup {
95 expressions: Option<Vec<Option<Expression>>>,
96 operators: Vec<ExpressionType>,
97 first: Option<Expression>,
98}
99
100const OPERATOR_PRECEDENCE_GROUPS_REVERSED: [&[ExpressionType]; 6] = [
103 &[ExpressionType::LogicalOr],
105 &[ExpressionType::LogicalAnd],
107 &[ExpressionType::Equals, ExpressionType::NotEquals],
109 &[
111 ExpressionType::MoreThan,
112 ExpressionType::MoreThanOrEqual,
113 ExpressionType::LessThan,
114 ExpressionType::LessThanOrEqual,
115 ],
116 &[ExpressionType::Add, ExpressionType::Subtract],
118 &[ExpressionType::Multiply, ExpressionType::Divide, ExpressionType::Remainder],
120];
121
122impl ExpressionOperatorGroup {
123 fn not_empty(&self) -> bool {
124 !self.empty()
125 }
126
127 fn empty(&self) -> bool {
128 self.expressions.is_none() && self.first.is_none()
129 }
130
131 fn append_first(&mut self, expr: Expression) {
132 assert!(!self.not_empty(), "the operator group already has an expression");
133 self.first = Some(expr);
134 }
135
136 fn append_with_operator(&mut self, expr: Expression, op: ExpressionType) {
137 if self.expressions.is_none() {
138 let first = self.first.take().expect("the operator group has no first expression");
139 self.expressions = Some(vec![Some(first)]);
140 self.operators = Vec::new();
141 }
142 if let Some(expressions) = &mut self.expressions {
143 expressions.push(Some(expr));
144 }
145 self.operators.push(op);
146 }
147
148 fn range_to_expression(
157 expressions: &mut [Option<Expression>],
158 operators: &[ExpressionType],
159 from: usize,
160 to: usize,
161 ) -> Result<Expression, ExpressionParseException> {
162 let mut take = |index: usize| -> Result<Expression, ExpressionParseException> {
163 expressions[index]
164 .take()
165 .ok_or_else(|| ExpressionParseException::new("Expression parsing algorithm bug in ToExpression", 0))
166 };
167
168 if to - from == 0 {
169 return take(from);
170 }
171
172 if to - from == 1 {
173 let left = take(from)?;
174 let right = take(to)?;
175 return Ok(BinaryExpression::new(left, right, operators[from]).into());
176 }
177
178 for grp in OPERATOR_PRECEDENCE_GROUPS_REVERSED {
179 for c in from..to {
180 let current_operator = operators[c];
181 for &operator_from_group in grp {
182 if current_operator == operator_from_group {
183 let left = Self::range_to_expression(expressions, operators, from, c)?;
185 let right = Self::range_to_expression(expressions, operators, c + 1, to)?;
186 return Ok(BinaryExpression::new(left, right, current_operator).into());
187 }
188 }
189 }
190 }
191
192 Err(ExpressionParseException::new(
194 "Expression parsing algorithm bug in ToExpression",
195 0,
196 ))
197 }
198
199 fn into_expression(mut self) -> Result<Expression, ExpressionParseException> {
200 match &mut self.expressions {
201 None => Ok(self.first.take().expect("the operator group is empty")),
202 Some(expressions) => {
203 let to = expressions.len() - 1;
204 Self::range_to_expression(expressions, &self.operators, 0, to)
205 }
206 }
207 }
208}
209
210fn parse_till_terminator(
213 parser: &mut TokenParser<'_>,
214 terminator_chars: &str,
215 throw_on_terminator: bool,
216 throw_on_end: bool,
217) -> Result<(Expression, Option<char>), ExpressionParseException> {
218 let mut left = ExpressionOperatorGroup::default();
219 loop {
220 if let Some(consumed_token) = parser.try_consume_any(terminator_chars) {
221 if throw_on_terminator || left.empty() {
222 return Err(ExpressionParseException::new("Unexpected ''", parser.position() - 1));
225 }
226 return Ok((left.into_expression()?, Some(consumed_token)));
227 }
228 parser.skip_whitespace();
229 if parser.length() == 0 {
230 if throw_on_end || left.empty() {
231 return Err(ExpressionParseException::new(
232 "Unexpected end of expression",
233 parser.position(),
234 ));
235 }
236 return Ok((left.into_expression()?, None));
237 }
238
239 let mut op: Option<ExpressionType> = None;
240 if left.not_empty() {
241 if parser.try_consume('?') {
242 let (true_part, _) = parse_till_terminator(parser, ":", false, true)?;
243 let (false_part, token) =
245 parse_till_terminator(parser, terminator_chars, throw_on_terminator, throw_on_end)?;
246
247 return Ok((
248 ConditionalExpression::new(left.into_expression()?, true_part, false_part).into(),
249 token,
250 ));
251 }
252
253 match try_parse_operator(parser) {
255 Some(sop) => op = Some(sop),
256 None => return Err(ExpressionParseException::new("Unexpected token", parser.position())),
257 }
258 }
259
260 let mut apply_negation = false;
262 while parser.try_consume('!') {
263 apply_negation = !apply_negation;
264 }
265
266 let mut apply_unary_minus = false;
267 while parser.try_consume('-') {
268 apply_unary_minus = !apply_unary_minus;
269 }
270
271 let mut parsed: Expression;
272
273 if parser.try_consume('(') {
274 parsed = parse_till_terminator(parser, ")", false, true)?.0;
275 } else if let Some(function_name) = parser.try_parse_call() {
276 let mut parameter_list = Vec::new();
277 while !parser.try_consume(')') {
278 let (parameter, closing_token) = parse_till_terminator(parser, ",)", false, true)?;
279 parameter_list.push(parameter);
280 if closing_token == Some(')') {
281 break;
282 }
283 if closing_token != Some(',') {
284 return Err(ExpressionParseException::new(
285 "Unexpected end of the expression",
286 parser.position(),
287 ));
288 }
289 }
290
291 parsed = FunctionCallExpression::new(function_name, parameter_list).into();
292 } else if let Some(atomic) = try_parse_atomic(parser) {
293 parsed = atomic;
294 } else {
295 return Err(ExpressionParseException::new("Unexpected token", parser.position()));
296 }
297
298 while parser.try_consume('.') {
300 match parser.try_parse_identifier() {
301 Some(member_name) => parsed = MemberAccessExpression::new(parsed, member_name).into(),
302 None => return Err(ExpressionParseException::new("Unexpected token", parser.position())),
303 }
304 }
305
306 if apply_negation {
308 parsed = UnaryExpression::new(parsed, ExpressionType::Not).into();
309 }
310
311 if apply_unary_minus {
312 parsed = match parsed {
313 Expression::Constant(constexpr) => ConstantExpression::new(-constexpr.constant).into(),
314 other => UnaryExpression::new(other, ExpressionType::UnaryMinus).into(),
315 };
316 }
317
318 match op {
319 None => left.append_first(parsed),
320 Some(op) => left.append_with_operator(parsed, op),
321 }
322 }
323}