Repository: Keats/tera (rust)

Issue:
`if` statement with parens fails to parse
The following fails to parse with tera but works fine with jinja2:

```
{% if (brand.id == "barfoo") or (brand.id == "foobar") %}LOL{% endif %}
```
with the following error:
```
Tera error: Error { kind: Msg("Failed to parse 'test.html'"), source: Some(Error { kind: Msg(" --> 1:17\n  |\n1 | {% if (brand.id == \"barfoo\") or (brand.id == \"foobar\") %}LOL{% endif %}\n  |                 ^---\n  |\n  = expected `+`, `-`, `*`, `/`, or `%`"), source: None }) }
```

Changing the template to something more reasonable also fails with the exact same error: 
```
{% if (brand.id == "barfoo" or brand.id == "foobar") %}LOL{% endif %}
```

I know the first example is a bit silly and you're not aiming for 100% jinja compatibility but do you think this could be fixed? 

Thanks in advance!

Pull request description:
Closes #882
Closes #882

Patch:
diff --git a/docs/content/docs/_index.md b/docs/content/docs/_index.md
index 73fb5b5..7eda240 100644
--- a/docs/content/docs/_index.md
+++ b/docs/content/docs/_index.md
@@ -200,7 +200,8 @@ would be rendered as `Hello {{ name }}`.
 ### Whitespace control
 
 Tera comes with easy to use whitespace control: use `{%-` if you want to remove all whitespace
-before a statement and `-%}` if you want to remove all whitespace after.
+before a statement and `-%}` if you want to remove all whitespace after. This behavior also 
+works with expressions, using `{{-` and `-}}`, and with comments, using `{#-` and `-#}`.
 
 For example, let's look at the following template:
 
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 07982c1..0419cbe 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -427,6 +427,7 @@ fn parse_logic_val(pair: Pair<Rule>) -> TeraResult<Expr> {
             Rule::in_cond => expr = Some(parse_in_condition(p)?),
             Rule::comparison_expr => expr = Some(parse_comparison_expression(p)?),
             Rule::string_expr_filter => expr = Some(parse_string_expr_with_filters(p)?),
+            Rule::logic_expr => expr = Some(parse_logic_expr(p)?),
             _ => unreachable!(),
         };
     }
diff --git a/src/parser/tera.pest b/src/parser/tera.pest
index ec2b172..0c89753 100644
--- a/src/parser/tera.pest
+++ b/src/parser/tera.pest
@@ -98,7 +98,7 @@ comparison_expr = { (string_expr_filter | comparison_val) ~ (comparison_op ~ (st
 in_cond_container = {string_expr_filter | array_filter | dotted_square_bracket_ident}
 in_cond = !{ (string_expr_filter | basic_expr_filter) ~ op_not? ~ "in" ~ in_cond_container }
 
-logic_val  = !{ op_not? ~ (in_cond | comparison_expr) }
+logic_val  = !{ op_not? ~ (in_cond | comparison_expr) | "(" ~ logic_expr ~ ")" }
 logic_expr = !{ logic_val ~ ((op_or | op_and) ~ logic_val)* }
 
 array = !{ "[" ~ (logic_val ~ ",")* ~ logic_val? ~ "]"}


Test patch:
diff --git a/src/parser/tests/parser.rs b/src/parser/tests/parser.rs
index 5a793f6..73db69b 100644
--- a/src/parser/tests/parser.rs
+++ b/src/parser/tests/parser.rs
@@ -360,6 +360,30 @@ fn parse_variable_tag_negated_expr() {
     );
 }
 
+#[test]
+fn parse_variable_tag_negated_expr_with_parentheses() {
+    let ast = parse("{{ (not id or not true) and not 1 + 1 }}").unwrap();
+    assert_eq!(
+        ast[0],
+        Node::VariableBlock(
+            WS::default(),
+            Expr::new(ExprVal::Logic(LogicExpr {
+                lhs: Box::new(Expr::new(ExprVal::Logic(LogicExpr {
+                    lhs: Box::new(Expr::new_negated(ExprVal::Ident("id".to_string()))),
+                    operator: LogicOperator::Or,
+                    rhs: Box::new(Expr::new_negated(ExprVal::Bool(true))),
+                },))),
+                operator: LogicOperator::And,
+                rhs: Box::new(Expr::new_negated(ExprVal::Math(MathExpr {
+                    lhs: Box::new(Expr::new(ExprVal::Int(1))),
+                    operator: MathOperator::Add,
+                    rhs: Box::new(Expr::new(ExprVal::Int(1))),
+                },))),
+            },))
+        )
+    );
+}
+
 #[test]
 fn parse_variable_tag_simple_test() {
     let ast = parse("{{ id is defined }}").unwrap();
diff --git a/src/renderer/tests/basic.rs b/src/renderer/tests/basic.rs
index 1054553..ca621be 100644
--- a/src/renderer/tests/basic.rs
+++ b/src/renderer/tests/basic.rs
@@ -476,6 +476,19 @@ fn render_if_elif_else() {
         ("{% if 'n' in name %}Admin{% else %}Hmm{% endif %}", "Admin"),
         // function in if
         ("{% if get_true() %}Truth{% endif %}", "Truth"),
+        // Parentheses around logic expressions
+        ("{% if age >= 18 and name == 'john' %}Truth{% endif %}", "Truth"),
+        ("{% if (age >= 18) and (name == 'john') %}Truth{% endif %}", "Truth"),
+        ("{% if (age >= 18) or (name == 'john') %}Truth{% endif %}", "Truth"),
+        ("{% if (age < 18) or (name == 'john') %}Truth{% endif %}", "Truth"),
+        ("{% if (age >= 18) or (name != 'john') %}Truth{% endif %}", "Truth"),
+        ("{% if (age < 18) and (name != 'john') %}Truth{% endif %}", ""),
+        ("{% if (age >= 18) and (name != 'john') %}Truth{% endif %}", ""),
+        ("{% if (age >= 18 and name == 'john') %}Truth{% endif %}", "Truth"),
+        ("{% if (age < 18 and name == 'john') %}Truth{% endif %}", ""),
+        ("{% if (age >= 18 and name != 'john') %}Truth{% endif %}", ""),
+        ("{% if age >= 18 or name == 'john' and is_false %}Truth{% endif %}", "Truth"),
+        ("{% if (age >= 18 or name == 'john') and is_false %}Truth{% endif %}", ""),
     ];
 
     for (input, expected) in inputs {