Skip to content

Commit c5073e0

Browse files
committed
Migrate CACHE INFORMATION comment to a module using quote.
The quote crate doesn't deal with comments, since there isn't a comment token. This patch migrates the cache information to a module. The module is nested within the `lrpar_mod!` module in order to avoid introducing multiple modules with the same name at top-level when generating multiple parsers. When possible it generate constants, otherwise it generates a function. Together these are intended to satisfy the same purpose as the previous `CACHE INFORMATION` comment.
1 parent a9ea8db commit c5073e0

File tree

1 file changed

+85
-40
lines changed

1 file changed

+85
-40
lines changed

lrpar/src/lib/ctbuilder.rs

Lines changed: 85 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
use std::{
44
any::type_name,
5-
borrow::Cow,
65
collections::{HashMap, HashSet},
76
env::{current_dir, var},
87
error::Error,
@@ -82,6 +81,27 @@ impl ToTokens for UnsuffixedUsize {
8281
}
8382
}
8483

84+
/// This wrapper adds a missing impl of `ToTokens` for tuples.
85+
/// For a tuple `(a, b)` emits `(a.to_tokens(), b.to_tokens())`
86+
struct QuoteTuple<T>(T);
87+
88+
impl<A: ToTokens, B: ToTokens> ToTokens for QuoteTuple<(A, B)> {
89+
fn to_tokens(&self, tokens: &mut TokenStream) {
90+
let (a, b) = &self.0;
91+
tokens.append_all(quote!((#a, #b)));
92+
}
93+
}
94+
95+
/// The wrapped `&str` value will be emitted with a call to `to_string()`
96+
struct QuoteToString<'a>(&'a str);
97+
98+
impl ToTokens for QuoteToString<'_> {
99+
fn to_tokens(&self, tokens: &mut TokenStream) {
100+
let x = &self.0;
101+
tokens.append_all(quote! { #x.to_string() });
102+
}
103+
}
104+
85105
impl<StorageT> fmt::Display for CTConflictsError<StorageT>
86106
where
87107
StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
@@ -164,16 +184,35 @@ pub enum RustEdition {
164184
Rust2021,
165185
}
166186

187+
impl ToTokens for Visibility {
188+
fn to_tokens(&self, tokens: &mut TokenStream) {
189+
tokens.extend(match self {
190+
Visibility::Private => quote!(),
191+
Visibility::Public => quote! {pub},
192+
Visibility::PublicSuper => quote! {pub(super)},
193+
Visibility::PublicSelf => quote! {pub(self)},
194+
Visibility::PublicCrate => quote! {pub(crate)},
195+
Visibility::PublicIn(data) => {
196+
let other = str::parse::<TokenStream>(data).unwrap();
197+
quote! {pub(in #other)}
198+
}
199+
})
200+
}
201+
}
202+
167203
impl Visibility {
168-
fn cow_str(&self) -> Cow<'static, str> {
169-
match self {
170-
Visibility::Private => Cow::from(""),
171-
Visibility::Public => Cow::from("pub"),
172-
Visibility::PublicSuper => Cow::from("pub(super)"),
173-
Visibility::PublicSelf => Cow::from("pub(self)"),
174-
Visibility::PublicCrate => Cow::from("pub(crate)"),
175-
Visibility::PublicIn(data) => Cow::from(format!("pub(in {})", data)),
176-
}
204+
fn to_variant_tokens(&self, tokens: &mut TokenStream) {
205+
tokens.extend(match self {
206+
Visibility::Private => quote!(::lrpar::Visibility::Private),
207+
Visibility::Public => quote!(::lrpar::Visibility::Public),
208+
Visibility::PublicSuper => quote!(::lrpar::Visibility::PublicSuper),
209+
Visibility::PublicSelf => quote!(::lrpar::Visibility::PublicSelf),
210+
Visibility::PublicCrate => quote!(::lrpar::Visibility::PublicCrate),
211+
Visibility::PublicIn(data) => {
212+
let data = QuoteToString(data);
213+
quote!(::lrpar::Visibility::PublicIn(#data))
214+
}
215+
})
177216
}
178217
}
179218

@@ -708,7 +747,8 @@ where
708747
cache: &str,
709748
) -> Result<(), Box<dyn Error>> {
710749
let mut outs = String::new();
711-
writeln!(outs, "{} mod {} {{", self.visibility.cow_str(), mod_name).ok();
750+
let visibility = self.visibility.clone();
751+
writeln!(outs, "{} mod {} {{", quote!(#visibility), mod_name).ok();
712752
// Emit user program section, and actions at the top so they may specify inner attributes.
713753
if let Some(YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools) =
714754
self.yacckind
@@ -741,10 +781,10 @@ where
741781
outs.push_str(" pub use _parser_::*;\n");
742782
outs.push_str(" #[allow(unused_imports)]\n");
743783
outs.push_str(" use ::lrpar::Lexeme;\n");
744-
outs.push_str("} // End of `mod {mod_name}` \n\n");
745784

746785
// Output the cache so that we can check whether the IDs map is stable.
747786
outs.push_str(cache);
787+
outs.push_str("} // End of `mod {mod_name}` \n\n");
748788

749789
let mut f = File::create(outp_rs)?;
750790
f.write_all(outs.as_bytes())?;
@@ -757,43 +797,48 @@ where
757797
fn rebuild_cache(&self, grm: &YaccGrammar<StorageT>) -> String {
758798
// We don't need to be particularly clever here: we just need to record the various things
759799
// that could change between builds.
760-
let mut cache = String::new();
761-
cache.push_str("\n/* CACHE INFORMATION\n");
762-
800+
//
763801
// Record the time that this version of lrpar was built. If the source code changes and
764802
// rustc forces a recompile, this will change this value, causing anything which depends on
765803
// this build of lrpar to be recompiled too.
766804
let build_time = env!("VERGEN_BUILD_TIMESTAMP");
767805
let grammar_path = self.grammar_path.as_ref().unwrap().to_string_lossy();
768-
let mod_name = self.mod_name;
806+
let mod_name = QuoteOption(self.mod_name);
769807
let recoverer = self.recoverer;
770808
let yacckind = self.yacckind;
771-
let visibility = self.visibility.cow_str();
809+
let mut visibility = TokenStream::new();
810+
self.visibility.to_variant_tokens(&mut visibility);
772811
let error_on_conflicts = self.error_on_conflicts;
773-
writeln!(cache, " Build time: {}", quote!(#build_time)).ok();
774-
writeln!(cache, " Grammar path: {}", quote!(#grammar_path)).ok();
775-
writeln!(cache, " Mod name: {}", quote!(#mod_name)).ok();
776-
writeln!(cache, " Recoverer: {}", quote!(#recoverer)).ok();
777-
writeln!(cache, " YaccKind: {}", quote!(#yacckind)).ok();
778-
writeln!(cache, " Visibility: {}", quote!(#visibility)).ok();
779-
writeln!(
780-
cache,
781-
" Error on conflicts: {}\n",
782-
quote!(#error_on_conflicts)
783-
)
784-
.ok();
785812

786-
// Record the rule IDs map
787-
for tidx in grm.iter_tidxs() {
788-
let n = match grm.token_name(tidx) {
789-
Some(n) => format!("'{}'", n),
790-
None => "<unknown>".to_string(),
791-
};
792-
writeln!(cache, " {} {}", usize::from(tidx), n).ok();
793-
}
794-
795-
cache.push_str("*/\n");
796-
cache
813+
let rule_map = grm
814+
.iter_tidxs()
815+
.map(|tidx| {
816+
QuoteTuple((
817+
usize::from(tidx),
818+
grm.token_name(tidx).unwrap_or("<unknown>"),
819+
))
820+
})
821+
.collect::<Vec<_>>();
822+
let rule_map_len = rule_map.len();
823+
let cache_module = quote! {
824+
#[allow(unused)]
825+
mod _cache_information_ {
826+
use ::lrpar::{RecoveryKind, Visibility, RustEdition};
827+
use ::cfgrammar::yacc::YaccKind;
828+
829+
const BUILD_TIME: &str = #build_time;
830+
const GRAMMAR_PATH: &str = #grammar_path;
831+
const MOD_NAME: Option<&str> = #mod_name;
832+
const RECOVERER: RecoveryKind = #recoverer;
833+
const YACC_KIND: YaccKind = #yacckind;
834+
const ERROR_ON_CONFLICTS: bool = #error_on_conflicts;
835+
const RULE_IDS_MAP: [(usize, &str); #rule_map_len] = [#(#rule_map,)*];
836+
fn visibility() -> Visibility {
837+
#visibility
838+
}
839+
}
840+
};
841+
cache_module.to_string()
797842
}
798843

799844
/// Generate the main parse() function for the output file.

0 commit comments

Comments
 (0)