boreal_parser/expression/
boolean_expression.rs

1//! Parsing related to expressions.
2//!
3//! This implements the `expression` element in grammar.y in libyara.
4use nom::branch::alt;
5use nom::bytes::complete::tag;
6use nom::combinator::{cut, map};
7use nom::sequence::preceded;
8use nom::Parser;
9
10use crate::error::{Error, ErrorKind};
11use crate::expression::common::range;
12use crate::expression::for_expression::{
13    for_expression_non_ambiguous, for_expression_with_expr_selection,
14};
15use crate::expression::primary_expression::primary_expression;
16use crate::expression::{Expression, ExpressionKind};
17use crate::nom_recipes::{rtrim, textual_tag as ttag};
18use crate::regex::{regex, Regex};
19use crate::string::string_identifier;
20use crate::types::{Input, ParseResult};
21
22/// parse or operator
23pub(crate) fn boolean_expression(mut input: Input) -> ParseResult<Expression> {
24    // Expression parsing involves multiple recursives paths, making it quite complex.
25    // There are however a few observations we can make:
26    // - only two combinators receive recursive calls: this one and `primary_expression`.
27    // - some paths can loop boolean_expression -> ... -> boolean_expression without going through
28    // primary_expression
29    // - some paths can loop primary_expression -> ... -> boolean_expression
30    // - some paths can loop primary_expression -> ... -> primary_expression
31    //
32    // To prevent stack overflows, both this combinator and `primary_expression` increment the
33    // same recursion counter. This means this counter can be incremented twice on a single
34    // recursive loop, on for example `module.function(true)`
35    let start = input.pos();
36
37    if input.expr_recursion_counter >= super::MAX_EXPR_RECURSION {
38        return Err(nom::Err::Failure(Error::new(
39            input.get_span_from(start),
40            ErrorKind::ExprTooDeep,
41        )));
42    }
43
44    input.expr_recursion_counter += 1;
45    let (mut input, res) = expression_and(input)?;
46
47    match rtrim(ttag("or")).parse(input) {
48        Ok((mut input, _)) => {
49            let mut ops = vec![res];
50            loop {
51                let (mut i2, elem) = cut(expression_and).parse(input)?;
52                ops.push(elem);
53                match rtrim(ttag("or"))(i2) {
54                    Ok((i3, _)) => input = i3,
55                    Err(_) => {
56                        i2.expr_recursion_counter -= 1;
57                        return Ok((
58                            i2,
59                            Expression {
60                                expr: ExpressionKind::Or(ops),
61                                span: i2.get_span_from(start),
62                            },
63                        ));
64                    }
65                }
66            }
67        }
68        Err(_) => {
69            input.expr_recursion_counter -= 1;
70            Ok((input, res))
71        }
72    }
73}
74
75/// parse and operator
76fn expression_and(input: Input) -> ParseResult<Expression> {
77    let start = input.pos();
78    let (input, res) = expression_not(input)?;
79
80    match rtrim(ttag("and")).parse(input) {
81        Ok((mut input, _)) => {
82            let mut ops = vec![res];
83            loop {
84                let (i2, elem) = cut(expression_not).parse(input)?;
85                ops.push(elem);
86                match rtrim(ttag("and"))(i2) {
87                    Ok((i3, _)) => input = i3,
88                    Err(_) => {
89                        return Ok((
90                            i2,
91                            Expression {
92                                expr: ExpressionKind::And(ops),
93                                span: i2.get_span_from(start),
94                            },
95                        ))
96                    }
97                }
98            }
99        }
100        Err(_) => Ok((input, res)),
101    }
102}
103
104/// parse defined & not operator
105fn expression_not(mut input: Input) -> ParseResult<Expression> {
106    let mut start = input.pos();
107    let mut ops = Vec::new();
108    // Push ops into a vec, to prevent a possible stack overflow if we used recursion.
109    while let Ok((i, op)) = rtrim(alt((ttag("not"), ttag("defined")))).parse(input) {
110        ops.push((
111            if op == "not" {
112                ExpressionKind::Not
113            } else {
114                ExpressionKind::Defined
115            },
116            start,
117        ));
118        input = i;
119        start = i.pos();
120    }
121
122    let (input, mut expr) = expression_item(input)?;
123    while let Some((op, start)) = ops.pop() {
124        expr = Expression {
125            expr: op(Box::new(expr)),
126            span: input.get_span_from(start),
127        };
128    }
129
130    Ok((input, expr))
131}
132
133/// parse rest of boolean expressions
134fn expression_item(input: Input) -> ParseResult<Expression> {
135    match alt((
136        // all variants of for expressions with a non ambiguous first token
137        for_expression_non_ambiguous,
138        // string_identifier ...
139        variable_expression,
140    ))
141    .parse(input)
142    {
143        Ok((input, expr)) => return Ok((input, expr)),
144        Err(nom::Err::Failure(e)) => return Err(nom::Err::Failure(e)),
145        Err(_) => (),
146    }
147
148    // primary_expression ...
149    let start = input.pos();
150    let (input, expr) = primary_expression_eq_all(input)?;
151
152    // try to parse it as a for expression with a leading expression
153    // as the first token. If it fails, it will return the given
154    // expression.
155    for_expression_with_expr_selection(expr, start, input)
156}
157
158enum EqExprBuilder {
159    Regex(fn(Expression, Regex) -> ExpressionKind),
160    Expr(fn(Expression, Expression) -> ExpressionKind),
161}
162
163/// parse `==`, `!=`, `(i)contains`, `(i)startswith`, `(i)endswith`,
164/// `iequals`, `matches` operators.
165fn primary_expression_eq_all(input: Input) -> ParseResult<Expression> {
166    let start = input.pos();
167    let (mut input, mut res) = primary_expression_cmp(input)?;
168
169    while let Ok((i, op)) = rtrim(alt((
170        map(tag("=="), |_| {
171            EqExprBuilder::Expr(|a, b| ExpressionKind::Eq(Box::new(a), Box::new(b)))
172        }),
173        map(tag("!="), |_| {
174            EqExprBuilder::Expr(|a, b| ExpressionKind::NotEq(Box::new(a), Box::new(b)))
175        }),
176        map(ttag("contains"), |_| {
177            EqExprBuilder::Expr(|a, b| ExpressionKind::Contains {
178                haystack: Box::new(a),
179                needle: Box::new(b),
180                case_insensitive: false,
181            })
182        }),
183        map(ttag("icontains"), |_| {
184            EqExprBuilder::Expr(|a, b| ExpressionKind::Contains {
185                haystack: Box::new(a),
186                needle: Box::new(b),
187                case_insensitive: true,
188            })
189        }),
190        map(ttag("startswith"), |_| {
191            EqExprBuilder::Expr(|a, b| ExpressionKind::StartsWith {
192                expr: Box::new(a),
193                prefix: Box::new(b),
194                case_insensitive: false,
195            })
196        }),
197        map(ttag("istartswith"), |_| {
198            EqExprBuilder::Expr(|a, b| ExpressionKind::StartsWith {
199                expr: Box::new(a),
200                prefix: Box::new(b),
201                case_insensitive: true,
202            })
203        }),
204        map(ttag("endswith"), |_| {
205            EqExprBuilder::Expr(|a, b| ExpressionKind::EndsWith {
206                expr: Box::new(a),
207                suffix: Box::new(b),
208                case_insensitive: false,
209            })
210        }),
211        map(ttag("iendswith"), |_| {
212            EqExprBuilder::Expr(|a, b| ExpressionKind::EndsWith {
213                expr: Box::new(a),
214                suffix: Box::new(b),
215                case_insensitive: true,
216            })
217        }),
218        map(ttag("iequals"), |_| {
219            EqExprBuilder::Expr(|a, b| ExpressionKind::IEquals(Box::new(a), Box::new(b)))
220        }),
221        map(ttag("matches"), |_| {
222            EqExprBuilder::Regex(|a, b| ExpressionKind::Matches(Box::new(a), b))
223        }),
224    )))
225    .parse(input)
226    {
227        match op {
228            EqExprBuilder::Expr(builder) => {
229                let (i2, new_expr) = cut(primary_expression_cmp).parse(i)?;
230                input = i2;
231                res = Expression {
232                    expr: builder(res, new_expr),
233                    span: input.get_span_from(start),
234                }
235            }
236            EqExprBuilder::Regex(builder) => {
237                let (i2, regex) = cut(regex).parse(i)?;
238                input = i2;
239                res = Expression {
240                    expr: builder(res, regex),
241                    span: input.get_span_from(start),
242                }
243            }
244        }
245    }
246    Ok((input, res))
247}
248
249/// parse `<=`, `>=`, `<`, `>`, operators.
250fn primary_expression_cmp(input: Input) -> ParseResult<Expression> {
251    let start = input.pos();
252    let (mut input, mut res) = primary_expression(input)?;
253
254    while let Ok((i, op)) = rtrim(alt((tag("<="), tag(">="), tag("<"), tag(">")))).parse(input) {
255        let (i2, right_elem) = cut(primary_expression).parse(i)?;
256        input = i2;
257        let op = op.cursor();
258        let less_than = op.bytes().next() == Some(b'<');
259        let can_be_equal = op.len() == 2;
260        res = Expression {
261            expr: ExpressionKind::Cmp {
262                left: Box::new(res),
263                right: Box::new(right_elem),
264                less_than,
265                can_be_equal,
266            },
267            span: input.get_span_from(start),
268        };
269    }
270    Ok((input, res))
271}
272
273/// Parse expressions using variables
274fn variable_expression(input: Input) -> ParseResult<Expression> {
275    let start = input.pos();
276    let (input, variable_name) = string_identifier(input)?;
277
278    // string_identifier 'at' primary_expression
279    if let Ok((input2, expr)) = preceded(rtrim(ttag("at")), primary_expression).parse(input) {
280        Ok((
281            input2,
282            Expression {
283                expr: ExpressionKind::VariableAt {
284                    variable_name,
285                    variable_name_span: input.get_span_from(start),
286                    offset: Box::new(expr),
287                },
288                span: input2.get_span_from(start),
289            },
290        ))
291    // string_identifier 'in' range
292    } else if let Ok((input2, (from, to))) = preceded(rtrim(tag("in")), range).parse(input) {
293        Ok((
294            input2,
295            Expression {
296                expr: ExpressionKind::VariableIn {
297                    variable_name,
298                    variable_name_span: input.get_span_from(start),
299                    from,
300                    to,
301                },
302                span: input2.get_span_from(start),
303            },
304        ))
305    // string_identifier
306    } else {
307        Ok((
308            input,
309            Expression {
310                expr: ExpressionKind::Variable(variable_name),
311                span: input.get_span_from(start),
312            },
313        ))
314    }
315}
316
317#[cfg(test)]
318mod tests {
319    use super::*;
320    use crate::{
321        expression::{Identifier, MAX_EXPR_RECURSION},
322        regex::{self, Literal},
323        test_helpers::{parse, parse_check, parse_err, parse_err_type, test_public_type},
324    };
325    use std::ops::Range;
326
327    #[track_caller]
328    fn test_precedence<F, F2>(
329        higher_op: &str,
330        lower_op: &str,
331        higher_constructor: F,
332        lower_constructor: F2,
333    ) where
334        F: FnOnce(Box<Expression>, Box<Expression>) -> ExpressionKind,
335        F2: FnOnce(Box<Expression>, Box<Expression>) -> ExpressionKind,
336    {
337        let input = format!("0 {lower_op} 1 {higher_op} 2");
338
339        parse(
340            boolean_expression,
341            &input,
342            "",
343            Expression {
344                expr: lower_constructor(
345                    Box::new(Expression {
346                        expr: ExpressionKind::Integer(0),
347                        span: 0..1,
348                    }),
349                    Box::new(Expression {
350                        expr: higher_constructor(
351                            Box::new(Expression {
352                                expr: ExpressionKind::Integer(1),
353                                span: Range {
354                                    start: 3 + lower_op.len(),
355                                    end: 4 + lower_op.len(),
356                                },
357                            }),
358                            Box::new(Expression {
359                                expr: ExpressionKind::Integer(2),
360                                span: Range {
361                                    start: 6 + lower_op.len() + higher_op.len(),
362                                    end: 7 + lower_op.len() + higher_op.len(),
363                                },
364                            }),
365                        ),
366                        span: Range {
367                            start: 3 + lower_op.len(),
368                            end: 7 + lower_op.len() + higher_op.len(),
369                        },
370                    }),
371                ),
372                span: Range {
373                    start: 0,
374                    end: 7 + lower_op.len() + higher_op.len(),
375                },
376            },
377        );
378    }
379
380    #[test]
381    fn test_variable_expression() {
382        parse(
383            variable_expression,
384            "$a at 100 b",
385            "b",
386            Expression {
387                expr: ExpressionKind::VariableAt {
388                    variable_name: "a".to_owned(),
389                    variable_name_span: 0..2,
390                    offset: Box::new(Expression {
391                        expr: ExpressionKind::Integer(100),
392                        span: 6..9,
393                    }),
394                },
395                span: 0..9,
396            },
397        );
398        parse(
399            variable_expression,
400            "$_ in (0.. 50) b",
401            "b",
402            Expression {
403                expr: ExpressionKind::VariableIn {
404                    variable_name: "_".to_owned(),
405                    variable_name_span: 0..2,
406                    from: Box::new(Expression {
407                        expr: ExpressionKind::Integer(0),
408                        span: 7..8,
409                    }),
410                    to: Box::new(Expression {
411                        expr: ExpressionKind::Integer(50),
412                        span: 11..13,
413                    }),
414                },
415                span: 0..14,
416            },
417        );
418        parse(
419            variable_expression,
420            "$ in (-10..-5)",
421            "",
422            Expression {
423                expr: ExpressionKind::VariableIn {
424                    variable_name: String::new(),
425                    variable_name_span: 0..1,
426                    from: Box::new(Expression {
427                        expr: ExpressionKind::Neg(Box::new(Expression {
428                            expr: ExpressionKind::Integer(10),
429                            span: 7..9,
430                        })),
431                        span: 6..9,
432                    }),
433                    to: Box::new(Expression {
434                        expr: ExpressionKind::Neg(Box::new(Expression {
435                            expr: ExpressionKind::Integer(5),
436                            span: 12..13,
437                        })),
438                        span: 11..13,
439                    }),
440                },
441                span: 0..14,
442            },
443        );
444        parse(
445            variable_expression,
446            "$c in (-10..-5",
447            "in (-10..-5",
448            Expression {
449                expr: ExpressionKind::Variable("c".to_owned()),
450                span: 0..2,
451            },
452        );
453
454        parse_err(variable_expression, "");
455        parse_err(variable_expression, "b");
456        parse_err(variable_expression, "50");
457    }
458
459    // Test operators that require bool operands
460    #[test]
461    fn test_bool_operators() {
462        parse(
463            boolean_expression,
464            "true and false b",
465            "b",
466            Expression {
467                expr: ExpressionKind::And(vec![
468                    Expression {
469                        expr: ExpressionKind::Boolean(true),
470                        span: 0..4,
471                    },
472                    Expression {
473                        expr: ExpressionKind::Boolean(false),
474                        span: 9..14,
475                    },
476                ]),
477                span: 0..14,
478            },
479        );
480        parse(
481            boolean_expression,
482            "not true or defined $b",
483            "",
484            Expression {
485                expr: ExpressionKind::Or(vec![
486                    Expression {
487                        expr: ExpressionKind::Not(Box::new(Expression {
488                            expr: ExpressionKind::Boolean(true),
489                            span: 4..8,
490                        })),
491                        span: 0..8,
492                    },
493                    Expression {
494                        expr: ExpressionKind::Defined(Box::new(Expression {
495                            expr: ExpressionKind::Variable("b".to_owned()),
496                            span: 20..22,
497                        })),
498                        span: 12..22,
499                    },
500                ]),
501                span: 0..22,
502            },
503        );
504        parse(
505            boolean_expression,
506            "not not true",
507            "",
508            Expression {
509                expr: ExpressionKind::Not(Box::new(Expression {
510                    expr: ExpressionKind::Not(Box::new(Expression {
511                        expr: ExpressionKind::Boolean(true),
512                        span: 8..12,
513                    })),
514                    span: 4..12,
515                })),
516                span: 0..12,
517            },
518        );
519        parse(
520            boolean_expression,
521            "false or false or true",
522            "",
523            Expression {
524                expr: ExpressionKind::Or(vec![
525                    Expression {
526                        expr: ExpressionKind::Boolean(false),
527                        span: 0..5,
528                    },
529                    Expression {
530                        expr: ExpressionKind::Boolean(false),
531                        span: 9..14,
532                    },
533                    Expression {
534                        expr: ExpressionKind::Boolean(true),
535                        span: 18..22,
536                    },
537                ]),
538                span: 0..22,
539            },
540        );
541        parse(
542            boolean_expression,
543            "false and false and true",
544            "",
545            Expression {
546                expr: ExpressionKind::And(vec![
547                    Expression {
548                        expr: ExpressionKind::Boolean(false),
549                        span: 0..5,
550                    },
551                    Expression {
552                        expr: ExpressionKind::Boolean(false),
553                        span: 10..15,
554                    },
555                    Expression {
556                        expr: ExpressionKind::Boolean(true),
557                        span: 20..24,
558                    },
559                ]),
560                span: 0..24,
561            },
562        );
563
564        parse_err(boolean_expression, "false and /a");
565        parse_err(boolean_expression, "false or /a");
566    }
567
568    #[test]
569    fn test_rest_operators() {
570        #[track_caller]
571        fn test_op<F>(op: &str, constructor: F)
572        where
573            F: FnOnce(Box<Expression>, Box<Expression>) -> ExpressionKind,
574        {
575            let input = format!("\"a\" {op} \"b\" b");
576
577            parse(
578                boolean_expression,
579                &input,
580                "b",
581                Expression {
582                    expr: constructor(
583                        Box::new(Expression {
584                            expr: ExpressionKind::Bytes(b"a".to_vec()),
585                            span: 0..3,
586                        }),
587                        Box::new(Expression {
588                            expr: ExpressionKind::Bytes(b"b".to_vec()),
589                            span: Range {
590                                start: 5 + op.len(),
591                                end: 8 + op.len(),
592                            },
593                        }),
594                    ),
595                    span: Range {
596                        start: 0,
597                        end: 8 + op.len(),
598                    },
599                },
600            );
601        }
602
603        test_op("==", ExpressionKind::Eq);
604        test_op("!=", ExpressionKind::NotEq);
605        test_op("contains", |a, b| ExpressionKind::Contains {
606            haystack: a,
607            needle: b,
608            case_insensitive: false,
609        });
610        test_op("icontains", |a, b| ExpressionKind::Contains {
611            haystack: a,
612            needle: b,
613            case_insensitive: true,
614        });
615        test_op("startswith", |a, b| ExpressionKind::StartsWith {
616            expr: a,
617            prefix: b,
618            case_insensitive: false,
619        });
620        test_op("istartswith", |a, b| ExpressionKind::StartsWith {
621            expr: a,
622            prefix: b,
623            case_insensitive: true,
624        });
625        test_op("endswith", |a, b| ExpressionKind::EndsWith {
626            expr: a,
627            suffix: b,
628            case_insensitive: false,
629        });
630        test_op("iendswith", |a, b| ExpressionKind::EndsWith {
631            expr: a,
632            suffix: b,
633            case_insensitive: true,
634        });
635        test_op("iequals", ExpressionKind::IEquals);
636
637        test_op("<", |a, b| ExpressionKind::Cmp {
638            left: a,
639            right: b,
640            less_than: true,
641            can_be_equal: false,
642        });
643        test_op("<=", |a, b| ExpressionKind::Cmp {
644            left: a,
645            right: b,
646            less_than: true,
647            can_be_equal: true,
648        });
649        test_op(">", |a, b| ExpressionKind::Cmp {
650            left: a,
651            right: b,
652            less_than: false,
653            can_be_equal: false,
654        });
655        test_op(">=", |a, b| ExpressionKind::Cmp {
656            left: a,
657            right: b,
658            less_than: false,
659            can_be_equal: true,
660        });
661    }
662
663    #[test]
664    fn test_matches() {
665        parse(
666            boolean_expression,
667            "\"a\" matches /b/i b",
668            "b",
669            Expression {
670                expr: ExpressionKind::Matches(
671                    Box::new(Expression {
672                        expr: ExpressionKind::Bytes(b"a".to_vec()),
673                        span: 0..3,
674                    }),
675                    Regex {
676                        ast: regex::Node::Literal(Literal {
677                            byte: b'b',
678                            span: 13..14,
679                            escaped: false,
680                        }),
681                        case_insensitive: true,
682                        dot_all: false,
683                        span: 12..16,
684                    },
685                ),
686                span: 0..16,
687            },
688        );
689
690        parse_err(boolean_expression, "\"a\" matches");
691        parse_err(boolean_expression, "\"a\" matches 1");
692    }
693
694    #[test]
695    fn test_expression_precedence_cmp_eq() {
696        let build_cmp = |less_than, can_be_equal| {
697            move |a, b| ExpressionKind::Cmp {
698                left: a,
699                right: b,
700                less_than,
701                can_be_equal,
702            }
703        };
704
705        // Test precedence of <, <=, >=, > over eq, etc
706        test_precedence("<", "==", build_cmp(true, false), ExpressionKind::Eq);
707        test_precedence("<=", "==", build_cmp(true, true), ExpressionKind::Eq);
708        test_precedence(">", "==", build_cmp(false, false), ExpressionKind::Eq);
709        test_precedence(">=", "==", build_cmp(false, true), ExpressionKind::Eq);
710        test_precedence("<", "!=", build_cmp(true, false), ExpressionKind::NotEq);
711        test_precedence("<", "contains", build_cmp(true, false), |a, b| {
712            ExpressionKind::Contains {
713                haystack: a,
714                needle: b,
715                case_insensitive: false,
716            }
717        });
718        test_precedence("<", "icontains", build_cmp(true, false), |a, b| {
719            ExpressionKind::Contains {
720                haystack: a,
721                needle: b,
722                case_insensitive: true,
723            }
724        });
725        test_precedence("<", "startswith", build_cmp(true, false), |a, b| {
726            ExpressionKind::StartsWith {
727                expr: a,
728                prefix: b,
729                case_insensitive: false,
730            }
731        });
732        test_precedence("<", "istartswith", build_cmp(true, false), |a, b| {
733            ExpressionKind::StartsWith {
734                expr: a,
735                prefix: b,
736                case_insensitive: true,
737            }
738        });
739        test_precedence("<", "endswith", build_cmp(true, false), |a, b| {
740            ExpressionKind::EndsWith {
741                expr: a,
742                suffix: b,
743                case_insensitive: false,
744            }
745        });
746        test_precedence("<", "iendswith", build_cmp(true, false), |a, b| {
747            ExpressionKind::EndsWith {
748                expr: a,
749                suffix: b,
750                case_insensitive: true,
751            }
752        });
753        test_precedence(
754            "<",
755            "iequals",
756            build_cmp(true, false),
757            ExpressionKind::IEquals,
758        );
759    }
760
761    #[test]
762    fn test_expression_precedence_eq_and_or() {
763        // Test precedence of and over or
764        parse(
765            boolean_expression,
766            "not true or false and true",
767            "",
768            Expression {
769                expr: ExpressionKind::Or(vec![
770                    Expression {
771                        expr: ExpressionKind::Not(Box::new(Expression {
772                            expr: ExpressionKind::Boolean(true),
773                            span: 4..8,
774                        })),
775                        span: 0..8,
776                    },
777                    Expression {
778                        expr: ExpressionKind::And(vec![
779                            Expression {
780                                expr: ExpressionKind::Boolean(false),
781                                span: 12..17,
782                            },
783                            Expression {
784                                expr: ExpressionKind::Boolean(true),
785                                span: 22..26,
786                            },
787                        ]),
788                        span: 12..26,
789                    },
790                ]),
791                span: 0..26,
792            },
793        );
794
795        // Test precedence of over eq, etc over and
796        test_precedence("==", "and", ExpressionKind::Eq, |a, b| {
797            ExpressionKind::And(vec![*a, *b])
798        });
799        test_precedence("!=", "and", ExpressionKind::NotEq, |a, b| {
800            ExpressionKind::And(vec![*a, *b])
801        });
802    }
803
804    #[test]
805    fn test_expression() {
806        parse(
807            boolean_expression,
808            "true b",
809            "b",
810            Expression {
811                expr: ExpressionKind::Boolean(true),
812                span: 0..4,
813            },
814        );
815        parse(
816            boolean_expression,
817            "((false))",
818            "",
819            Expression {
820                expr: ExpressionKind::Boolean(false),
821                span: 2..7,
822            },
823        );
824        parse(
825            boolean_expression,
826            "not true b",
827            "b",
828            Expression {
829                expr: ExpressionKind::Not(Box::new(Expression {
830                    expr: ExpressionKind::Boolean(true),
831                    span: 4..8,
832                })),
833                span: 0..8,
834            },
835        );
836        parse(
837            boolean_expression,
838            "not defined $a  c",
839            "c",
840            Expression {
841                expr: ExpressionKind::Not(Box::new(Expression {
842                    expr: ExpressionKind::Defined(Box::new(Expression {
843                        expr: ExpressionKind::Variable("a".to_owned()),
844                        span: 12..14,
845                    })),
846                    span: 4..14,
847                })),
848                span: 0..14,
849            },
850        );
851        parse(
852            boolean_expression,
853            "defined not $a  c",
854            "c",
855            Expression {
856                expr: ExpressionKind::Defined(Box::new(Expression {
857                    expr: ExpressionKind::Not(Box::new(Expression {
858                        expr: ExpressionKind::Variable("a".to_owned()),
859                        span: 12..14,
860                    })),
861                    span: 8..14,
862                })),
863                span: 0..14,
864            },
865        );
866
867        // primary expression is also an expression
868        parse(
869            boolean_expression,
870            "5 b",
871            "b",
872            Expression {
873                expr: ExpressionKind::Integer(5),
874                span: 0..1,
875            },
876        );
877
878        parse_err(boolean_expression, " ");
879        parse_err(boolean_expression, "(");
880        parse_err(boolean_expression, "()");
881        parse_err(boolean_expression, "not");
882        parse_err(boolean_expression, "defined");
883        parse_err(boolean_expression, "1 == ");
884        parse_err(boolean_expression, "1 <= ");
885        parse_err(boolean_expression, "1 | ");
886        parse_err(boolean_expression, "1 & ");
887        parse_err(boolean_expression, "1 ^ ");
888        parse_err(boolean_expression, "1 << ");
889        parse_err(boolean_expression, "1 + ");
890        parse_err(boolean_expression, "1 * ");
891        parse_err(boolean_expression, "1 + -");
892    }
893
894    #[test]
895    fn test_textual_tag() {
896        // Not parsed as "1 or a", but as "1" with trailing "ora", which
897        // makes the parsing of ( expr ) fail.
898        parse_err(boolean_expression, "(1ora)");
899        parse_err(boolean_expression, "(1anda)");
900        parse_check(boolean_expression, "nota", |e| {
901            assert_eq!(
902                e.expr,
903                ExpressionKind::Identifier(Identifier {
904                    name: "nota".to_owned(),
905                    name_span: 0..4,
906                    operations: vec![]
907                }),
908            );
909        });
910        parse_check(boolean_expression, "defineda", |e| {
911            assert_eq!(
912                e.expr,
913                ExpressionKind::Identifier(Identifier {
914                    name: "defineda".to_owned(),
915                    name_span: 0..8,
916                    operations: vec![]
917                }),
918            );
919        });
920        parse_check(boolean_expression, "truea", |e| {
921            assert_eq!(
922                e.expr,
923                ExpressionKind::Identifier(Identifier {
924                    name: "truea".to_owned(),
925                    name_span: 0..5,
926                    operations: vec![]
927                }),
928            );
929        });
930        parse_check(boolean_expression, "falsea", |e| {
931            assert_eq!(
932                e.expr,
933                ExpressionKind::Identifier(Identifier {
934                    name: "falsea".to_owned(),
935                    name_span: 0..6,
936                    operations: vec![]
937                }),
938            );
939        });
940
941        parse_err(boolean_expression, "(a containsb)");
942        parse_err(boolean_expression, "(a icontainsb)");
943        parse_err(boolean_expression, "(a startswitha)");
944        parse_err(boolean_expression, "(a istartswitha)");
945        parse_err(boolean_expression, "(a endswitha)");
946        parse_err(boolean_expression, "(a iendswitha)");
947        parse_err(boolean_expression, "(a iequalsa)");
948
949        parse_err(boolean_expression, "($a atb)");
950
951        // However, == and != do not use textual tags:
952        parse(
953            boolean_expression,
954            "0==0",
955            "",
956            Expression {
957                expr: ExpressionKind::Eq(
958                    Box::new(Expression {
959                        expr: ExpressionKind::Integer(0),
960                        span: 0..1,
961                    }),
962                    Box::new(Expression {
963                        expr: ExpressionKind::Integer(0),
964                        span: 3..4,
965                    }),
966                ),
967                span: 0..4,
968            },
969        );
970        parse(
971            boolean_expression,
972            "1!=2",
973            "",
974            Expression {
975                expr: ExpressionKind::NotEq(
976                    Box::new(Expression {
977                        expr: ExpressionKind::Integer(1),
978                        span: 0..1,
979                    }),
980                    Box::new(Expression {
981                        expr: ExpressionKind::Integer(2),
982                        span: 3..4,
983                    }),
984                ),
985                span: 0..4,
986            },
987        );
988    }
989
990    #[test]
991    fn test_stack_overflow_1() {
992        // boolean_expression -> for_expression_full -> boolean_expression is a recursion loop,
993        // test it.
994        let mut v = String::new();
995        for _ in 0..100_000 {
996            v.push_str("for any of them : ( ");
997        }
998        v.push_str("true");
999        for _ in 0..100_000 {
1000            v.push_str(" ) ");
1001        }
1002
1003        parse_err_type(
1004            boolean_expression,
1005            &v,
1006            &Error::new(400..400, ErrorKind::ExprTooDeep),
1007        );
1008
1009        // counter should reset, so many imbricated groups, but all below the limit should be fine.
1010        let mut v = String::new();
1011        let nb = MAX_EXPR_RECURSION - 2;
1012        for _ in 0..nb {
1013            v.push_str("for any of them : ( ");
1014        }
1015        v.push_str("true");
1016        for _ in 0..nb {
1017            v.push_str(" ) ");
1018        }
1019
1020        v.push_str(" and ");
1021
1022        for _ in 0..nb {
1023            v.push_str("for any of them : ( ");
1024        }
1025        v.push_str("true");
1026        for _ in 0..nb {
1027            v.push_str(" ) ");
1028        }
1029
1030        let input = Input::new(&v);
1031        let _res = boolean_expression(input).unwrap();
1032        assert_eq!(input.expr_recursion_counter, 0);
1033    }
1034
1035    #[test]
1036    fn test_stack_overflow_2() {
1037        // boolean_expression -> primary_expression -> function_call -> boolean_expression
1038        // is a recursion loop, test it.
1039        let mut v = String::new();
1040        for _ in 0..100_000 {
1041            v.push_str("a.b(");
1042        }
1043        v.push_str("true");
1044        for _ in 0..100_000 {
1045            v.push(')');
1046        }
1047
1048        parse_err_type(
1049            boolean_expression,
1050            &v,
1051            &Error::new(40..40, ErrorKind::ExprTooDeep),
1052        );
1053
1054        // counter should reset, so many imbricated groups, but all below the limit should be fine.
1055        let mut v = String::new();
1056        // Since this goes into both boolean_expression and primary_expression, the counter is
1057        // incremented twice per recursion.
1058        let nb = MAX_EXPR_RECURSION / 2 - 1;
1059        for _ in 0..nb {
1060            v.push_str("a.b(");
1061        }
1062        v.push_str("true");
1063        for _ in 0..nb {
1064            v.push(')');
1065        }
1066
1067        let input = Input::new(&v);
1068        let _res = boolean_expression(input).unwrap();
1069        assert_eq!(input.expr_recursion_counter, 0);
1070    }
1071
1072    #[test]
1073    fn test_public_types() {
1074        test_public_type(boolean_expression(Input::new("a == 2")).unwrap());
1075    }
1076}