1
/// Pattern-match on [`Nodes`] with slice-like syntax and strong types..
2
///
3
/// See [examples] and [crate-level documentation][`pest_consume`] for usage.
4
///
5
/// Example usage:
6
/// ```ignore
7
/// let nodes: Nodes<_, _> = ...:
8
/// match_nodes!(nodes;
9
///     [string(s), number(n)] => s.len() + n,
10
///     [_, field(fs)..] => fs.filter(|f| f > 0).count(),
11
/// )
12
/// ```
13
///
14
/// # Syntax
15
///
16
/// The macro takes an expression followed by `;`, followed by one or more branches separated by `,`.
17
/// Each branch has the form `[$patterns] => $body`. The body is an arbitrary expression.
18
/// The patterns are a comma-seperated list of either `$rule_name($binder)` or just `$binder`, each
19
/// optionally followed by `..` to indicate a variable-length pattern.
20
///
21
/// # How it works
22
///
23
/// `match_nodes` desugars rather straightforwardly into calls to the methods corresponding to
24
/// the rules matched on.
25
/// For example:
26
/// ```ignore
27
/// match_nodes!(input.into_children();
28
///     [field(fields)..] => fields.count(),
29
///     [string(s), number(n)] => s.len() + n,
30
/// )
31
/// ```
32
/// desugars roughly into:
33
/// ```ignore
34
/// let nodes = { input.into_children() };
35
/// if ... { // check that all rules in `nodes` are the `field` rule
36
///     let fields = nodes
37
///         .map(|node| Self::field(node)) // Recursively parse children nodes
38
///         ... // Propagate errors
39
///     { fields.count() }
40
/// } else if ... { // check that the nodes has two elements, with rules `string` and `number`
41
///     let s = Self::string(nodes.next().unwrap())?;
42
///     let n = Self::number(nodes.next().unwrap())?;
43
///     { s.len() + n }
44
/// } else {
45
///     ... // error because we got unexpected rules
46
/// }
47
/// ```
48
///
49
/// # Tags
50
///
51
/// `match_nodes` supports matching with tags:
52
///
53
/// ```ignore
54
/// match_nodes!(input.into_children();
55
///     [tag1 # string(s).., number(n)] => { ... },
56
/// )
57
/// ```
58
///
59
/// # Matching raw nodes
60
///
61
/// Sometimes you may want to manipulate `Node`s directly. For that, just omit a rule name when
62
/// matching:
63
/// ```ignore
64
/// match_nodes!(input.into_children();
65
///     [string(s), node] => { ... },
66
/// )
67
/// ```
68
/// Here the first node will be parsed using the `string` rule, but the second node will be
69
/// returned as-is. This also supports the `..` syntax, which returns an iterator of `Node`s.
70
///
71
/// This can come useful when `pest_consume` isn't powerful enough for your use-case, for example
72
/// if you want the ability to choose from multiple parsing functions for the same rule. This can
73
/// usually be avoided by using some [advanced features] or tweaking the grammar, but if not you
74
/// can always fall back to manipulating `Node`s by hand.
75
///
76
/// # Variable-length patterns
77
///
78
/// Variable-length patterns (`rule(binder)..`) are special: they match any number of nodes with
79
/// the given rule, and the `binder` is bound to an iterator of the parsed results.
80
///
81
/// Multiple variable-length patterns match greedily: in `[rule(x).., rule(y)..]`, `y` will always
82
/// be empty because all the elements were matched by `x`.
83
///
84
/// Subtlety with trailing patterns: single trailing patterns are correctly handled, e.g.
85
/// `[rule(many).., rule(x), rule(y)]` works as expected. This doesn't apply to patterns in the
86
/// middle: `[rule(many1).., rule(x), other_rule(many2)..]` will always fail because `many1` will
87
/// have consumed all the `rule` nodes.
88
///
89
/// # Or-patterns
90
///
91
/// `match_nodes` supports a simple form of or-patterns:
92
///
93
/// ```ignore
94
/// match_nodes!(input.into_children();
95
///     [number(x), boolean(b)] | [boolean(b), number(x)] => { ... },
96
/// )
97
/// ```
98
///
99
/// This is implemented by simply duplicating the branch body.
100
///
101
/// # Notes
102
///
103
/// The macro assumes that it is used within a consumer method, and uses `Self::$method(...)` to
104
/// parse the input nodes.
105
/// To use it outside a method, you can pass it the parser struct as follows (the angle brackets are mandatory):
106
/// ```ignore
107
/// match_nodes(<CSVParser>; nodes;
108
///     ...
109
/// )
110
/// ```
111
///
112
/// It also assumes it can `return Err(...)` in case of errors.
113
///
114
/// [`pest_consume`]: index.html
115
/// [advanced features]: advanced_features/index.html
116
/// [`Nodes`]: struct.Nodes.html
117
/// [examples]: https://github.com/Nadrieril/pest_consume/tree/master/pest_consume/examples
118
// We wrap the proc-macro in a macro here because I want to write the doc in this crate.
119
#[macro_export]
120
macro_rules! match_nodes {
121
    ($($x:tt)*) => {
122
        $crate::match_nodes_!($($x)*)
123
    };
124
}
125
pub use merc_pest_consume_macros::match_nodes as match_nodes_;
126

            
127
// Reexport
128
pub use itertools::Itertools;
129

            
130
/// The trait that powers the `match_nodes` macro. Exposed to make `match_nodes` testable and
131
/// usable outside `pest`. It and its siblings are very ad-hoc for macro purposes and will break
132
/// semver.
133
pub trait NodeList<M: NodeMatcher> {
134
    type Node;
135
    /// The data for the next step.
136
    type NodeNamer: NodeNamer<M>;
137
    fn consume(self) -> (Vec<Self::Node>, Self::NodeNamer);
138
}
139

            
140
/// Sibling trait to `NodeList`. The separate trait is needed so we can guide inference in macros
141
/// (where we can't write the type name).
142
pub trait NodeNamer<M: NodeMatcher> {
143
    type Node;
144
    /// The type of errors.
145
    type Error;
146

            
147
    fn node_name(&self, n: &Self::Node) -> M::NodeName;
148
    fn tag<'a>(&self, n: &'a Self::Node) -> Option<&'a str>;
149
    fn error(self, message: String) -> Self::Error;
150
}
151

            
152
/// Sibling trait to `NodeList`.
153
pub trait NodeMatcher {
154
    /// An enum such that each `NodeName::$rule` has a corresponding `Self::$rule(n: Node) -> T` function.
155
    type NodeName: Eq;
156
}
157

            
158
impl<T: crate::Parser> NodeMatcher for T {
159
    type NodeName = T::AliasedRule;
160
}
161

            
162
impl<'i, P, D> NodeList<P> for crate::Nodes<'i, P::Rule, D>
163
where
164
    D: Clone,
165
    P: crate::Parser,
166
{
167
    type Node = crate::Node<'i, P::Rule, D>;
168
    type NodeNamer = Self;
169

            
170
523298
    fn consume(mut self) -> (Vec<Self::Node>, Self::NodeNamer) {
171
523298
        let vec = self.by_ref().collect();
172
523298
        (vec, self)
173
523298
    }
174
}
175

            
176
impl<'i, P, D> NodeNamer<P> for crate::Nodes<'i, P::Rule, D>
177
where
178
    D: Clone,
179
    P: crate::Parser,
180
{
181
    type Node = crate::Node<'i, P::Rule, D>;
182
    type Error = pest::error::Error<P::Rule>;
183

            
184
1530568
    fn node_name(&self, n: &Self::Node) -> P::AliasedRule {
185
1530568
        n.as_aliased_rule::<P>()
186
1530568
    }
187

            
188
    fn tag<'a>(&self, n: &'a Self::Node) -> Option<&'a str> {
189
        n.as_pair().as_node_tag()
190
    }
191

            
192
    fn error(self, message: String) -> Self::Error {
193
        // It is unclear to me how this works, but the reference actually allows us to call something from Node and removing it yields an error.
194
        #[allow(clippy::needless_borrow)]
195
        (&self).error(message)
196
    }
197
}