diff --git a/rinja_parser/src/expr.rs b/rinja_parser/src/expr.rs index 7dccdfbc..6f033d2c 100644 --- a/rinja_parser/src/expr.rs +++ b/rinja_parser/src/expr.rs @@ -4,14 +4,14 @@ use std::str; use nom::branch::alt; use nom::bytes::complete::{tag, take_till}; use nom::character::complete::char; -use nom::combinator::{cut, map, not, opt, peek, recognize}; +use nom::combinator::{cut, map, not, opt, peek, recognize, value}; use nom::error::ErrorKind; use nom::error_position; use nom::multi::{fold_many0, many0, separated_list0}; use nom::sequence::{pair, preceded, terminated, tuple}; use super::{ - char_lit, filter, identifier, not_ws, num_lit, path_or_identifier, str_lit, ws, Level, + char_lit, filter, identifier, keyword, not_ws, num_lit, path_or_identifier, str_lit, ws, Level, PathOrIdentifier, }; use crate::{ErrorContext, ParseResult, WithSpan}; @@ -22,10 +22,7 @@ macro_rules! expr_prec_layer { let (_, level) = level.nest(i)?; let start = i; let (i, left) = Self::$inner(i, level)?; - let (i, right) = many0(pair( - ws(tag($op)), - |i| Self::$inner(i, level), - ))(i)?; + let (i, right) = many0(pair(ws($op), |i| Self::$inner(i, level)))(i)?; Ok(( i, right.into_iter().fold(left, |left, (op, right)| { @@ -34,22 +31,28 @@ macro_rules! expr_prec_layer { )) } }; - ( $name:ident, $inner:ident, $( $op:expr ),+ ) => { - fn $name(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> { - let (_, level) = level.nest(i)?; - let start = i; - let (i, left) = Self::$inner(i, level)?; - let (i, right) = many0(pair( - ws(alt(($( tag($op) ),+,))), - |i| Self::$inner(i, level), - ))(i)?; - Ok(( - i, - right.into_iter().fold(left, |left, (op, right)| { - WithSpan::new(Self::BinOp(op, Box::new(left), Box::new(right)), start) - }), - )) +} + +fn alternative_binop<'a>( + rust: &'static str, + dont_match: Option<&'static str>, + rinja: &'static str, +) -> impl Fn(&'a str) -> ParseResult<'a, &'static str> { + move |i: &'a str| -> ParseResult<'a, &'static str> { + let (_, fail) = opt(tag(rust))(i)?; + if fail.is_some() { + let succeed = match dont_match { + Some(dont_match) => opt(tag(dont_match))(i)?.1.is_some(), + None => false, + }; + if !succeed { + return Err(nom::Err::Failure(ErrorContext::new( + format!("the binary operator '{rust}' is called '{rinja}' in rinja"), + i, + ))); + } } + value(rust, keyword(rinja))(i) } } @@ -189,15 +192,26 @@ impl<'a> Expr<'a> { ))(i) } - expr_prec_layer!(or, and, "||"); - expr_prec_layer!(and, compare, "&&"); - expr_prec_layer!(compare, bor, "==", "!=", ">=", ">", "<=", "<"); - expr_prec_layer!(bor, bxor, "|"); - expr_prec_layer!(bxor, band, "^"); - expr_prec_layer!(band, shifts, "&"); - expr_prec_layer!(shifts, addsub, ">>", "<<"); - expr_prec_layer!(addsub, muldivmod, "+", "-"); - expr_prec_layer!(muldivmod, filtered, "*", "/", "%"); + expr_prec_layer!(or, and, tag("||")); + expr_prec_layer!(and, compare, tag("&&")); + expr_prec_layer!( + compare, + bor, + alt(( + tag("=="), + tag("!="), + tag(">="), + tag(">"), + tag("<="), + tag("<"), + )) + ); + expr_prec_layer!(bor, bxor, alternative_binop("|", Some("||"), "bitor")); + expr_prec_layer!(bxor, band, alternative_binop("^", None, "xor")); + expr_prec_layer!(band, shifts, alternative_binop("&", Some("&&"), "bitand")); + expr_prec_layer!(shifts, addsub, alt((tag(">>"), tag("<<")))); + expr_prec_layer!(addsub, muldivmod, alt((tag("+"), tag("-")))); + expr_prec_layer!(muldivmod, filtered, alt((tag("*"), tag("/"), tag("%")))); fn filtered(i: &'a str, mut level: Level) -> ParseResult<'a, WithSpan<'a, Self>> { let start = i; diff --git a/rinja_parser/src/tests.rs b/rinja_parser/src/tests.rs index 29c2b8e3..2860ce69 100644 --- a/rinja_parser/src/tests.rs +++ b/rinja_parser/src/tests.rs @@ -684,20 +684,6 @@ fn test_odd_calls() { })), )] ); - assert_eq!( - Ast::from_str("{{ a(b) |c }}", None, &syntax).unwrap().nodes, - vec![Node::Expr( - Ws(None, None), - WithSpan::no_span(Expr::BinOp( - "|", - Box::new(WithSpan::no_span(Expr::Call( - Box::new(WithSpan::no_span(Expr::Var("a"))), - vec![WithSpan::no_span(Expr::Var("b"))] - ))), - Box::new(WithSpan::no_span(Expr::Var("c"))) - ),) - )] - ); } #[test] @@ -847,19 +833,6 @@ fn test_parse_tuple() { })), )], ); - assert_eq!( - Ast::from_str("{{ () | abs }}", None, &syntax) - .unwrap() - .nodes, - vec![Node::Expr( - Ws(None, None), - WithSpan::no_span(Expr::BinOp( - "|", - Box::new(WithSpan::no_span(Expr::Tuple(vec![]))), - Box::new(WithSpan::no_span(Expr::Var("abs"))) - )), - )], - ); assert_eq!( Ast::from_str("{{ (1)|abs }}", None, &syntax).unwrap().nodes, vec![Node::Expr( @@ -872,21 +845,6 @@ fn test_parse_tuple() { })), )], ); - assert_eq!( - Ast::from_str("{{ (1) | abs }}", None, &syntax) - .unwrap() - .nodes, - vec![Node::Expr( - Ws(None, None), - WithSpan::no_span(Expr::BinOp( - "|", - Box::new(WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span( - Expr::NumLit("1") - ))))), - Box::new(WithSpan::no_span(Expr::Var("abs"))) - )), - )], - ); assert_eq!( Ast::from_str("{{ (1,)|abs }}", None, &syntax) .unwrap() @@ -901,21 +859,6 @@ fn test_parse_tuple() { })), )], ); - assert_eq!( - Ast::from_str("{{ (1,) | abs }}", None, &syntax) - .unwrap() - .nodes, - vec![Node::Expr( - Ws(None, None), - WithSpan::no_span(Expr::BinOp( - "|", - Box::new(WithSpan::no_span(Expr::Tuple(vec![WithSpan::no_span( - Expr::NumLit("1") - )]))), - Box::new(WithSpan::no_span(Expr::Var("abs"))) - )), - )], - ); assert_eq!( Ast::from_str("{{ (1, 2)|abs }}", None, &syntax) .unwrap() @@ -931,22 +874,6 @@ fn test_parse_tuple() { })), )], ); - assert_eq!( - Ast::from_str("{{ (1, 2) | abs }}", None, &syntax) - .unwrap() - .nodes, - vec![Node::Expr( - Ws(None, None), - WithSpan::no_span(Expr::BinOp( - "|", - Box::new(WithSpan::no_span(Expr::Tuple(vec![ - WithSpan::no_span(Expr::NumLit("1")), - WithSpan::no_span(Expr::NumLit("2")) - ]))), - Box::new(WithSpan::no_span(Expr::Var("abs"))) - )), - )], - ); } #[test] @@ -1050,17 +977,6 @@ fn test_parse_array() { })) )], ); - assert_eq!( - Ast::from_str("{{ [] |foo }}", None, &syntax).unwrap().nodes, - vec![Node::Expr( - Ws(None, None), - WithSpan::no_span(Expr::BinOp( - "|", - Box::new(WithSpan::no_span(Expr::Array(vec![]))), - Box::new(WithSpan::no_span(Expr::Var("foo"))) - )), - )], - ); } #[test] diff --git a/testing/templates/allow-whitespaces.html b/testing/templates/allow-whitespaces.html index 4c64ca2e..dbe65537 100644 --- a/testing/templates/allow-whitespaces.html +++ b/testing/templates/allow-whitespaces.html @@ -34,8 +34,8 @@ {{-1}}{{ -1 }}{{ - 1 }} {{1+2}}{{ 1+2 }}{{ 1 +2 }}{{ 1+ 2 }} {{ 1 + 2 }} {{1*2}}{{ 1*2 }}{{ 1 *2 }}{{ 1* 2 }} {{ 1 * 2 }} -{{1&2}}{{ 1&2 }}{{ 1 &2 }}{{ 1& 2 }} {{ 1 & 2 }} -{{1|2}}{{ 1|2 }}{{ 1 |2 }}{{ 1| 2 }} {{ 1 | 2 }} +{{1 bitand 2}}{{ 1 bitand 2 }}{{ 1 bitand 2 }}{{ 1 bitand 2 }} {{ 1 bitand 2 }} +{{1 bitor 2}}{{ 1 bitor 2 }}{{ 1 bitor 2}}{{1 bitor 2 }} {{1 bitor 2}} {{true}}{{false}} {{!true}}{{ !true }}{{ ! true }} diff --git a/testing/templates/operators.html b/testing/templates/operators.html index 7d8ed638..62b7ee70 100644 --- a/testing/templates/operators.html +++ b/testing/templates/operators.html @@ -16,13 +16,13 @@ {% if c >> b == a -%} lsh {%- endif -%} -{% if a & b == b -%} +{% if a bitand b == b -%} band {%- endif -%} -{% if b ^ c == a + c -%} +{% if b xor c == a + c -%} bxor {%- endif -%} -{% if (b | c) == a + c -%} +{% if b bitor c == a + c -%} bor {%- endif -%} {% if a == b && a + b == c -%} diff --git a/testing/templates/precedence.html b/testing/templates/precedence.html index e5d12ddb..c9e2fa1f 100644 --- a/testing/templates/precedence.html +++ b/testing/templates/precedence.html @@ -4,4 +4,4 @@ {{ 1 * 2 + 4 -}} {{ 11 - 15 / 3 -}} {{ 4 + 5 % 3 -}} -{{ 4 | 2 + 5 & 2 -}} +{{ 4 bitor 2 + 5 bitand 2 -}} diff --git a/testing/tests/ui/iso646.rs b/testing/tests/ui/iso646.rs new file mode 100644 index 00000000..efe9d642 --- /dev/null +++ b/testing/tests/ui/iso646.rs @@ -0,0 +1,46 @@ +use rinja::Template; + +#[derive(Template)] +#[template(ext = "txt", source = "{{ a & b }}")] +struct BitAnd { + a: u32, + b: u32, +} + +#[derive(Template)] +#[template(ext = "txt", source = "{{ a bitand b }}")] +struct BitAndIso646 { + a: u32, + b: u32, +} + +#[derive(Template)] +#[template(ext = "txt", source = "{{ a | b }}")] +struct BitOr { + a: u32, + b: u32, +} + +#[derive(Template)] +#[template(ext = "txt", source = "{{ a bitor b }}")] +struct BitOrIso646 { + a: u32, + b: u32, +} + +#[derive(Template)] +#[template(ext = "txt", source = "{{ a ^ b }}")] +struct Xor { + a: u32, + b: u32, +} + +#[derive(Template)] +#[template(ext = "txt", source = "{{ a xor b }}")] +struct XorIso646 { + a: u32, + b: u32, +} + +fn main() { +} diff --git a/testing/tests/ui/iso646.stderr b/testing/tests/ui/iso646.stderr new file mode 100644 index 00000000..1ff1ae6c --- /dev/null +++ b/testing/tests/ui/iso646.stderr @@ -0,0 +1,29 @@ +error: the binary operator '&' is called 'bitand' in rinja + failed to parse template source at row 1, column 5 near: + "& b }}" + --> tests/ui/iso646.rs:3:10 + | +3 | #[derive(Template)] + | ^^^^^^^^ + | + = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: the binary operator '|' is called 'bitor' in rinja + failed to parse template source at row 1, column 5 near: + "| b }}" + --> tests/ui/iso646.rs:17:10 + | +17 | #[derive(Template)] + | ^^^^^^^^ + | + = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: the binary operator '^' is called 'xor' in rinja + failed to parse template source at row 1, column 5 near: + "^ b }}" + --> tests/ui/iso646.rs:31:10 + | +31 | #[derive(Template)] + | ^^^^^^^^ + | + = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)