peg_macros/
translate.rs

1use proc_macro2::Delimiter;
2use proc_macro2::{Group, Ident, Literal, Span, TokenStream, TokenTree};
3use std::collections::{HashMap, HashSet};
4
5use quote::{format_ident, quote, quote_spanned};
6
7pub use self::Expr::*;
8use crate::analysis;
9use crate::ast::*;
10
11pub fn report_error(span: Span, msg: String) -> TokenStream {
12    quote_spanned!(span=>compile_error!(#msg);)
13}
14
15pub fn report_error_expr(span: Span, msg: String) -> TokenStream {
16    // panic!() to avoid "Mismatched types" error
17    quote_spanned!(span=> { compile_error!(#msg); panic!() })
18}
19
20/// Test if the group begins with a specific marker character, and if so, return the remaining tokens.
21fn group_check_prefix(group: &Group, prefix: char) -> Option<TokenStream> {
22    let mut iter = group.stream().into_iter();
23    match iter.next() {
24        Some(TokenTree::Punct(p)) if p.as_char() == prefix => Some(iter.collect()),
25        _ => None,
26    }
27}
28
29fn extra_args_def(grammar: &Grammar) -> TokenStream {
30    let args: Vec<TokenStream> = grammar
31        .args
32        .iter()
33        .map(|&(ref name, ref tp)| quote!(, #name: #tp))
34        .collect();
35    quote!(#(#args)*)
36}
37
38fn extra_args_call(grammar: &Grammar) -> TokenStream {
39    let args: Vec<TokenStream> = grammar
40        .args
41        .iter()
42        .map(|&(ref name, _)| quote!(, #name))
43        .collect();
44    quote!(#(#args)*)
45}
46
47#[derive(Clone)]
48struct Context<'a> {
49    rules: &'a HashMap<String, &'a Rule>,
50    rules_from_args: HashSet<String>,
51    grammar_lifetime_params: &'a [TokenStream],
52    input_ty: TokenStream,
53    parse_state_ty: TokenStream,
54    extra_args_call: TokenStream,
55    extra_args_def: TokenStream,
56}
57
58pub(crate) fn compile_grammar(grammar: &Grammar) -> TokenStream {
59    let analysis = analysis::check(grammar);
60
61    let grammar_lifetime_params = ty_params_slice(&grammar.lifetime_params);
62
63    let context = &Context {
64        rules: &analysis.rules,
65        rules_from_args: HashSet::new(),
66        grammar_lifetime_params,
67        input_ty: quote!(&'input Input<#(#grammar_lifetime_params),*>),
68        parse_state_ty: quote!(&mut ParseState<'input #(, #grammar_lifetime_params)*>),
69        extra_args_call: extra_args_call(grammar),
70        extra_args_def: extra_args_def(grammar),
71    };
72
73    let mut seen_rule_names = HashSet::new();
74
75    let mut items = vec![];
76    for item in &grammar.items {
77        match item {
78            Item::Use(tt) => items.push(tt.clone()),
79            Item::Rule(rule) => {
80                if !seen_rule_names.insert(rule.name.to_string()) {
81                    items.push(report_error(
82                        rule.name.span(),
83                        format!("duplicate rule `{}`", rule.name),
84                    ));
85                    continue;
86                }
87
88                if rule.cache.is_some() && !(rule.params.is_empty() && rule.ty_params.is_none()) {
89                    items.push(report_error(
90                            rule.name.span(),
91                            "rules with generics or parameters cannot use #[cache] or #[cache_left_rec]".to_string(),
92                    ));
93                    continue;
94                }
95
96                if rule.visibility.is_some() {
97                    for param in &rule.params {
98                        if let RuleParamTy::Rule(..) = &param.ty {
99                            items.push(report_error(
100                                param.name.span(),
101                                "parameters on `pub rule` must be Rust types".to_string(),
102                            ))
103                        }
104                    }
105
106                    items.push(compile_rule_export(context, rule));
107                } else if rule.no_eof {
108                    items.push(report_error(
109                        rule.name.span(),
110                        "#[no_eof] is only meaningful for `pub rule`".to_string(),
111                    ));
112                }
113
114                items.push(compile_rule(context, rule));
115            }
116        }
117    }
118
119    let parse_state = make_parse_state(grammar);
120    let Grammar {
121        name,
122        doc,
123        input_type,
124        visibility,
125        ..
126    } = grammar;
127
128    let mut errors: Vec<TokenStream> = analysis
129        .left_recursion
130        .iter()
131        .map(|rec| report_error(rec.span, rec.msg()))
132        .collect();
133
134    errors.extend(
135        analysis
136            .loop_nullability
137            .iter()
138            .map(|nl| report_error(nl.span, nl.msg())),
139    );
140
141    quote_spanned! { Span::mixed_site() =>
142        #doc
143        #visibility mod #name {
144            #[allow(unused_imports)]
145            use super::*;
146            type Input<#(#grammar_lifetime_params),*> = #input_type;
147            type PositionRepr<#(#grammar_lifetime_params),*> = <Input<#(#grammar_lifetime_params),*> as ::peg::Parse>::PositionRepr;
148
149            #(#errors)*
150            #parse_state
151            #(#items)*
152        }
153    }
154}
155
156fn make_parse_state(grammar: &Grammar) -> TokenStream {
157    let span = Span::mixed_site();
158    let grammar_lifetime_params = ty_params_slice(&grammar.lifetime_params);
159    let mut cache_fields_def: Vec<TokenStream> = Vec::new();
160    let mut cache_fields: Vec<Ident> = Vec::new();
161    for rule in grammar.iter_rules() {
162        if rule.cache.is_some() && rule.params.is_empty() && rule.ty_params.is_none() {
163            let name = format_ident!("{}_cache", rule.name);
164            let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(()));
165            cache_fields_def.push(
166                quote_spanned! { span =>  #name: ::std::collections::HashMap<usize, ::peg::RuleResult<#ret_ty>> },
167            );
168            cache_fields.push(name);
169        }
170    }
171
172    quote_spanned! { span =>
173        #[allow(unused_parens)]
174        struct ParseState<'input #(, #grammar_lifetime_params)*> {
175            _phantom: ::core::marker::PhantomData<(&'input () #(, &#grammar_lifetime_params ())*)>,
176            #(#cache_fields_def),*
177        }
178
179        impl<'input #(, #grammar_lifetime_params)*> ParseState<'input #(, #grammar_lifetime_params)*> {
180            fn new() -> ParseState<'input #(, #grammar_lifetime_params)*> {
181                ParseState {
182                    _phantom: ::core::marker::PhantomData,
183                    #(#cache_fields: ::std::collections::HashMap::new()),*
184                }
185            }
186        }
187    }
188}
189
190fn ty_params_slice(ty_params: &Option<Vec<TokenStream>>) -> &[TokenStream] {
191    ty_params.as_ref().map(|x| &x[..]).unwrap_or(&[])
192}
193
194fn rule_params_list(context: &Context, rule: &Rule) -> Vec<TokenStream> {
195    let Context {
196        input_ty,
197        parse_state_ty,
198        ..
199    } = context;
200    let span = rule.span.resolved_at(Span::mixed_site());
201    rule.params.iter().map(|param| {
202        let name = &param.name;
203        match &param.ty {
204            RuleParamTy::Rust(ty) => quote_spanned!{ span => #name: #ty },
205            RuleParamTy::Rule(ty) => quote_spanned!{ span =>
206                #name: impl Fn(#input_ty, #parse_state_ty, &mut ::peg::error::ErrorState, usize) -> ::peg::RuleResult<#ty>
207            },
208        }
209    }).collect()
210}
211
212/// Compile a rule to a function for use internal to the grammar.
213/// Returns `RuleResult<T>`.
214fn compile_rule(context: &Context, rule: &Rule) -> TokenStream {
215    let span = rule.span.resolved_at(Span::mixed_site());
216    let name = format_ident!("__parse_{}", rule.name, span = span);
217    let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(()));
218    let ty_params = ty_params_slice(&rule.ty_params);
219    let where_clause = rule.where_clause.as_ref().into_iter();
220
221    let Context {
222        input_ty,
223        parse_state_ty,
224        grammar_lifetime_params,
225        extra_args_def,
226        ..
227    } = context;
228
229    let mut context = context.clone();
230    context
231        .rules_from_args
232        .extend(rule.params.iter().map(|param| param.name.to_string()));
233
234    let body = compile_expr(&context, &rule.expr, rule.ret_type.is_some());
235
236    let wrapped_body = if cfg!(feature = "trace") {
237        let str_rule_name = rule.name.to_string();
238        quote_spanned! { span => {
239            let loc = ::peg::Parse::position_repr(__input, __pos);
240            println!("[PEG_TRACE] Attempting to match rule `{}` at {}", #str_rule_name, loc);
241            let __peg_result: ::peg::RuleResult<#ret_ty> = {#body};
242            match __peg_result {
243                ::peg::RuleResult::Matched(epos, _) => {
244                    let eloc = ::peg::Parse::position_repr(__input, epos);
245                    println!("[PEG_TRACE] Matched rule `{}` at {} to {}", #str_rule_name, loc, eloc);
246                }
247                ::peg::RuleResult::Failed => {
248                    println!("[PEG_TRACE] Failed to match rule `{}` at {}", #str_rule_name, loc);
249                }
250            }
251
252            __peg_result
253        }}
254    } else {
255        body
256    };
257
258    let rule_params = rule_params_list(&context, rule);
259
260    let fn_body = match &rule.cache {
261        None => wrapped_body,
262        Some(cache_type) => {
263            let cache_field = format_ident!("{}_cache", rule.name);
264
265            let cache_trace = if cfg!(feature = "trace") {
266                let str_rule_name = rule.name.to_string();
267                quote_spanned! { span =>
268                    let loc = ::peg::Parse::position_repr(__input, __pos);
269                    match &entry {
270                        &::peg::RuleResult::Matched(..) => println!("[PEG_TRACE] Cached match of rule {} at {}", #str_rule_name, loc),
271                        &Failed => println!("[PEG_TRACE] Cached fail of rule {} at {}", #str_rule_name, loc),
272                    };
273                }
274            } else {
275                quote!()
276            };
277
278            match cache_type {
279                Cache::Simple => quote_spanned! { span =>
280                    if let Some(entry) = __state.#cache_field.get(&__pos) {
281                        #cache_trace
282                        return entry.clone();
283                    }
284
285                    let __rule_result = #wrapped_body;
286                    __state.#cache_field.insert(__pos, __rule_result.clone());
287                    __rule_result
288                },
289                Cache::Recursive =>
290                // `#[cache_left_rec] support for recursive rules using the technique described here:
291                // <https://medium.com/@gvanrossum_83706/left-recursive-peg-grammars-65dab3c580e1>
292                {
293                    quote_spanned! { span =>
294                        if let Some(entry) = __state.#cache_field.get(&__pos) {
295                            #cache_trace
296                            return entry.clone();
297                        }
298
299                        __state.#cache_field.insert(__pos, ::peg::RuleResult::Failed);
300                        let mut __last_result = ::peg::RuleResult::Failed;
301                        loop {
302                            let __current_result = { #wrapped_body };
303                            match __current_result {
304                                ::peg::RuleResult::Failed => break,
305                                ::peg::RuleResult::Matched(__current_endpos, _) =>
306                                    match __last_result {
307                                        ::peg::RuleResult::Matched(__last_endpos, _) if __current_endpos <= __last_endpos => break,
308                                        _ => {
309                                            __state.#cache_field.insert(__pos, __current_result.clone());
310                                            __last_result = __current_result;
311                                        },
312                                    }
313                            }
314                        }
315
316                        return __last_result;
317                    }
318                }
319            }
320        }
321    };
322
323    quote_spanned! { span =>
324        fn #name<'input #(, #grammar_lifetime_params)* #(, #ty_params)*>(
325            __input: #input_ty,
326            __state: #parse_state_ty,
327            __err_state: &mut ::peg::error::ErrorState,
328            __pos: usize #extra_args_def #(, #rule_params)*,
329        ) -> ::peg::RuleResult<#ret_ty>
330        #(#where_clause)*
331        {
332            #![allow(non_snake_case, unused, clippy::redundant_closure_call)]
333            #fn_body
334        }
335    }
336}
337
338/// Compile a rule into the parsing function which will be exported.
339/// Returns `Result<T, ParseError>`.
340fn compile_rule_export(context: &Context, rule: &Rule) -> TokenStream {
341    let span = rule.span.resolved_at(Span::mixed_site());
342
343    let Rule {
344        doc,
345        name,
346        visibility,
347        ..
348    } = rule;
349    let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(()));
350    let parse_fn = format_ident!("__parse_{}", rule.name, span = name.span());
351    let ty_params = ty_params_slice(&rule.ty_params);
352    let where_clause = rule.where_clause.as_ref().into_iter();
353    let rule_params = rule_params_list(context, rule);
354    let rule_params_call: Vec<TokenStream> = rule
355        .params
356        .iter()
357        .map(|param| {
358            let param_name = &param.name;
359            quote!(#param_name)
360        })
361        .collect();
362
363    let Context {
364        input_ty,
365        extra_args_call,
366        extra_args_def,
367        grammar_lifetime_params,
368        ..
369    } = context;
370    let eof_check = if rule.no_eof {
371        quote_spanned! { span => true }
372    } else {
373        quote_spanned! { span => ::peg::Parse::is_eof(__input, __pos) }
374    };
375
376    // Parse once. If it succeeds or throws an error, return that.
377    // If it fails, parse again to determine the set of all tokens
378    // that were expected at the failure position.
379
380    quote_spanned! { span =>
381        #doc
382        #visibility fn #name<'input #(, #grammar_lifetime_params)* #(, #ty_params)*>(
383            __input: #input_ty #extra_args_def #(, #rule_params)*
384        ) -> ::core::result::Result<
385            #ret_ty,
386            ::peg::error::ParseError<PositionRepr<#(#grammar_lifetime_params),*>>
387        >
388        #(#where_clause)*
389        {
390            #![allow(non_snake_case, unused)]
391
392            let mut __err_state = ::peg::error::ErrorState::new(::peg::Parse::start(__input));
393            let mut __state = ParseState::new();
394            match #parse_fn(__input, &mut __state, &mut __err_state, ::peg::Parse::start(__input) #extra_args_call #(, #rule_params_call)*) {
395                ::peg::RuleResult::Matched(__pos, __value) => {
396                    if #eof_check {
397                        return Ok(__value)
398                    } else {
399                        __err_state.mark_failure(__pos, "EOF");
400                    }
401                }
402                _ => ()
403            }
404
405            __state = ParseState::new();
406            __err_state.reparse_for_error();
407
408            match #parse_fn(__input, &mut __state, &mut __err_state, ::peg::Parse::start(__input) #extra_args_call #(, #rule_params_call)*) {
409                ::peg::RuleResult::Matched(__pos, __value) => {
410                    if #eof_check {
411                        panic!("Parser is nondeterministic: succeeded when reparsing for error position");
412                        return Ok(__value); // dead code, but needed for type inference
413                    } else {
414                        __err_state.mark_failure(__pos, "EOF");
415                    }
416                }
417                _ => ()
418            }
419
420            Err(__err_state.into_parse_error(__input))
421        }
422    }
423}
424
425fn name_or_ignore(n: Option<&Ident>) -> TokenStream {
426    match n {
427        Some(n) => quote!(#n),
428        None => quote!(_),
429    }
430}
431
432fn ordered_choice(span: Span, mut rs: impl DoubleEndedIterator<Item = TokenStream>) -> TokenStream {
433    rs.next_back().map(|last| rs.rfold(last, |fallback, preferred| {
434        quote_spanned! { span => {
435            let __choice_res = #preferred;
436            match __choice_res {
437                ::peg::RuleResult::Matched(__pos, __value) => ::peg::RuleResult::Matched(__pos, __value),
438                ::peg::RuleResult::Failed => #fallback
439            }
440        }}
441    })).expect("ordered choice must not be empty")
442}
443
444fn labeled_seq(context: &Context, exprs: &[TaggedExpr], inner: TokenStream) -> TokenStream {
445    exprs.iter().rfold(inner, |then, expr| {
446        compile_expr_continuation(context, &expr.expr, expr.name.as_ref(), then)
447    })
448}
449
450fn compile_expr_continuation(
451    context: &Context,
452    e: &SpannedExpr,
453    result_name: Option<&Ident>,
454    continuation: TokenStream,
455) -> TokenStream {
456    let span = e.span.resolved_at(Span::mixed_site());
457
458    let result_pat = name_or_ignore(result_name);
459    match e.expr {
460        LiteralExpr(ref s) => compile_literal_expr(s, continuation),
461
462        PatternExpr(ref pattern) => {
463            let result_name = result_name
464                .cloned()
465                .unwrap_or_else(|| Ident::new("__ch", span));
466            compile_pattern_expr(
467                pattern,
468                result_name,
469                quote_spanned! { span =>
470                    { let __pos = __next; { #continuation } }
471                },
472            )
473        }
474
475        _ => {
476            let seq_res = compile_expr(context, e, result_name.is_some());
477            quote_spanned! { span => {
478                let __seq_res = #seq_res;
479                match __seq_res {
480                    ::peg::RuleResult::Matched(__pos, #result_pat) => { #continuation }
481                    ::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
482                }
483            }}
484        }
485    }
486}
487
488fn compile_literal_expr(s: &Literal, continuation: TokenStream) -> TokenStream {
489    let span = s.span().resolved_at(Span::mixed_site());
490    let escaped_str = s.to_string();
491    quote_spanned! { span =>
492            match ::peg::ParseLiteral::parse_string_literal(__input, __pos, #s) {
493            ::peg::RuleResult::Matched(__pos, __val) => { #continuation }
494            ::peg::RuleResult::Failed => { __err_state.mark_failure(__pos, #escaped_str); ::peg::RuleResult::Failed }
495        }
496    }
497}
498
499fn compile_pattern_expr(
500    pattern_group: &Group,
501    result_name: Ident,
502    success_res: TokenStream,
503) -> TokenStream {
504    let span = pattern_group.span().resolved_at(Span::mixed_site());
505    let pat_str = pattern_group.to_string();
506    let failure_res = quote_spanned! { span => { __err_state.mark_failure(__pos, #pat_str); ::peg::RuleResult::Failed } };
507
508    let (pattern, in_set, not_in_set) =
509        if let Some(pattern) = group_check_prefix(pattern_group, '^') {
510            (pattern, failure_res, success_res)
511        } else {
512            (pattern_group.stream(), success_res, failure_res)
513        };
514
515    let pattern = Group::new(Delimiter::None, pattern);
516
517    quote_spanned! { span =>
518        match ::peg::ParseElem::parse_elem(__input, __pos) {
519            ::peg::RuleResult::Matched(__next, #result_name) => match #result_name {
520                #pattern => #in_set,
521                _ => #not_in_set,
522            }
523            ::peg::RuleResult::Failed => { __err_state.mark_failure(__pos, #pat_str); ::peg::RuleResult::Failed }
524        }
525    }
526}
527
528fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenStream {
529    let span = e.span.resolved_at(Span::mixed_site());
530
531    match e.expr {
532        LiteralExpr(ref s) => compile_literal_expr(
533            s,
534            quote_spanned! { span =>
535                 ::peg::RuleResult::Matched(__pos, __val)
536            },
537        ),
538
539        PatternExpr(ref pattern_group) => {
540            let res_name = Ident::new("__ch", span);
541            let res = if result_used {
542                quote!(#res_name)
543            } else {
544                quote_spanned! { span => () }
545            };
546            compile_pattern_expr(
547                pattern_group,
548                res_name,
549                quote_spanned! { span =>
550                    ::peg::RuleResult::Matched(__next, #res)
551                },
552            )
553        }
554
555        RuleExpr(ref rule_name, ref generics, ref rule_args)
556            if context.rules_from_args.contains(&rule_name.to_string()) =>
557        {
558            if !rule_args.is_empty() {
559                return report_error_expr(
560                    rule_name.span(),
561                    "rule closure does not accept arguments".to_string(),
562                );
563            }
564
565            if generics.is_some() {
566                return report_error_expr(
567                    rule_name.span(),
568                    "rule closure cannot have generics".to_string()
569                );
570            }
571
572            quote_spanned! { span=> #rule_name(__input, __state, __err_state, __pos) }
573        }
574
575        RuleExpr(ref rule_name, ref generics, ref rule_args) => {
576            let rule_name_str = rule_name.to_string();
577
578            let rule_def = if let Some(rule_def) = context.rules.get(&rule_name_str) {
579                rule_def
580            } else {
581                return report_error_expr(
582                    rule_name.span(),
583                    format!("undefined rule `{}`", rule_name_str),
584                );
585            };
586
587            if result_used && rule_def.ret_type.is_none() {
588                let msg = format!(
589                    "using result of rule `{}`, which does not return a value",
590                    rule_name_str
591                );
592                return report_error_expr(rule_name.span(), msg);
593            }
594
595            if rule_def.params.len() != rule_args.len() {
596                return report_error_expr(
597                    rule_name.span(),
598                    format!(
599                        "this rule takes {} parameters but {} parameters were supplied",
600                        rule_def.params.len(),
601                        rule_args.len()
602                    ),
603                );
604            }
605
606            let func = format_ident!("__parse_{}", rule_name, span = rule_name.span());
607            let extra_args_call = &context.extra_args_call;
608
609            let rule_args_call: Vec<TokenStream> = rule_args
610                .iter()
611                .map(|arg| match arg {
612                    RuleArg::Peg(e) => {
613                        let expr = compile_expr(context, e, true);
614                        quote_spanned! { span=> |__input, __state, __err_state, __pos| { #expr } }
615                    }
616                    RuleArg::Rust(e) => e.clone(),
617                })
618                .collect();
619
620            if result_used {
621                quote_spanned! { span=> #func #generics (__input, __state, __err_state, __pos #extra_args_call #(, #rule_args_call)*) }
622            } else {
623                quote_spanned! { span=>
624                    match #func #generics (__input, __state, __err_state, __pos #extra_args_call #(, #rule_args_call)*){
625                        ::peg::RuleResult::Matched(pos, _) => ::peg::RuleResult::Matched(pos, ()),
626                        ::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
627                    }
628                }
629            }
630        }
631
632        MethodExpr(ref method, ref args) => {
633            quote_spanned! { span=> __input.#method(__pos, #args) }
634        }
635
636        CustomExpr(ref code) => {
637            let code = code.stream();
638            quote_spanned! { span=> ::peg::call_custom_closure((#code), __input, __pos) }
639        }
640
641        ChoiceExpr(ref exprs) => ordered_choice(
642            span,
643            exprs
644                .iter()
645                .map(|expr| compile_expr(context, expr, result_used)),
646        ),
647
648        OptionalExpr(ref e) => {
649            let optional_res = compile_expr(context, e, result_used);
650
651            if result_used {
652                quote_spanned! { span=>
653                    match #optional_res {
654                        ::peg::RuleResult::Matched(__newpos, __value) => { ::peg::RuleResult::Matched(__newpos, Some(__value)) },
655                        ::peg::RuleResult::Failed => { ::peg::RuleResult::Matched(__pos, None) },
656                    }
657                }
658            } else {
659                quote_spanned! { span=>
660                    match #optional_res {
661                        ::peg::RuleResult::Matched(__newpos, _) => { ::peg::RuleResult::Matched(__newpos, ()) },
662                        ::peg::RuleResult::Failed => { ::peg::RuleResult::Matched(__pos, ()) },
663                    }
664                }
665            }
666        }
667
668        Repeat {
669            ref inner,
670            ref bound,
671            ref sep,
672        } => {
673            let inner = compile_expr(context, inner, result_used);
674
675            let (min, max) = match bound {
676                BoundedRepeat::None => (None, None),
677                BoundedRepeat::Plus => (Some(quote!(1)), None),
678                BoundedRepeat::Exact(ref code) => (Some(code.clone()), Some(code.clone())),
679                BoundedRepeat::Both(ref min, ref max) => (min.clone(), max.clone()),
680            };
681
682            let match_sep = if let Some(sep) = sep {
683                let sep_inner = compile_expr(context, sep, false);
684                quote_spanned! { span=>
685                    let __pos = if __repeat_value.is_empty() { __pos } else {
686                        let __sep_res = #sep_inner;
687                        match __sep_res {
688                            ::peg::RuleResult::Matched(__newpos, _) => { __newpos },
689                            ::peg::RuleResult::Failed => break,
690                        }
691                    };
692                }
693            } else {
694                quote!()
695            };
696
697            let result = if result_used {
698                quote_spanned! { span=> __repeat_value }
699            } else {
700                quote!(())
701            };
702
703            let (repeat_vec, repeat_step) =
704                if result_used || min.is_some() || max.is_some() || sep.is_some() {
705                    (
706                        Some(quote_spanned! { span => let mut __repeat_value = vec!(); }),
707                        Some(quote_spanned! { span => __repeat_value.push(__value); }),
708                    )
709                } else {
710                    (None, None)
711                };
712
713            let max_check = max.map(|max| {
714                quote_spanned! { span=> if __repeat_value.len() >= #max { break } }
715            });
716
717            let result_check = if let Some(min) = min {
718                quote_spanned! { span=>
719                    if __repeat_value.len() >= #min {
720                        ::peg::RuleResult::Matched(__repeat_pos, #result)
721                    } else {
722                        ::peg::RuleResult::Failed
723                    }
724                }
725            } else {
726                quote_spanned! { span=> ::peg::RuleResult::Matched(__repeat_pos, #result) }
727            };
728
729            quote_spanned! { span=> {
730                let mut __repeat_pos = __pos;
731                #repeat_vec
732
733                loop {
734                    let __pos = __repeat_pos;
735
736                    #match_sep
737                    #max_check
738
739                    let __step_res = #inner;
740                    match __step_res {
741                        ::peg::RuleResult::Matched(__newpos, __value) => {
742                            __repeat_pos = __newpos;
743                            #repeat_step
744                        },
745                        ::peg::RuleResult::Failed => {
746                            break;
747                        }
748                    }
749                }
750
751                #result_check
752            }}
753        }
754
755        PosAssertExpr(ref e) => {
756            let assert_res = compile_expr(context, e, result_used);
757            quote_spanned! { span=> {
758                __err_state.suppress_fail += 1;
759                let __assert_res = #assert_res;
760                __err_state.suppress_fail -= 1;
761                match __assert_res {
762                    ::peg::RuleResult::Matched(_, __value) => ::peg::RuleResult::Matched(__pos, __value),
763                    ::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
764                }
765            }}
766        }
767
768        NegAssertExpr(ref e) => {
769            let assert_res = compile_expr(context, e, false);
770            quote_spanned! { span=> {
771                __err_state.suppress_fail += 1;
772                let __assert_res = #assert_res;
773                __err_state.suppress_fail -= 1;
774                match __assert_res {
775                    ::peg::RuleResult::Failed => ::peg::RuleResult::Matched(__pos, ()),
776                    ::peg::RuleResult::Matched(..) => ::peg::RuleResult::Failed,
777                }
778            }}
779        }
780
781        ActionExpr(ref exprs, ref code) => labeled_seq(context, exprs, {
782            if let Some(code) = code {
783                let code_span = code.span().resolved_at(Span::mixed_site());
784
785                // Peek and see if the first token in the block is '?'. If so, it's a conditional block
786                if let Some(body) = group_check_prefix(code, '?') {
787                    quote_spanned! {code_span =>
788                        match (||{ #body })() {
789                            Ok(res) => ::peg::RuleResult::Matched(__pos, res),
790                            Err(expected) => {
791                                __err_state.mark_failure(__pos, expected);
792                                ::peg::RuleResult::Failed
793                            },
794                        }
795                    }
796                } else {
797                    quote_spanned! {code_span => ::peg::RuleResult::Matched(__pos, (||#code)()) }
798                }
799            } else {
800                quote_spanned! { span => ::peg::RuleResult::Matched(__pos, ()) }
801            }
802        }),
803        MatchStrExpr(ref expr) => {
804            let inner = compile_expr(context, expr, false);
805            quote_spanned! { span => {
806                let str_start = __pos;
807                match #inner {
808                    ::peg::RuleResult::Matched(__newpos, _) => { ::peg::RuleResult::Matched(__newpos, ::peg::ParseSlice::parse_slice(__input, str_start, __newpos)) },
809                    ::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
810                }
811            }}
812        }
813        PositionExpr => {
814            quote_spanned! { span => ::peg::RuleResult::Matched(__pos, __pos) }
815        }
816        QuietExpr(ref expr) => {
817            let inner = compile_expr(context, expr, result_used);
818            quote_spanned! { span => {
819                __err_state.suppress_fail += 1;
820                let res = #inner;
821                __err_state.suppress_fail -= 1;
822                res
823            }}
824        }
825        FailExpr(ref expected) => {
826            quote_spanned! { span => { __err_state.mark_failure(__pos, #expected); ::peg::RuleResult::Failed }}
827        }
828
829        PrecedenceExpr { ref levels } => {
830            let mut pre_rules = Vec::new();
831            let mut level_code = Vec::new();
832            let mut span_capture: Option<(TokenStream, TokenStream, TokenStream, &Group)> = None;
833
834            for (prec, level) in levels.iter().enumerate() {
835                let prec = prec as i32;
836
837                let mut post_rules = Vec::new();
838
839                for op in &level.operators {
840                    let op_span = op.span.resolved_at(Span::mixed_site());
841
842                    if op.elements.is_empty() {
843                        return report_error(op_span, "incomplete rule".to_string());
844                    }
845
846                    let left_arg = &op.elements[0];
847                    let l_arg = name_or_ignore(left_arg.name.as_ref());
848
849                    let right_arg = &op.elements[op.elements.len() - 1];
850                    let r_arg = name_or_ignore(right_arg.name.as_ref());
851
852                    let action = &op.action;
853                    let action = quote_spanned!(op.action.span()=>(||#action)());
854
855                    let action = if let Some((lpos_name, val_name, rpos_name, wrap_action)) =
856                        &span_capture
857                    {
858                        let wrap_action_span = wrap_action.span().resolved_at(Span::mixed_site());
859                        quote_spanned!(wrap_action_span => (|#lpos_name, #val_name, #rpos_name|#wrap_action)(__lpos, #action, __pos))
860                    } else {
861                        action
862                    };
863
864                    match (&left_arg.expr.expr, &right_arg.expr.expr) {
865                        (&PositionExpr, &PositionExpr) if op.elements.len() == 3 => {
866                            // wrapper rule to capture expression span
867                            match &op.elements[1].expr.expr {
868                                &MarkerExpr(..) => (),
869                                _ => {
870                                    return report_error(op_span, "span capture rule must be `l:position!() n:@ r:position!()".to_string());
871                                }
872                            }
873
874                            span_capture = Some((
875                                name_or_ignore(op.elements[0].name.as_ref()),
876                                name_or_ignore(op.elements[1].name.as_ref()),
877                                name_or_ignore(op.elements[2].name.as_ref()),
878                                &op.action,
879                            ));
880                        }
881                        (&MarkerExpr(la), &MarkerExpr(ra)) if op.elements.len() >= 3 => {
882                            //infix
883                            let new_prec = match (la, ra) {
884                                (true, false) => prec + 1, // left associative
885                                (false, true) => prec,     // right associative
886                                _ => return report_error(op_span, "precedence rules must use `@` and `(@)` to indicate associativity".to_string())
887                            };
888
889                            post_rules.push(
890                                labeled_seq(context, &op.elements[1..op.elements.len()-1], {
891                                    quote_spanned!{ op_span =>
892                                        if let ::peg::RuleResult::Matched(__pos, #r_arg) = __recurse(__pos, #new_prec, __state, __err_state) {
893                                            let #l_arg = __infix_result;
894                                            __infix_result = #action;
895                                            ::peg::RuleResult::Matched(__pos, ())
896                                        } else { ::peg::RuleResult::Failed }
897                                    }
898                                })
899                            );
900                        }
901                        (&MarkerExpr(_), _) if op.elements.len() >= 2 => {
902                            // postfix
903                            post_rules.push(labeled_seq(
904                                context,
905                                &op.elements[1..op.elements.len()],
906                                {
907                                    quote_spanned! { op_span =>
908                                        let #l_arg = __infix_result;
909                                        __infix_result = #action;
910                                        ::peg::RuleResult::Matched(__pos, ())
911                                    }
912                                },
913                            ));
914                        }
915                        (_, &MarkerExpr(a)) if op.elements.len() >= 2 => {
916                            // prefix
917                            let new_prec = match a {
918                                true => prec,
919                                false => prec + 1,
920                            };
921                            pre_rules.push(
922                                labeled_seq(context, &op.elements[..op.elements.len()-1], {
923                                    quote_spanned!{ op_span =>
924                                        if let ::peg::RuleResult::Matched(__pos, #r_arg) = __recurse(__pos, #new_prec, __state, __err_state) {
925                                            ::peg::RuleResult::Matched(__pos, #action)
926                                        } else { ::peg::RuleResult::Failed }
927                                    }
928                                })
929                            );
930                        }
931                        _ => {
932                            // atom
933                            pre_rules.push(labeled_seq(context, &op.elements, {
934                                quote_spanned! { op_span => ::peg::RuleResult::Matched(__pos, #action) }
935                            }));
936                        }
937                    };
938                }
939
940                if !post_rules.is_empty() {
941                    level_code.push(quote_spanned! { span =>
942                        if #prec >= __min_prec {
943                            #(
944                                if let ::peg::RuleResult::Matched(__pos, ()) = #post_rules {
945                                    return (__infix_result, ::peg::RuleResult::Matched(__pos, ()));
946                                }
947                            )*
948                        }
949                    });
950                }
951            }
952
953            let (enter, leave) = if cfg!(feature = "trace") {
954                (
955                    quote_spanned! {span => println!("[PEG_TRACE] Entering level {}", min_prec);},
956                    quote_spanned! {span => println!("[PEG_TRACE] Leaving level {}", min_prec);},
957                )
958            } else {
959                (quote!(), quote!())
960            };
961
962            // The closures below must be defined within the function call to which they are passed
963            // due to https://github.com/rust-lang/rust/issues/41078
964
965            quote_spanned! { span => {
966                fn __infix_parse<T, S>(
967                    state: &mut S,
968                    err_state: &mut ::peg::error::ErrorState,
969                    min_prec: i32,
970                    lpos: usize,
971                    prefix_atom: &dyn Fn(usize, &mut S, &mut ::peg::error::ErrorState, &dyn Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<T>) -> ::peg::RuleResult<T>,
972                    level_code: &dyn Fn(usize, usize, i32, T, &mut S, &mut ::peg::error::ErrorState, &dyn Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<T>) -> (T, ::peg::RuleResult<()>),
973                ) -> ::peg::RuleResult<T> {
974                    let initial = {
975                        prefix_atom(lpos, state, err_state, &|pos, min_prec, state, err_state| {
976                            __infix_parse(state, err_state, min_prec, pos, prefix_atom, level_code)
977                        })
978                    };
979
980                    if let ::peg::RuleResult::Matched(pos, mut infix_result) = initial {
981                        #enter
982                        let mut repeat_pos = pos;
983                        loop {
984                            let (val, res) = level_code(
985                                repeat_pos,
986                                lpos,
987                                min_prec,
988                                infix_result,
989                                state,
990                                err_state,
991                                &|pos, min_prec, state, err_state| {
992                                    __infix_parse(state, err_state, min_prec, pos, prefix_atom, level_code)
993                                }
994                            );
995                            infix_result = val;
996
997                            if let ::peg::RuleResult::Matched(pos, ()) = res {
998                                repeat_pos = pos;
999                                continue;
1000                            }
1001
1002                            break;
1003                        }
1004                        #leave
1005                        ::peg::RuleResult::Matched(repeat_pos, infix_result)
1006                    } else {
1007                        ::peg::RuleResult::Failed
1008                    }
1009                }
1010
1011                __infix_parse(__state, __err_state, 0, __pos,
1012                    &|__pos, __state, __err_state, __recurse| {
1013                        let __lpos = __pos;
1014                        #(
1015                            if let ::peg::RuleResult::Matched(__pos, __v) = #pre_rules {
1016                                return ::peg::RuleResult::Matched(__pos, __v);
1017                            }
1018                        )*
1019
1020                        ::peg::RuleResult::Failed
1021                    },
1022                    &|__pos, __lpos, __min_prec, mut __infix_result, __state, __err_state, __recurse| {
1023                        #(#level_code)*
1024                        (__infix_result, ::peg::RuleResult::Failed)
1025                    }
1026                )
1027            }}
1028        }
1029        MarkerExpr { .. } => {
1030            report_error(span, "`@` is only allowed in `precedence!{}`".to_string())
1031        }
1032    }
1033}