Skip to content

Commit

Permalink
match_nodes: Support top-level or-patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Mar 3, 2024
1 parent 8a851d0 commit c3adf3f
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 11 deletions.
12 changes: 12 additions & 0 deletions pest_consume/src/match_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@
/// middle: `[rule(many1).., rule(x), other_rule(many2)..]` will always fail because `many1` will
/// have consumed all the `rule` nodes.
///
/// # Or-patterns
///
/// `match_nodes` supports a simple form of or-patterns:
///
/// ```ignore
/// match_nodes!(input.into_children();
/// [number(x), boolean(b)] | [boolean(b), number(x)] => { ... },
/// )
/// ```
///
/// This is implemented by simply duplicating the branch body.
///
/// # Notes
///
/// The macro assumes that it is used within a consumer method, and uses `Self::$method(...)` to
Expand Down
11 changes: 11 additions & 0 deletions pest_consume/tests/match_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,14 @@ fn multi_multi_tag() {
Ok((3, 4))
);
}

#[test]
fn or_pattern() {
let or_pattern = |input: Vec<NodeKind>| {
Ok(match_nodes!(<TestMatcher>; notag(input);
[number(x), boolean(b)] | [boolean(b), number(x)] => (x, b),
))
};
assert_eq!(or_pattern(vec![number(42), boolean(true)]), Ok((42, true)));
assert_eq!(or_pattern(vec![boolean(true), number(42)]), Ok((42, true)));
}
39 changes: 28 additions & 11 deletions pest_consume_macros/src/match_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ struct Pattern {
multiple: bool,
}

struct MatchBranch {
struct Alternative {
patterns: Punctuated<Pattern, Token![,]>,
}

struct MatchBranch {
alternatives: Punctuated<Alternative, Token![|]>,
body: Expr,
}

Expand Down Expand Up @@ -50,14 +54,20 @@ impl Parse for MacroInput {

impl Parse for MatchBranch {
fn parse(input: ParseStream) -> Result<Self> {
let contents;
let _: token::Bracket = bracketed!(contents in input);

let patterns = Punctuated::parse_terminated(&contents)?;
let alternatives = Punctuated::parse_separated_nonempty(&input)?;
let _: Token![=>] = input.parse()?;
let body = input.parse()?;

Ok(MatchBranch { patterns, body })
Ok(MatchBranch { alternatives, body })
}
}

impl Parse for Alternative {
fn parse(input: ParseStream) -> Result<Self> {
let contents;
let _: token::Bracket = bracketed!(contents in input);
let patterns = Punctuated::parse_terminated(&contents)?;
Ok(Alternative { patterns })
}
}

Expand Down Expand Up @@ -156,16 +166,17 @@ fn traverse_pattern(
)
}

fn make_branch(
branch: MatchBranch,
fn make_alternative(
alternative: Alternative,
body: &Expr,
i_nodes: &Ident,
i_node_namer: &Ident,
parser: &Type,
) -> TokenStream {
let i_nodes_iter = Ident::new("___nodes_iter", Span::call_site());
let name_enum = quote!(<#parser as ::pest_consume::NodeMatcher>::NodeName);
let node_namer_ty = quote!(<_ as ::pest_consume::NodeNamer<#parser>>);
let patterns: Vec<_> = branch.patterns.into_iter().collect();
let patterns: Vec<_> = alternative.patterns.into_iter().collect();

let matches_pat = |pat: &Pattern, x| {
let rule_cond = match &pat.rule_name {
Expand Down Expand Up @@ -236,7 +247,6 @@ fn make_branch(
quote!(unreachable!()),
);

let body = &branch.body;
quote!(
_ if {
let check_condition = |slice: &[_]| -> bool {
Expand Down Expand Up @@ -267,7 +277,14 @@ pub fn match_nodes(
let branches = input
.branches
.into_iter()
.map(|br| make_branch(br, &i_nodes, &i_node_namer, parser))
.flat_map(|br| {
let body = br.body;
let i_nodes = &i_nodes;
let i_node_namer = &i_node_namer;
br.alternatives.into_iter().map(move |alt| {
make_alternative(alt, &body, i_nodes, i_node_namer, parser)
})
})
.collect::<Vec<_>>();

let node_list_ty = quote!(<_ as ::pest_consume::NodeList<#parser>>);
Expand Down

0 comments on commit c3adf3f

Please sign in to comment.