@@ -52,21 +52,26 @@ struct Context<'a> {
5252 parse_state_ty : TokenStream ,
5353 extra_args_call : TokenStream ,
5454 extra_args_def : TokenStream ,
55+ injected_vars : TokenStream ,
5556}
5657
5758pub ( crate ) fn compile_grammar ( grammar : & Grammar ) -> TokenStream {
5859 let analysis = analysis:: check ( grammar) ;
5960
6061 let grammar_lifetime_params = ty_params_slice ( & grammar. lifetime_params ) ;
62+ let extra_args_def = extra_args_def ( grammar) ;
63+ let extra_args_call = extra_args_call ( grammar) ;
64+ let injected_vars = invoke_injected_vars ( grammar, & extra_args_call) ;
6165
6266 let context = & Context {
6367 rules : & analysis. rules ,
6468 rules_from_args : HashSet :: new ( ) ,
6569 grammar_lifetime_params,
6670 input_ty : quote ! ( & ' input Input <#( #grammar_lifetime_params) , * >) ,
6771 parse_state_ty : quote ! ( & mut ParseState <' input #( , #grammar_lifetime_params) * >) ,
68- extra_args_call : extra_args_call ( grammar) ,
69- extra_args_def : extra_args_def ( grammar) ,
72+ extra_args_call,
73+ extra_args_def,
74+ injected_vars,
7075 } ;
7176
7277 let mut seen_rule_names = HashSet :: new ( ) ;
@@ -75,6 +80,7 @@ pub(crate) fn compile_grammar(grammar: &Grammar) -> TokenStream {
7580 for item in & grammar. items {
7681 match item {
7782 Item :: Use ( tt) => items. push ( tt. clone ( ) ) ,
83+ Item :: InjectVar ( var) => items. push ( compile_inject_func ( context, var) ) ,
7884 Item :: Rule ( rule) => {
7985 if !seen_rule_names. insert ( rule. name . to_string ( ) ) {
8086 items. push ( report_error (
@@ -208,6 +214,61 @@ fn rule_params_list(context: &Context, rule: &Rule) -> Vec<TokenStream> {
208214 } ) . collect ( )
209215}
210216
217+ fn compile_inject_func ( context : & Context , var : & InjectVar ) -> TokenStream {
218+ let span = var. name . span ( ) . resolved_at ( Span :: mixed_site ( ) ) ;
219+
220+ let InjectVar {
221+ doc,
222+ name,
223+ input_param,
224+ lpos_param,
225+ rpos_param,
226+ ty,
227+ body,
228+ } = var;
229+
230+ let name = format_ident ! ( "__inject_{}" , name, span = span) ;
231+
232+ let Context {
233+ input_ty,
234+ grammar_lifetime_params,
235+ extra_args_def,
236+ ..
237+ } = context;
238+
239+ quote_spanned ! { span =>
240+ #doc
241+ fn #name<' input #( , #grammar_lifetime_params) * >(
242+ #input_param: #input_ty,
243+ #lpos_param: usize ,
244+ #rpos_param: usize
245+ #extra_args_def
246+ ) -> #ty #body
247+ }
248+ }
249+
250+ fn invoke_injected_vars ( grammar : & Grammar , extra_args_call : & TokenStream ) -> TokenStream {
251+ let vars = grammar
252+ . items
253+ . iter ( )
254+ . filter_map ( |item| match item {
255+ Item :: InjectVar ( var) => Some ( var) ,
256+ _ => None ,
257+ } )
258+ . map ( |var| {
259+ let name = & var. name ;
260+ let name_fn = format_ident ! ( "__inject_{}" , var. name) ;
261+ let span = var. name . span ( ) . resolved_at ( Span :: mixed_site ( ) ) ;
262+ quote_spanned ! { span =>
263+ #[ allow( unused) ]
264+ let #name = #name_fn( __input, __lpos, __pos #extra_args_call) ;
265+ }
266+ } )
267+ . collect :: < Vec < _ > > ( ) ;
268+
269+ quote ! ( #( #vars) * )
270+ }
271+
211272/// Compile a rule to a function for use internal to the grammar.
212273/// Returns `RuleResult<T>`.
213274fn compile_rule ( context : & Context , rule : & Rule ) -> TokenStream {
@@ -777,28 +838,44 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
777838 } }
778839 }
779840
780- Expr :: Action ( ref exprs, ref code) => labeled_seq ( context, exprs, {
781- if let Some ( code) = code {
782- let code_span = code. span ( ) . resolved_at ( Span :: mixed_site ( ) ) ;
783-
784- // Peek and see if the first token in the block is '?'. If so, it's a conditional block
785- if let Some ( body) = group_check_prefix ( code, '?' ) {
786- quote_spanned ! { code_span =>
787- match ( ||{ #body } ) ( ) {
788- Ok ( res) => :: peg:: RuleResult :: Matched ( __pos, res) ,
789- Err ( expected) => {
790- __err_state. mark_failure( __pos, expected) ;
791- :: peg:: RuleResult :: Failed
792- } ,
841+ Expr :: Action ( ref exprs, ref code) => {
842+ let seq = labeled_seq ( context, exprs, {
843+ if let Some ( code) = code {
844+ let injected_vars = & context. injected_vars ;
845+ let code_span = code. span ( ) . resolved_at ( Span :: mixed_site ( ) ) ;
846+
847+ // Peek and see if the first token in the block is '?'. If so, it's a conditional block
848+ if let Some ( body) = group_check_prefix ( code, '?' ) {
849+ quote_spanned ! { code_span =>
850+ match ( ||{ #injected_vars #body } ) ( ) {
851+ Ok ( res) => :: peg:: RuleResult :: Matched ( __pos, res) ,
852+ Err ( expected) => {
853+ __err_state. mark_failure( __pos, expected) ;
854+ :: peg:: RuleResult :: Failed
855+ } ,
856+ }
793857 }
858+ } else {
859+ let body = code. stream ( ) ;
860+ quote_spanned ! { code_span => :: peg:: RuleResult :: Matched ( __pos, ( || {
861+ #injected_vars
862+ #body
863+ } ) ( ) ) }
794864 }
795865 } else {
796- quote_spanned ! { code_span => :: peg:: RuleResult :: Matched ( __pos, ( ||#code ) ( ) ) }
866+ quote_spanned ! { span => :: peg:: RuleResult :: Matched ( __pos, ( ) ) }
797867 }
868+ } ) ;
869+
870+ if context. injected_vars . is_empty ( ) {
871+ seq
798872 } else {
799- quote_spanned ! { span => :: peg:: RuleResult :: Matched ( __pos, ( ) ) }
873+ quote_spanned ! { span => {
874+ let __lpos = __pos;
875+ #seq
876+ } }
800877 }
801- } ) ,
878+ }
802879 Expr :: MatchStr ( ref expr) => {
803880 let inner = compile_expr ( context, expr, false ) ;
804881 quote_spanned ! { span => {
@@ -826,6 +903,7 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
826903 }
827904
828905 Expr :: Precedence { ref levels } => {
906+ let injected_vars = & context. injected_vars ;
829907 let mut pre_rules = Vec :: new ( ) ;
830908 let mut level_code = Vec :: new ( ) ;
831909 let mut span_capture: Option < ( TokenStream , TokenStream , TokenStream , & Group ) > = None ;
@@ -848,8 +926,8 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
848926 let right_arg = & op. elements [ op. elements . len ( ) - 1 ] ;
849927 let r_arg = name_or_ignore ( right_arg. name . as_ref ( ) ) ;
850928
851- let action = & op. action ;
852- let action = quote_spanned ! ( op. action. span( ) =>( ||# action) ( ) ) ;
929+ let action = & op. action . stream ( ) ;
930+ let action = quote_spanned ! ( op. action. span( ) =>( ||{ #injected_vars # action } ) ( ) ) ;
853931
854932 let action = if let Some ( ( lpos_name, val_name, rpos_name, wrap_action) ) =
855933 & span_capture
0 commit comments