@@ -19,7 +19,8 @@ use cfgrammar::{newlinecache::NewlineCache, Spanned};
1919use lazy_static:: lazy_static;
2020use lrpar:: { CTParserBuilder , LexerTypes } ;
2121use num_traits:: { AsPrimitive , PrimInt , Unsigned } ;
22- use quote:: quote;
22+ use proc_macro2:: TokenStream ;
23+ use quote:: { quote, ToTokens , TokenStreamExt } ;
2324use regex:: Regex ;
2425use serde:: Serialize ;
2526
@@ -78,11 +79,48 @@ pub enum RustEdition {
7879 Rust2021 ,
7980}
8081
82+ /// The quote impl of `ToTokens` for `Option` prints an empty string for `None`
83+ /// and the inner value for `Some(inner_value)`.
84+ ///
85+ /// This wrapper instead emits both `Some` and `None` variants.
86+ /// See: [quote #20](https://github.com/dtolnay/quote/issues/20)
87+ struct QuoteOption < T > ( Option < T > ) ;
88+
89+ impl < T : ToTokens > ToTokens for QuoteOption < T > {
90+ fn to_tokens ( & self , tokens : & mut TokenStream ) {
91+ tokens. append_all ( match self . 0 {
92+ Some ( ref t) => quote ! { :: std:: option:: Option :: Some ( #t) } ,
93+ None => quote ! { :: std:: option:: Option :: None } ,
94+ } ) ;
95+ }
96+ }
97+
98+ /// This wrapper adds a missing impl of `ToTokens` for tuples.
99+ /// For a tuple `(a, b)` emits `(a.to_tokens(), b.to_tokens())`
100+ struct QuoteTuple < T > ( T ) ;
101+
102+ impl < A : ToTokens , B : ToTokens > ToTokens for QuoteTuple < ( A , B ) > {
103+ fn to_tokens ( & self , tokens : & mut TokenStream ) {
104+ let ( a, b) = & self . 0 ;
105+ tokens. append_all ( quote ! ( ( #a, #b) ) ) ;
106+ }
107+ }
108+
109+ /// The wrapped `&str` value will be emitted with a call to `to_string()`
110+ struct QuoteToString < ' a > ( & ' a str ) ;
111+
112+ impl ToTokens for QuoteToString < ' _ > {
113+ fn to_tokens ( & self , tokens : & mut TokenStream ) {
114+ let x = & self . 0 ;
115+ tokens. append_all ( quote ! { #x. to_string( ) } ) ;
116+ }
117+ }
118+
81119/// A `CTLexerBuilder` allows one to specify the criteria for building a statically generated
82120/// lexer.
83121pub struct CTLexerBuilder < ' a , LexerTypesT : LexerTypes = DefaultLexerTypes < u32 > >
84122where
85- LexerTypesT :: StorageT : Debug + Eq + Hash ,
123+ LexerTypesT :: StorageT : Debug + Eq + Hash + ToTokens ,
86124 usize : num_traits:: AsPrimitive < LexerTypesT :: StorageT > ,
87125{
88126 lrpar_config : Option < Box < dyn Fn ( CTParserBuilder < LexerTypesT > ) -> CTParserBuilder < LexerTypesT > > > ,
@@ -108,7 +146,7 @@ impl CTLexerBuilder<'_, DefaultLexerTypes<u32>> {
108146impl < ' a , LexerTypesT : LexerTypes > CTLexerBuilder < ' a , LexerTypesT >
109147where
110148 LexerTypesT :: StorageT :
111- ' static + Debug + Eq + Hash + PrimInt + Serialize + TryFrom < usize > + Unsigned ,
149+ ' static + Debug + Eq + Hash + PrimInt + Serialize + TryFrom < usize > + Unsigned + ToTokens ,
112150 usize : AsPrimitive < LexerTypesT :: StorageT > ,
113151{
114152 /// Create a new [CTLexerBuilder].
@@ -438,31 +476,52 @@ pub fn lexerdef() -> {lexerdef_type} {{
438476 )
439477 . ok ( ) ;
440478
479+ let RegexOptions {
480+ dot_matches_new_line,
481+ multi_line,
482+ octal,
483+ posix_escapes,
484+ case_insensitive,
485+ unicode,
486+ swap_greed,
487+ ignore_whitespace,
488+ size_limit,
489+ dfa_size_limit,
490+ nest_limit,
491+ } = self . regex_options ;
492+ let case_insensitive = QuoteOption ( case_insensitive) ;
493+ let unicode = QuoteOption ( unicode) ;
494+ let swap_greed = QuoteOption ( swap_greed) ;
495+ let ignore_whitespace = QuoteOption ( ignore_whitespace) ;
496+ let size_limit = QuoteOption ( size_limit) ;
497+ let dfa_size_limit = QuoteOption ( dfa_size_limit) ;
498+ let nest_limit = QuoteOption ( nest_limit) ;
499+
441500 outs. push_str ( & format ! (
442501 "let regex_options = ::lrlex::RegexOptions {{
443- dot_matches_new_line: {dot_matches_new_line:? },
444- multi_line: {multi_line:? },
445- octal: {octal:? },
446- posix_escapes: {posix_escapes:? },
447- case_insensitive: {case_insensitive:? },
448- unicode: {unicode:? },
449- swap_greed: {swap_greed:? },
450- ignore_whitespace: {ignore_whitespace:? },
451- size_limit: {size_limit:? },
452- dfa_size_limit: {dfa_size_limit:? },
453- nest_limit: {nest_limit:? },
502+ dot_matches_new_line: {dot_matches_new_line},
503+ multi_line: {multi_line},
504+ octal: {octal},
505+ posix_escapes: {posix_escapes},
506+ case_insensitive: {case_insensitive},
507+ unicode: {unicode},
508+ swap_greed: {swap_greed},
509+ ignore_whitespace: {ignore_whitespace},
510+ size_limit: {size_limit},
511+ dfa_size_limit: {dfa_size_limit},
512+ nest_limit: {nest_limit},
454513 }};" ,
455- dot_matches_new_line = self . regex_options . dot_matches_new_line,
456- multi_line = self . regex_options . multi_line,
457- octal = self . regex_options . octal,
458- posix_escapes = self . regex_options . posix_escapes,
459- case_insensitive = self . regex_options . case_insensitive,
460- unicode = self . regex_options . unicode,
461- swap_greed = self . regex_options . swap_greed,
462- ignore_whitespace = self . regex_options . ignore_whitespace,
463- size_limit = self . regex_options . size_limit,
464- dfa_size_limit = self . regex_options . dfa_size_limit,
465- nest_limit = self . regex_options . nest_limit,
514+ dot_matches_new_line = quote! ( # dot_matches_new_line) ,
515+ multi_line = quote! ( # multi_line) ,
516+ octal = quote! ( # octal) ,
517+ posix_escapes = quote! ( # posix_escapes) ,
518+ case_insensitive = quote! ( # case_insensitive) ,
519+ unicode = quote! ( # unicode) ,
520+ swap_greed = quote! ( # swap_greed) ,
521+ ignore_whitespace = quote! ( # ignore_whitespace) ,
522+ size_limit = quote! ( # size_limit) ,
523+ dfa_size_limit = quote! ( # dfa_size_limit) ,
524+ nest_limit = quote! ( # nest_limit) ,
466525 ) ) ;
467526
468527 outs. push_str ( " let start_states: Vec<StartState> = vec![" ) ;
@@ -485,35 +544,22 @@ pub fn lexerdef() -> {lexerdef_type} {{
485544
486545 // Individual rules
487546 for r in lexerdef. iter_rules ( ) {
488- let tok_id = match r. tok_id {
489- Some ( ref t) => format ! ( "Some({:?})" , t) ,
490- None => "None" . to_owned ( ) ,
491- } ;
492- let n = match r. name ( ) {
493- Some ( ref n) => format ! ( "Some({}.to_string())" , quote!( #n) ) ,
494- None => "None" . to_owned ( ) ,
495- } ;
496- let target_state = match & r. target_state ( ) {
497- Some ( ( id, op) ) => format ! ( "Some(({}, ::lrlex::StartStateOperation::{:?}))" , id, op) ,
498- None => "None" . to_owned ( ) ,
499- } ;
500- let n_span = format ! (
501- "::cfgrammar::Span::new({}, {})" ,
502- r. name_span( ) . start( ) ,
503- r. name_span( ) . end( )
504- ) ;
505- let regex = & r. re_str ;
547+ let tok_id = QuoteOption ( r. tok_id ) ;
548+ let n = QuoteOption ( r. name ( ) . map ( QuoteToString ) ) ;
549+ let target_state = QuoteOption ( r. target_state ( ) . map ( |( x, y) | QuoteTuple ( ( x, y) ) ) ) ;
550+ let n_span = r. name_span ( ) ;
551+ let regex = QuoteToString ( & r. re_str ) ;
506552 let start_states = r. start_states ( ) ;
507553 write ! (
508554 outs,
509555 "
510556 Rule::new(::lrlex::unstable_api::InternalPublicApi, {}, {}, {}, {}.to_string(), {}.to_vec(), {}, ®ex_options).unwrap()," ,
511- tok_id,
512- n ,
513- n_span,
557+ quote! ( # tok_id) ,
558+ quote! ( #n ) ,
559+ quote! ( # n_span) ,
514560 quote!( #regex) ,
515561 quote!( [ #( #start_states) , * ] ) ,
516- target_state,
562+ quote! ( # target_state) ,
517563 )
518564 . ok ( ) ;
519565 }
@@ -537,10 +583,10 @@ pub fn lexerdef() -> {lexerdef_type} {{
537583 if RE_TOKEN_ID . is_match ( n) {
538584 write ! (
539585 outs,
540- "#[allow(dead_code)]\n pub const T_{}: {} = {:? };\n " ,
586+ "#[allow(dead_code)]\n pub const T_{}: {} = {};\n " ,
541587 n. to_ascii_uppercase( ) ,
542588 type_name:: <LexerTypesT :: StorageT >( ) ,
543- * id
589+ quote! ( #id )
544590 )
545591 . ok ( ) ;
546592 }
0 commit comments