From 48cd05134bca8e46ad01c5bf75a1d41a13f995c2 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 31 Oct 2024 13:46:52 +0100 Subject: [PATCH] Fix a bug with parenless 'is' tests --- minijinja/src/compiler/lexer.rs | 1 - minijinja/src/compiler/parser.rs | 23 +++++++++++++++++++ minijinja/src/compiler/tokens.rs | 3 --- minijinja/tests/inputs/tests.txt | 9 +++++++- minijinja/tests/lexer-inputs/operators.txt | 2 +- .../test_lexer__lexer@operators.txt.snap | 5 +--- .../test_templates__vm@tests.txt.snap | 9 +++++++- 7 files changed, 41 insertions(+), 11 deletions(-) diff --git a/minijinja/src/compiler/lexer.rs b/minijinja/src/compiler/lexer.rs index 2299cb9a..a06f64f4 100644 --- a/minijinja/src/compiler/lexer.rs +++ b/minijinja/src/compiler/lexer.rs @@ -854,7 +854,6 @@ impl<'s> Tokenizer<'s> { Some(b'*') => Some(Token::Mul), Some(b'/') => Some(Token::Div), Some(b'%') => Some(Token::Mod), - Some(b'!') => Some(Token::Bang), Some(b'.') => Some(Token::Dot), Some(b',') => Some(Token::Comma), Some(b':') => Some(Token::Colon), diff --git a/minijinja/src/compiler/parser.rs b/minijinja/src/compiler/parser.rs index c498bbea..10a24462 100644 --- a/minijinja/src/compiler/parser.rs +++ b/minijinja/src/compiler/parser.rs @@ -460,6 +460,29 @@ impl<'a> Parser<'a> { expect_token!(self, Token::Ident(name) => name, "identifier"); let args = if matches_token!(self, Token::ParenOpen) { ok!(self.parse_args()) + } else if matches_token!( + self, + Token::Ident(_) + | Token::Str(_) + | Token::String(_) + | Token::Int(_) + | Token::Int128(_) + | Token::Float(_) + | Token::Plus + | Token::Minus + | Token::BracketOpen + | Token::BraceOpen + ) && !matches_token!( + self, + Token::Ident("and") + | Token::Ident("or") + | Token::Ident("else") + | Token::Ident("is") + ) { + let span = self.stream.current_span(); + let mut expr = ok!(self.parse_unary_only()); + expr = ok!(self.parse_postfix(expr, span)); + vec![expr] } else { Vec::new() }; diff --git a/minijinja/src/compiler/tokens.rs b/minijinja/src/compiler/tokens.rs index a11033d9..1dc08ac7 100644 --- a/minijinja/src/compiler/tokens.rs +++ b/minijinja/src/compiler/tokens.rs @@ -44,8 +44,6 @@ pub enum Token<'a> { Pow, /// A mod (`%`) operator. Mod, - /// The bang (`!`) operator. - Bang, /// A dot operator (`.`) Dot, /// The comma operator (`,`) @@ -103,7 +101,6 @@ impl<'a> fmt::Display for Token<'a> { Token::FloorDiv => f.write_str("`//`"), Token::Pow => f.write_str("`**`"), Token::Mod => f.write_str("`%`"), - Token::Bang => f.write_str("`!`"), Token::Dot => f.write_str("`.`"), Token::Comma => f.write_str("`,`"), Token::Colon => f.write_str("`:`"), diff --git a/minijinja/tests/inputs/tests.txt b/minijinja/tests/inputs/tests.txt index 725a9eea..a3773511 100644 --- a/minijinja/tests/inputs/tests.txt +++ b/minijinja/tests/inputs/tests.txt @@ -38,7 +38,9 @@ map: {{ map is mapping }} string: {{ string is string }} not-string: {{ mapping is string }} starts-with-a: {{ string is startingwith('a') }} +starts-with-a-noparen: {{ string is startingwith 'a' }} ends-with-ha: {{ string is endingwith('ha') }} +ends-with-ha-noparen: {{ string is endingwith 'ha' }} not-safe: {{ "foo" is safe }} safe: {{ "foo"|escape is safe }} is-true: {{ true is true }} | {{ 42 is true }} @@ -47,8 +49,13 @@ is-filter: {{ 'escape' is filter }} | {{ 'unknown-filter' is filter }} is-test: {{ 'safe' is test }} | {{ 'unknown-test' is test }} is-boolean: {{ true is boolean }} | {{ 42 is boolean }} is-divisibleby: {{ 42 is divisibleby(2) }} | {{ 41 is divisibleby(2) }} +is-divisibleby-noparen: {{ 42 is divisibleby(2) }} | {{ 41 is divisibleby 2 }} is-lower: {{ "foo" is lower }} | {{ "FOO" is lower }} is-upper: {{ "foo" is upper }} | {{ "FOO" is upper }} seq-same-as: {{ [1, 2, 3] is sameas([1, 2, 3]) }} +seq-same-as-noparen: {{ [1, 2, 3] is sameas [1, 2, 3] }} const-same-as: {{ true is sameas(true) }} -int-same-as: {{ 1 is sameas(1.0) }} \ No newline at end of file +const-same-as-noparen: {{ true is sameas true }} +int-same-as: {{ 1 is sameas(1.0) }} +int-same-as-noparen: {{ 1 is sameas 1.0 }} +neg-int-same-as-noparen: {{ -1 is sameas -1 }} \ No newline at end of file diff --git a/minijinja/tests/lexer-inputs/operators.txt b/minijinja/tests/lexer-inputs/operators.txt index 632b5d4b..019ca14b 100644 --- a/minijinja/tests/lexer-inputs/operators.txt +++ b/minijinja/tests/lexer-inputs/operators.txt @@ -1,3 +1,3 @@ {} --- -{{ (!a + b) * (c - d) / e % f // g ~ h }} \ No newline at end of file +{{ (a + b) * (c - d) / e % f // g ~ h }} \ No newline at end of file diff --git a/minijinja/tests/snapshots/test_lexer__lexer@operators.txt.snap b/minijinja/tests/snapshots/test_lexer__lexer@operators.txt.snap index 99e07951..8f242203 100644 --- a/minijinja/tests/snapshots/test_lexer__lexer@operators.txt.snap +++ b/minijinja/tests/snapshots/test_lexer__lexer@operators.txt.snap @@ -1,14 +1,12 @@ --- source: minijinja/tests/test_lexer.rs -description: "{{ (!a + b) * (c - d) / e % f // g ~ h }}" +description: "{{ (a + b) * (c - d) / e % f // g ~ h }}" input_file: minijinja/tests/lexer-inputs/operators.txt --- VariableStart "{{" ParenOpen "(" -Bang - "!" Ident("a") "a" Plus @@ -47,4 +45,3 @@ Ident("h") "h" VariableEnd "}}" - diff --git a/minijinja/tests/snapshots/test_templates__vm@tests.txt.snap b/minijinja/tests/snapshots/test_templates__vm@tests.txt.snap index 4378c3d5..295c689e 100644 --- a/minijinja/tests/snapshots/test_templates__vm@tests.txt.snap +++ b/minijinja/tests/snapshots/test_templates__vm@tests.txt.snap @@ -1,6 +1,6 @@ --- source: minijinja/tests/test_templates.rs -description: "even: {{ two is even }}\nodd: {{ two is odd }}\nundefined: {{ two is undefined }}\ndefined: {{ two is defined }}\nundefined2: {{ ohwell is undefined }}\ndefined2: {{ ohwell is defined }}\nnone: {{ none is none }}\nnot-none: {{ 42 is not none }}\nnumber-int: {{ two is number }}\nnumber-float: {{ two_dot_two is number }}\ninteger-int: {{ 42 is integer }}\ninteger-float: {{ 42.0 is integer }}\nfloat-int: {{ 42 is float }}\nfloat-float: {{ 42.0 is float }}\nnot-seq: {{ two is sequence }}\nseq: {{ seq is sequence }}\nreverse-not-seq: {{ seq|reverse is sequence }}\niterable: {{ seq is iterable }}\niterable-reverse: {{ seq|reverse is iterable }}\nstring-iterable: {{ string is iterable }}\nnot-iterable: {{ two is iterable }}\nnot-map: {{ two is mapping }}\nmap: {{ map is mapping }}\nstring: {{ string is string }}\nnot-string: {{ mapping is string }}\nstarts-with-a: {{ string is startingwith('a') }}\nends-with-ha: {{ string is endingwith('ha') }}\nnot-safe: {{ \"foo\" is safe }}\nsafe: {{ \"foo\"|escape is safe }}\nis-true: {{ true is true }} | {{ 42 is true }}\nis-false: {{ false is false }} | {{ 0 is false }}\nis-filter: {{ 'escape' is filter }} | {{ 'unknown-filter' is filter }}\nis-test: {{ 'safe' is test }} | {{ 'unknown-test' is test }}\nis-boolean: {{ true is boolean }} | {{ 42 is boolean }}\nis-divisibleby: {{ 42 is divisibleby(2) }} | {{ 41 is divisibleby(2) }}\nis-lower: {{ \"foo\" is lower }} | {{ \"FOO\" is lower }}\nis-upper: {{ \"foo\" is upper }} | {{ \"FOO\" is upper }}\nseq-same-as: {{ [1, 2, 3] is sameas([1, 2, 3]) }}\nconst-same-as: {{ true is sameas(true) }}\nint-same-as: {{ 1 is sameas(1.0) }}" +description: "even: {{ two is even }}\nodd: {{ two is odd }}\nundefined: {{ two is undefined }}\ndefined: {{ two is defined }}\nundefined2: {{ ohwell is undefined }}\ndefined2: {{ ohwell is defined }}\nnone: {{ none is none }}\nnot-none: {{ 42 is not none }}\nnumber-int: {{ two is number }}\nnumber-float: {{ two_dot_two is number }}\ninteger-int: {{ 42 is integer }}\ninteger-float: {{ 42.0 is integer }}\nfloat-int: {{ 42 is float }}\nfloat-float: {{ 42.0 is float }}\nnot-seq: {{ two is sequence }}\nseq: {{ seq is sequence }}\nreverse-not-seq: {{ seq|reverse is sequence }}\niterable: {{ seq is iterable }}\niterable-reverse: {{ seq|reverse is iterable }}\nstring-iterable: {{ string is iterable }}\nnot-iterable: {{ two is iterable }}\nnot-map: {{ two is mapping }}\nmap: {{ map is mapping }}\nstring: {{ string is string }}\nnot-string: {{ mapping is string }}\nstarts-with-a: {{ string is startingwith('a') }}\nstarts-with-a-noparen: {{ string is startingwith 'a' }}\nends-with-ha: {{ string is endingwith('ha') }}\nends-with-ha-noparen: {{ string is endingwith 'ha' }}\nnot-safe: {{ \"foo\" is safe }}\nsafe: {{ \"foo\"|escape is safe }}\nis-true: {{ true is true }} | {{ 42 is true }}\nis-false: {{ false is false }} | {{ 0 is false }}\nis-filter: {{ 'escape' is filter }} | {{ 'unknown-filter' is filter }}\nis-test: {{ 'safe' is test }} | {{ 'unknown-test' is test }}\nis-boolean: {{ true is boolean }} | {{ 42 is boolean }}\nis-divisibleby: {{ 42 is divisibleby(2) }} | {{ 41 is divisibleby(2) }}\nis-divisibleby-noparen: {{ 42 is divisibleby(2) }} | {{ 41 is divisibleby 2 }}\nis-lower: {{ \"foo\" is lower }} | {{ \"FOO\" is lower }}\nis-upper: {{ \"foo\" is upper }} | {{ \"FOO\" is upper }}\nseq-same-as: {{ [1, 2, 3] is sameas([1, 2, 3]) }}\nseq-same-as-noparen: {{ [1, 2, 3] is sameas [1, 2, 3] }}\nconst-same-as: {{ true is sameas(true) }}\nconst-same-as-noparen: {{ true is sameas true }}\nint-same-as: {{ 1 is sameas(1.0) }}\nint-same-as-noparen: {{ 1 is sameas 1.0 }}\nneg-int-same-as-noparen: {{ -1 is sameas -1 }}" info: two: 2 two_dot_two: 2.2 @@ -39,7 +39,9 @@ map: true string: true not-string: false starts-with-a: true +starts-with-a-noparen: true ends-with-ha: true +ends-with-ha-noparen: true not-safe: false safe: true is-true: true | false @@ -48,8 +50,13 @@ is-filter: true | false is-test: true | false is-boolean: true | false is-divisibleby: true | false +is-divisibleby-noparen: true | false is-lower: true | false is-upper: false | true seq-same-as: false +seq-same-as-noparen: false const-same-as: true +const-same-as-noparen: true int-same-as: false +int-same-as-noparen: false +neg-int-same-as-noparen: true