Skip to content

Commit

Permalink
Fix precedence for quantifiers and triple-op exprs (#26)
Browse files Browse the repository at this point in the history
Fixes #25 

Basically, rather than considering `&&&` and `|||` to be prefix+binary
operations (as the actual Verus parser does), we instead split them out
into their own expression group. This allows us to fix the parsing
precedence with respect to quantifiers.

Co-authored-by: Jay Bosamiya
  • Loading branch information
parno authored Feb 7, 2024
2 parents 9fdd735 + 89970e3 commit cbcd829
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 12 deletions.
29 changes: 27 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,25 @@ fn to_doc<'a>(
Rule::expr_outer => map_to_doc(ctx, arena, pair),
Rule::expr_outer_no_struct => map_to_doc(ctx, arena, pair),
Rule::expr_no_struct => map_to_doc(ctx, arena, pair),
Rule::bulleted_expr => block_braces(arena, map_to_doc(ctx, arena, pair), true),
Rule::bulleted_expr_inner => {
let pairs = pair.into_inner();
let mut first = true;
arena.concat(pairs.map(|p| {
let d = to_doc(ctx, p.clone(), arena);
match p.as_rule() {
Rule::triple_and | Rule::triple_or => {
if first {
first = false;
d
} else {
arena.hardline().append(d)
}
}
_ => d,
}
}))
}
Rule::macro_expr => unsupported(pair),
Rule::literal => map_to_doc(ctx, arena, pair),
Rule::path_expr => map_to_doc(ctx, arena, pair),
Expand Down Expand Up @@ -982,12 +1001,18 @@ fn to_doc<'a>(
Rule::fn_block_expr => {
let pairs = pair.into_inner();
let mapped = map_pairs_to_doc(ctx, arena, &pairs);
block_braces(arena, mapped, terminal_expr(&pairs))
block_braces(
arena,
mapped,
terminal_expr(&pairs)
|| pairs
.clone()
.any(|x| x.as_rule() == Rule::bulleted_expr_inner),
)
}
Rule::prefix_expr => map_to_doc(ctx, arena, pair),
Rule::prefix_expr_no_struct => map_to_doc(ctx, arena, pair),
Rule::assignment_ops => docs![arena, arena.space(), s, arena.line()],
Rule::bin_expr_ops_special => arena.hardline().append(map_to_doc(ctx, arena, pair)),
Rule::bin_expr_ops_normal => docs![arena, arena.line(), s, arena.space()]
.nest(INDENT_SPACES)
.group(),
Expand Down
28 changes: 18 additions & 10 deletions src/verus.pest
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,8 @@ expr_inner_no_struct = _{
| assert_expr
| assume_expr
| assert_forall_expr
| bulleted_expr
| bulleted_expr_inner
| block_expr
| box_expr
| break_expr
Expand Down Expand Up @@ -1038,6 +1040,15 @@ expr_with_block = {
| assert_forall_expr
}

bulleted_expr = {
"{" ~ bulleted_expr_inner ~ "}"
}

bulleted_expr_inner = {
(triple_and ~ expr)+
| (triple_or ~ expr)+
}

macro_expr = {
macro_call
}
Expand Down Expand Up @@ -1091,28 +1102,25 @@ fn_block_expr = {
attr* ~
stmt* ~
expr? ~
"}"
"}"
| "{" ~ bulleted_expr_inner ~ "}"
}

prefix_expr = {
attr* ~ (dash_str | bang_str | star_str | triple_or | triple_and) ~ expr
attr* ~ (dash_str | bang_str | star_str) ~ expr
}

prefix_expr_no_struct = {
attr* ~ (dash_str | bang_str | star_str | triple_or | triple_and) ~ expr_no_struct
attr* ~ (dash_str | bang_str | star_str) ~ expr_no_struct
}

assignment_ops = {
"=" | "+=" | "/=" | "*=" | "%=" | ">>=" | "<<=" | "-=" | "|=" | "&=" | "^="
}

bin_expr_ops_special = {
triple_or | triple_and
}

bin_expr_ops_normal = {
| ("||" | "|")
| ("&&" | "&")
| (!"|||" ~ ("||" | "|"))
| (!"&&&" ~ ("&&" | "&"))
| "<==>"
| "===" | "=~=" | "=~~="
| "==>" | "<=="
Expand All @@ -1124,7 +1132,7 @@ bin_expr_ops_normal = {
}

bin_expr_ops = {
bin_expr_ops_special | bin_expr_ops_normal
bin_expr_ops_normal
}

paren_expr_inner = {
Expand Down
17 changes: 17 additions & 0 deletions tests/verus-consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1435,3 +1435,20 @@ fn test()
} // verus!
"###);
}

#[test]
fn verus_quantifier_and_bulleted_expr_precedence() {
// Regression test for https://github.com/jaybosamiya/verusfmt/issues/25
let file = r#" verus! { spec fn foo() { &&& forall|x:int| f &&& g } } "#;

assert_snapshot!(parse_and_format(file).unwrap(), @r###"
verus! {
spec fn foo() {
&&& forall|x: int| f
&&& g
}
} // verus!
"###);
}

0 comments on commit cbcd829

Please sign in to comment.