Use ISO 646 alternative operators for bit ops

This change allows simplifying the use of filter expressions, because
you won't have to care about spaces around the `|` pipe operator.
This commit is contained in:
René Kijewski 2024-06-19 10:58:43 +02:00
parent 704c506770
commit 879d889bb2
7 changed files with 125 additions and 120 deletions

View File

@ -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;

View File

@ -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]

View File

@ -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 }}

View File

@ -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 -%}

View File

@ -4,4 +4,4 @@
{{ 1 * 2 + 4 -}}
{{ 11 - 15 / 3 -}}
{{ 4 + 5 % 3 -}}
{{ 4 | 2 + 5 & 2 -}}
{{ 4 bitor 2 + 5 bitand 2 -}}

View File

@ -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() {
}

View File

@ -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)