recursive

Function recursive 

Source
pub fn recursive<'a, K, O, F>(f: F) -> PRecursive<'a, K, O>
where K: PartialEq + 'a, O: 'a, F: FnOnce(PRecursive<'a, K, O>) -> Box<dyn ParserCore<'a, K, O> + 'a>,
Expand description

Creates a new recursive parser by providing a function f that receives a PRecursive instance and returns the actual parser implementation.

§Type Parameters

  • 'a - Lifetime of the input tokens.
  • K - The token type.
  • O - The output type of the parser.
  • F - A function that takes a recursive parser and returns a boxed parser.

§Arguments

  • f - A function that receives the recursive parser handle and returns a boxed parser that can recursively call itself.

§Returns

A PRecursive instance implementing ParserCore, which supports recursive parsing by delegating to the parser created inside f.

§Example

use cypress::prelude::*;
let input = b"(1+(2+3))".into_input();

#[derive(Debug, PartialEq, Clone)]
enum AST {
    Num(u32),
    Expr(Box<AST>, Box<AST>),
}

let parser = recursive(|expr| {
    Box::new(choice!(
        pnum().map(|a: u8| AST::Num((a - b'0').into())),
        just('(')
            .then(expr.clone())
            .then(just('+').then(expr))
            .then(just(')'))
            .map(|(((_, lhs), (_, rhs)), _)| AST::Expr(Box::new(lhs), Box::new(rhs)))
    ))
});

match parser.parse(input) {
    Ok(PSuccess { val, rest: _ }) => assert_eq!(
        val,
        AST::Expr(
            Box::new(AST::Num(1)),
            Box::new(AST::Expr(Box::new(AST::Num(2)), Box::new(AST::Num(3))))
        )
    ),
    Err(_) => assert!(false),
}
Examples found in repository?
examples/foo.rs (lines 90-101)
89fn expr<'a>() -> impl Parser<'a, u8, Expr> {
90    recursive(|expr| {
91        Box::new(
92            choice!(
93                var_assignment(expr.clone()),
94                print(expr),
95                ident().map(Expr::Var),
96                str_().map(Expr::Str),
97                num().map(Expr::Num)
98            )
99            .map(|expr| expr),
100        )
101    })
102}
More examples
Hide additional examples
examples/brainfuck.rs (lines 25-43)
24fn main() {
25    let parser = recursive(|expr| {
26        // Parser for a single instruction (not the whole program)
27        let instr = choice!(
28            select! {
29                '<' => Instruction::Left,
30                '>' => Instruction::Right,
31                '+' => Instruction::Increment,
32                '-' => Instruction::Decrement,
33                ',' => Instruction::Read,
34                '.' => Instruction::Write,
35            },
36            // Now inside Loop, parse *many instructions* again — not the full `expr`
37            expr.many()
38                .between(just('['), just(']'))
39                .map(Instruction::Loop)
40        );
41
42        Box::new(instr)
43    })
44    .many()
45    .until_end();
46
47    let input = b"+++++[>>+<<-]".into_input();
48
49    let expected_bf = vec![
50        Instruction::Increment,
51        Instruction::Increment,
52        Instruction::Increment,
53        Instruction::Increment,
54        Instruction::Increment,
55        Instruction::Loop(vec![
56            Instruction::Right,
57            Instruction::Right,
58            Instruction::Increment,
59            Instruction::Left,
60            Instruction::Left,
61            Instruction::Decrement,
62        ]),
63    ];
64
65    match parser.parse(input) {
66        Ok(PSuccess {
67            val: actual_bf,
68            rest: _,
69        }) => {
70            println!("{:?}", actual_bf);
71            assert_eq!(actual_bf, expected_bf)
72        }
73        Err(e) => println!("{}", e),
74    }
75}
examples/control.rs (lines 221-287)
220fn statement_parser<'a>() -> impl Parser<'a, u8, Statement> {
221    recursive(|stmt| {
222        let let_ = (pident("let").padded_by(pinlinews()))
223            .then(
224                choice!(pletter(), just('_'))
225                    .many1()
226                    .map(|xs| String::from_utf8(xs).unwrap()),
227            )
228            .padded_by(pinlinews())
229            .then(just('=').padded_by(pinlinews()).then(expr_parser()))
230            .map(|((_, name), (_, expr))| Statement::Let(name, expr));
231
232        let letless = choice!(pletter(), just('_'))
233            .many1()
234            .map(|xs| String::from_utf8(xs).unwrap())
235            .then(just('=').padded_by(pinlinews()))
236            .then(expr_parser())
237            .map(|((name, _), expr)| Statement::Let(name, expr));
238
239        let while_ = pident("while")
240            .then(expr_parser().padded_by(pinlinews()))
241            .then(pbetween(
242                just('{').padded_by(pws()),
243                (stmt.clone().padded_by(pinlinews()))
244                    .delimited_by(just('\n').many())
245                    .padded_by(pinlinews()),
246                just('}').padded_by(pws()),
247            ))
248            .map(|((_, cond), stmts)| Statement::While(cond, stmts));
249
250        let if_ = pident("if")
251            .then(expr_parser().padded_by(pinlinews()))
252            .then(pbetween(
253                just('{').padded_by(pws()),
254                (stmt.clone().padded_by(pinlinews()))
255                    .delimited_by(just('\n').many())
256                    .padded_by(pinlinews()),
257                just('}').padded_by(pws()),
258            ))
259            .map(|((_, cond), stmts)| Statement::If(cond, stmts, vec![]));
260
261        let if_else_ = pident("if")
262            .then(expr_parser().padded_by(pinlinews()))
263            .then(pbetween(
264                just('{').padded_by(pws()),
265                (stmt.clone().padded_by(pinlinews()))
266                    .delimited_by(just('\n').many())
267                    .padded_by(pinlinews()),
268                just('}').padded_by(pws()),
269            ))
270            .then(pident("else"))
271            .then(pbetween(
272                just('{').padded_by(pws()),
273                (stmt.clone().padded_by(pinlinews()))
274                    .delimited_by(just('\n').many())
275                    .padded_by(pinlinews()),
276                just('}').padded_by(pws()),
277            ))
278            .map(|((((_, cond), if_stmts), _), else_stmts)| {
279                Statement::If(cond, if_stmts, else_stmts)
280            });
281
282        let print_ = pident("print")
283            .then(pbetween(just('('), expr_parser(), just(')')))
284            .map(|(_, expr)| Statement::Print(expr));
285
286        Box::new(choice!(let_, letless, while_, if_else_, if_, print_))
287    })
288}