1use std::{
4 any::type_name,
5 collections::{HashMap, HashSet},
6 env::{current_dir, var},
7 error::Error,
8 fmt::{self, Debug, Write as fmtWrite},
9 fs::{self, File, create_dir_all, read_to_string},
10 hash::Hash,
11 io::Write,
12 marker::PhantomData,
13 path::{Path, PathBuf},
14 sync::{LazyLock, Mutex},
15};
16
17use crate::{
18 LexerTypes, RTParserBuilder, RecoveryKind,
19 diagnostics::{DiagnosticFormatter, SpannedDiagnosticFormatter},
20};
21
22#[cfg(feature = "_unstable_api")]
23use crate::unstable_api::UnstableApi;
24
25use cfgrammar::{
26 Location, RIdx, Symbol,
27 header::{
28 GrmtoolsSectionParser, Header, HeaderError, HeaderErrorKind, HeaderValue, Namespaced,
29 Setting, Value,
30 },
31 markmap::{Entry, MergeBehavior},
32 yacc::{YaccGrammar, YaccKind, YaccOriginalActionKind, ast::ASTWithValidityInfo},
33};
34use filetime::FileTime;
35use lrtable::{Minimiser, StateGraph, StateTable, from_yacc, statetable::Conflicts};
36use num_traits::{AsPrimitive, PrimInt, Unsigned};
37use proc_macro2::{Literal, TokenStream};
38use quote::{ToTokens, TokenStreamExt, format_ident, quote};
39use syn::{Generics, parse_quote};
40use wincode::{SchemaRead, SchemaReadOwned, SchemaWrite};
41
42const ACTION_PREFIX: &str = "__gt_";
43const GLOBAL_PREFIX: &str = "__GT_";
44const ACTIONS_KIND: &str = "__GtActionsKind";
45const ACTIONS_KIND_PREFIX: &str = "Ak";
46const ACTIONS_KIND_HIDDEN: &str = "__GtActionsKindHidden";
47
48const RUST_FILE_EXT: &str = "rs";
49
50const WARNING: &str = "[Warning]";
51const ERROR: &str = "[Error]";
52
53static GENERATED_PATHS: LazyLock<Mutex<HashSet<PathBuf>>> =
54 LazyLock::new(|| Mutex::new(HashSet::new()));
55
56struct CTConflictsError<StorageT: Eq + Hash> {
57 conflicts_diagnostic: String,
58 #[cfg(test)]
59 #[cfg_attr(test, allow(dead_code))]
60 stable: StateTable<StorageT>,
61 phantom: PhantomData<StorageT>,
62}
63
64struct QuoteOption<T>(Option<T>);
70
71impl<T: ToTokens> ToTokens for QuoteOption<T> {
72 fn to_tokens(&self, tokens: &mut TokenStream) {
73 tokens.append_all(match self.0 {
74 Some(ref t) => quote! { ::std::option::Option::Some(#t) },
75 None => quote! { ::std::option::Option::None },
76 });
77 }
78}
79
80struct UnsuffixedUsize(usize);
85
86impl ToTokens for UnsuffixedUsize {
87 fn to_tokens(&self, tokens: &mut TokenStream) {
88 tokens.append(Literal::usize_unsuffixed(self.0))
89 }
90}
91
92struct QuoteTuple<T>(T);
95
96impl<A: ToTokens, B: ToTokens> ToTokens for QuoteTuple<(A, B)> {
97 fn to_tokens(&self, tokens: &mut TokenStream) {
98 let (a, b) = &self.0;
99 tokens.append_all(quote!((#a, #b)));
100 }
101}
102
103struct QuoteToString<'a>(&'a str);
105
106impl ToTokens for QuoteToString<'_> {
107 fn to_tokens(&self, tokens: &mut TokenStream) {
108 let x = &self.0;
109 tokens.append_all(quote! { #x.to_string() });
110 }
111}
112
113impl<StorageT> fmt::Display for CTConflictsError<StorageT>
114where
115 StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
116 usize: AsPrimitive<StorageT>,
117{
118 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119 write!(f, "{}", self.conflicts_diagnostic)
120 }
121}
122
123impl<StorageT> fmt::Debug for CTConflictsError<StorageT>
124where
125 StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
126 usize: AsPrimitive<StorageT>,
127{
128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129 write!(f, "{}", self.conflicts_diagnostic)
130 }
131}
132
133impl<StorageT> Error for CTConflictsError<StorageT>
134where
135 StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
136 usize: AsPrimitive<StorageT>,
137{
138}
139
140struct ErrorString(String);
142impl fmt::Display for ErrorString {
143 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 let ErrorString(s) = self;
145 write!(f, "{}", s)
146 }
147}
148impl fmt::Debug for ErrorString {
149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150 let ErrorString(s) = self;
151 write!(f, "{}", s)
152 }
153}
154impl Error for ErrorString {}
155
156#[derive(Clone, PartialEq, Eq, Debug)]
158#[non_exhaustive]
159pub enum Visibility {
160 Private,
162 Public,
164 PublicSuper,
166 PublicSelf,
168 PublicCrate,
170 PublicIn(String),
172}
173
174#[derive(Clone, Copy, PartialEq, Eq, Debug)]
178#[non_exhaustive]
179pub enum RustEdition {
180 Rust2015,
181 Rust2018,
182 Rust2021,
183}
184
185impl RustEdition {
186 fn to_variant_tokens(self) -> TokenStream {
187 match self {
188 RustEdition::Rust2015 => quote!(::lrpar::RustEdition::Rust2015),
189 RustEdition::Rust2018 => quote!(::lrpar::RustEdition::Rust2018),
190 RustEdition::Rust2021 => quote!(::lrpar::RustEdition::Rust2021),
191 }
192 }
193}
194
195impl ToTokens for Visibility {
196 fn to_tokens(&self, tokens: &mut TokenStream) {
197 tokens.extend(match self {
198 Visibility::Private => quote!(),
199 Visibility::Public => quote! {pub},
200 Visibility::PublicSuper => quote! {pub(super)},
201 Visibility::PublicSelf => quote! {pub(self)},
202 Visibility::PublicCrate => quote! {pub(crate)},
203 Visibility::PublicIn(data) => {
204 let other = str::parse::<TokenStream>(data).unwrap();
205 quote! {pub(in #other)}
206 }
207 })
208 }
209}
210
211impl Visibility {
212 fn to_variant_tokens(&self) -> TokenStream {
213 match self {
214 Visibility::Private => quote!(::lrpar::Visibility::Private),
215 Visibility::Public => quote!(::lrpar::Visibility::Public),
216 Visibility::PublicSuper => quote!(::lrpar::Visibility::PublicSuper),
217 Visibility::PublicSelf => quote!(::lrpar::Visibility::PublicSelf),
218 Visibility::PublicCrate => quote!(::lrpar::Visibility::PublicCrate),
219 Visibility::PublicIn(data) => {
220 let data = QuoteToString(data);
221 quote!(::lrpar::Visibility::PublicIn(#data))
222 }
223 }
224 }
225}
226
227#[non_exhaustive]
231#[derive(SchemaRead, SchemaWrite, Debug, Clone, Copy)]
232pub enum SerialisationFormat {
233 FixedSizeInteger,
235 VariableSizedInteger,
237}
238
239impl TryFrom<SerialisationFormat> for Value<Location> {
240 type Error = cfgrammar::header::HeaderError<Location>;
241 fn try_from(kind: SerialisationFormat) -> Result<Value<Location>, HeaderError<Location>> {
242 let from_loc = Location::Other("From<SerialisationFormat>".to_string());
243 Ok(match kind {
244 SerialisationFormat::FixedSizeInteger => Value::Setting(Setting::Unitary(Namespaced {
245 namespace: Some(("serialisationformat".to_string(), from_loc.clone())),
246 member: ("fixedsizeinteger".to_string(), from_loc),
247 })),
248 SerialisationFormat::VariableSizedInteger => {
249 Value::Setting(Setting::Unitary(Namespaced {
250 namespace: Some(("serialisationformat".to_string(), from_loc.clone())),
251 member: ("variablesizedinteger".to_string(), from_loc),
252 }))
253 }
254 })
255 }
256}
257
258impl<T: Clone + Debug> TryFrom<&Value<T>> for SerialisationFormat {
259 type Error = HeaderError<T>;
260 fn try_from(value: &Value<T>) -> Result<SerialisationFormat, HeaderError<T>> {
261 let mut err_locs = Vec::new();
262 match value {
263 Value::Setting(Setting::Unitary(Namespaced {
265 namespace,
266 member: (enc_value, enc_value_loc),
267 })) => {
268 if let Some((ns, ns_loc)) = namespace
269 && ns != "serialisationformat"
270 {
271 err_locs.push(ns_loc.clone());
272 }
273 let encodings = [
274 (
275 "fixedsizeinteger".to_string(),
276 SerialisationFormat::FixedSizeInteger,
277 ),
278 (
279 "variablesizedinteger".to_string(),
280 SerialisationFormat::VariableSizedInteger,
281 ),
282 ];
283 let enc_found = encodings
284 .iter()
285 .find_map(|(enc_str, enc)| (enc_str == enc_value).then_some(enc));
286 if let Some(enc) = enc_found {
287 if err_locs.is_empty() {
288 Ok(*enc)
289 } else {
290 Err(HeaderError {
291 kind: HeaderErrorKind::InvalidEntry("serialisation_format"),
292 locations: err_locs,
293 })
294 }
295 } else {
296 err_locs.push(enc_value_loc.clone());
297 Err(HeaderError {
298 kind: HeaderErrorKind::InvalidEntry("serialisation_format"),
299 locations: err_locs,
300 })
301 }
302 }
303 val => {
304 err_locs.push(val.primary_location().clone());
305 Err(HeaderError {
306 kind: HeaderErrorKind::InvalidEntry("serialisation_format"),
307 locations: err_locs,
308 })
309 }
310 }
311 }
312}
313
314#[doc(hidden)]
316pub use wincode;
317impl ToTokens for SerialisationFormat {
318 fn to_tokens(&self, tokens: &mut TokenStream) {
319 tokens.extend(match self {
320 SerialisationFormat::FixedSizeInteger => {
321 quote! {::lrpar::ctbuilder::SerialisationFormat::FixedSizeInteger}
322 }
323 SerialisationFormat::VariableSizedInteger => {
324 quote! {::lrpar::ctbuilder::SerialisationFormat::VariableSizedInteger}
325 }
326 })
327 }
328}
329
330pub struct CTParserBuilder<'a, LexerTypesT: LexerTypes>
333where
334 LexerTypesT::StorageT: Eq + Hash,
335 usize: AsPrimitive<LexerTypesT::StorageT>,
336{
337 grammar_path: Option<PathBuf>,
341 grammar_src: Option<String>,
343 from_ast: Option<ASTWithValidityInfo>,
345 output_path: Option<PathBuf>,
346 mod_name: Option<&'a str>,
347 recoverer: Option<RecoveryKind>,
348 yacckind: Option<YaccKind>,
349 error_on_conflicts: bool,
350 warnings_are_errors: bool,
351 show_warnings: bool,
352 visibility: Visibility,
353 rust_edition: RustEdition,
354 inspect_rt: Option<
355 Box<
356 dyn for<'b> FnMut(
357 &'b mut Header<Location>,
358 RTParserBuilder<LexerTypesT::StorageT, LexerTypesT>,
359 &'b HashMap<String, LexerTypesT::StorageT>,
360 &PathBuf,
361 ) -> Result<(), Box<dyn Error>>,
362 >,
363 >,
364 serialisation_format: Option<SerialisationFormat>,
365 #[cfg(test)]
367 inspect_callback: Option<Box<dyn Fn(RecoveryKind) -> Result<(), Box<dyn Error>>>>,
368 phantom: PhantomData<LexerTypesT>,
369}
370
371type FixIntConfig = wincode::config::Configuration;
373type VarIntConfig = wincode::config::Configuration<
375 true,
376 4194304,
377 wincode::len::BincodeLen,
378 wincode::int_encoding::LittleEndian,
379 wincode::int_encoding::VarInt,
380>;
381
382impl<
383 'a,
384 StorageT: 'static
385 + Debug
386 + Hash
387 + PrimInt
388 + SchemaWrite<FixIntConfig, Src = StorageT>
389 + SchemaWrite<VarIntConfig, Src = StorageT>
390 + Unsigned,
391 LexerTypesT: LexerTypes<StorageT = StorageT>,
392> CTParserBuilder<'a, LexerTypesT>
393where
394 usize: AsPrimitive<StorageT>,
395{
396 pub fn new() -> Self {
418 CTParserBuilder {
419 grammar_path: None,
420 grammar_src: None,
421 from_ast: None,
422 output_path: None,
423 mod_name: None,
424 recoverer: None,
425 yacckind: None,
426 error_on_conflicts: true,
427 warnings_are_errors: true,
428 show_warnings: true,
429 visibility: Visibility::Private,
430 rust_edition: RustEdition::Rust2021,
431 inspect_rt: None,
432 serialisation_format: None,
433 #[cfg(test)]
434 inspect_callback: None,
435 phantom: PhantomData,
436 }
437 }
438
439 pub fn grammar_in_src_dir<P>(mut self, srcp: P) -> Result<Self, Box<dyn Error>>
456 where
457 P: AsRef<Path>,
458 {
459 if !srcp.as_ref().is_relative() {
460 return Err(format!(
461 "Grammar path '{}' must be a relative path.",
462 srcp.as_ref().to_str().unwrap_or("<invalid UTF-8>")
463 )
464 .into());
465 }
466
467 let mut grmp = current_dir()?;
468 grmp.push("src");
469 grmp.push(srcp.as_ref());
470 self.grammar_path = Some(grmp);
471
472 let mut outp = PathBuf::new();
473 outp.push(var("OUT_DIR").unwrap());
474 outp.push(srcp.as_ref().parent().unwrap().to_str().unwrap());
475 create_dir_all(&outp)?;
476 let mut leaf = srcp
477 .as_ref()
478 .file_name()
479 .unwrap()
480 .to_str()
481 .unwrap()
482 .to_owned();
483 write!(leaf, ".{}", RUST_FILE_EXT).ok();
484 outp.push(leaf);
485 Ok(self.output_path(outp))
486 }
487
488 #[cfg(feature = "_unstable_api")]
491 pub fn grammar_ast(mut self, valid_ast: ASTWithValidityInfo, _api_key: UnstableApi) -> Self {
492 self.from_ast = Some(valid_ast);
493 self
494 }
495
496 pub fn grammar_path<P>(mut self, inp: P) -> Self
500 where
501 P: AsRef<Path>,
502 {
503 self.grammar_path = Some(inp.as_ref().to_owned());
504 self
505 }
506
507 #[cfg(feature = "_unstable_api")]
508 pub fn with_grammar_src(mut self, src: String, _api_key: UnstableApi) -> Self {
509 self.grammar_src = Some(src);
510 self
511 }
512
513 pub fn output_path<P>(mut self, outp: P) -> Self
518 where
519 P: AsRef<Path>,
520 {
521 self.output_path = Some(outp.as_ref().to_owned());
522 self
523 }
524
525 pub fn mod_name(mut self, mod_name: &'a str) -> Self {
529 self.mod_name = Some(mod_name);
530 self
531 }
532
533 pub fn visibility(mut self, vis: Visibility) -> Self {
535 self.visibility = vis;
536 self
537 }
538
539 pub fn recoverer(mut self, rk: RecoveryKind) -> Self {
541 self.recoverer = Some(rk);
542 self
543 }
544
545 pub fn yacckind(mut self, yk: YaccKind) -> Self {
547 self.yacckind = Some(yk);
548 self
549 }
550
551 pub fn error_on_conflicts(mut self, b: bool) -> Self {
554 self.error_on_conflicts = b;
555 self
556 }
557
558 pub fn warnings_are_errors(mut self, b: bool) -> Self {
561 self.warnings_are_errors = b;
562 self
563 }
564
565 pub fn show_warnings(mut self, b: bool) -> Self {
568 self.show_warnings = b;
569 self
570 }
571
572 pub fn rust_edition(mut self, edition: RustEdition) -> Self {
575 self.rust_edition = edition;
576 self
577 }
578
579 pub fn serialisation_format(mut self, serialisation_format: SerialisationFormat) -> Self {
580 self.serialisation_format = Some(serialisation_format);
581 self
582 }
583
584 #[cfg(test)]
585 pub fn inspect_recoverer(
586 mut self,
587 cb: Box<dyn for<'h, 'y> Fn(RecoveryKind) -> Result<(), Box<dyn Error>>>,
588 ) -> Self {
589 self.inspect_callback = Some(cb);
590 self
591 }
592
593 #[doc(hidden)]
594 pub fn inspect_rt(
595 mut self,
596 cb: Box<
597 dyn for<'b, 'y> FnMut(
598 &'b mut Header<Location>,
599 RTParserBuilder<'y, StorageT, LexerTypesT>,
600 &'b HashMap<String, StorageT>,
601 &PathBuf,
602 ) -> Result<(), Box<dyn Error>>,
603 >,
604 ) -> Self {
605 self.inspect_rt = Some(cb);
606 self
607 }
608
609 pub fn build(mut self) -> Result<CTParser<StorageT>, Box<dyn Error>> {
662 let grmp = self
663 .grammar_path
664 .as_ref()
665 .expect("grammar_path must be specified before processing.");
666 let outp = self
667 .output_path
668 .as_ref()
669 .expect("output_path must be specified before processing.");
670 let mut header = Header::new();
671
672 match header.entry("yacckind".to_string()) {
673 Entry::Occupied(_) => unreachable!(),
674 Entry::Vacant(mut v) => match self.yacckind {
675 Some(YaccKind::Eco) => panic!("Eco compile-time grammar generation not supported."),
676 Some(yk) => {
677 let yk_value = Value::try_from(yk)?;
678 let mut o = v.insert_entry(HeaderValue(
679 Location::Other("CTParserBuilder".to_string()),
680 yk_value,
681 ));
682 o.set_merge_behavior(MergeBehavior::Ours);
683 }
684 None => {
685 v.mark_required();
686 }
687 },
688 }
689 if let Some(recoverer) = self.recoverer {
690 match header.entry("recoverer".to_string()) {
691 Entry::Occupied(_) => unreachable!(),
692 Entry::Vacant(v) => {
693 let rk_value: Value<Location> = Value::try_from(recoverer)?;
694 let mut o = v.insert_entry(HeaderValue(
695 Location::Other("CTParserBuilder".to_string()),
696 rk_value,
697 ));
698 o.set_merge_behavior(MergeBehavior::Ours);
699 }
700 }
701 }
702
703 if let Some(encoding) = self.serialisation_format {
704 match header.entry("serialisation_format".to_string()) {
705 Entry::Occupied(_) => unreachable!(),
706 Entry::Vacant(v) => {
707 let rk_value: Value<Location> = Value::try_from(encoding)?;
708 let mut o = v.insert_entry(HeaderValue(
709 Location::Other("CTParserBuilder".to_string()),
710 rk_value,
711 ));
712 o.set_merge_behavior(MergeBehavior::Ours);
713 }
714 }
715 }
716
717 {
718 let mut lk = GENERATED_PATHS.lock().unwrap();
719 if lk.contains(outp.as_path()) {
720 return Err(format!("Generating two parsers to the same path ('{}') is not allowed: use CTParserBuilder::output_path (and, optionally, CTParserBuilder::mod_name) to differentiate them.", outp.to_str().unwrap()).into());
721 }
722 lk.insert(outp.clone());
723 }
724
725 let inc = if let Some(grammar_src) = &self.grammar_src {
726 grammar_src.clone()
727 } else {
728 read_to_string(grmp).map_err(|e| format!("When reading '{}': {e}", grmp.display()))?
729 };
730 let yacc_diag = SpannedDiagnosticFormatter::new(&inc, grmp);
731 let parsed_header = GrmtoolsSectionParser::new(&inc, false).parse();
732 if let Err(errs) = parsed_header {
733 let mut out = String::new();
734 out.push_str(&format!(
735 "\n{ERROR}{}\n",
736 yacc_diag.file_location_msg(" parsing the `%grmtools` section", None)
737 ));
738 for e in errs {
739 out.push_str(&indent(" ", &yacc_diag.format_error(e).to_string()));
740 }
741 return Err(ErrorString(out).into());
742 };
743 let (parsed_header, _) = parsed_header.unwrap();
744 header.merge_from(parsed_header)?;
745 self.yacckind = header
746 .get("yacckind")
747 .map(|HeaderValue(_, val)| val)
748 .map(YaccKind::try_from)
749 .transpose()?;
750 header.mark_used(&"yacckind".to_string());
751 let ast_validation = if let Some(ast) = &self.from_ast {
752 ast.clone()
753 } else if let Some(yk) = self.yacckind {
754 ASTWithValidityInfo::new(yk, &inc)
755 } else {
756 Err("Missing 'yacckind'".to_string())?
757 };
758
759 header.mark_used(&"recoverer".to_string());
760 let rk_val = header.get("recoverer").map(|HeaderValue(_, rk_val)| rk_val);
761
762 if let Some(rk_val) = rk_val {
763 self.recoverer = Some(RecoveryKind::try_from(rk_val)?);
764 } else {
765 self.recoverer = Some(RecoveryKind::CPCTPlus);
767 }
768 header.mark_used(&"serialisation_format".to_string());
769 if let Some(ec_val) = header
770 .get("serialisation_format")
771 .map(|HeaderValue(_, ec_val)| ec_val)
772 {
773 self.serialisation_format = Some(SerialisationFormat::try_from(ec_val)?);
774 } else {
775 self.serialisation_format = Some(SerialisationFormat::VariableSizedInteger);
776 }
777
778 self.yacckind = Some(ast_validation.yacc_kind());
779 let warnings = ast_validation.ast().warnings();
780 if self.warnings_are_errors && !warnings.is_empty() {
781 let mut out = String::new();
782 out.push_str(&format!(
783 "\n{ERROR}{}\n",
784 yacc_diag.file_location_msg("", None)
785 ));
786 for e in warnings {
787 out.push_str(&format!(
788 "{}\n",
789 indent(" ", &yacc_diag.format_warning(e).to_string())
790 ));
791 }
792 return Err(ErrorString(out).into());
793 } else if !warnings.is_empty() {
794 for w in warnings {
795 let ws_loc = yacc_diag.file_location_msg("", None);
796 let ws = indent(" ", &yacc_diag.format_warning(w).to_string());
797 if std::env::var("OUT_DIR").is_ok() && self.show_warnings {
799 for line in ws_loc.lines().chain(ws.lines()) {
800 println!("cargo:warning={}", line);
801 }
802 } else if self.show_warnings {
803 eprintln!("{}", ws_loc);
804 eprintln!("{WARNING} {}", ws);
805 }
806 }
807 }
808 let grm = match YaccGrammar::<StorageT>::new_from_ast_with_validity_info(&ast_validation) {
809 Ok(grm) => grm,
810 Err(errs) => {
811 let mut out = String::new();
812 out.push_str(&format!(
813 "\n{ERROR}{}\n",
814 yacc_diag.file_location_msg("", None)
815 ));
816 for e in errs {
817 out.push_str(&indent(" ", &yacc_diag.format_error(e).to_string()));
818 out.push('\n');
819 }
820 return Err(ErrorString(out).into());
821 }
822 };
823 #[cfg(test)]
824 if let Some(cb) = &self.inspect_callback {
825 cb(self.recoverer.expect("has a default value"))?;
826 }
827
828 let rule_ids = grm
829 .tokens_map()
830 .iter()
831 .map(|(&n, &i)| (n.to_owned(), i.as_storaget()))
832 .collect::<HashMap<_, _>>();
833
834 let derived_mod_name = match self.mod_name {
835 Some(s) => s.to_owned(),
836 None => {
837 let mut stem = grmp.to_str().unwrap();
842 loop {
843 let new_stem = Path::new(stem).file_stem().unwrap().to_str().unwrap();
844 if stem == new_stem {
845 break;
846 }
847 stem = new_stem;
848 }
849 format!("{}_y", stem)
850 }
851 };
852
853 let cache = self.rebuild_cache(&derived_mod_name, &grm);
854
855 if let Ok(ref inmd) = fs::metadata(grmp)
863 && let Ok(ref out_rs_md) = fs::metadata(outp)
864 && FileTime::from_last_modification_time(out_rs_md)
865 > FileTime::from_last_modification_time(inmd)
866 && let Ok(outc) = read_to_string(outp)
867 {
868 if outc.contains(&cache.to_string()) {
869 return Ok(CTParser {
870 regenerated: false,
871 rule_ids,
872 yacc_grammar: grm,
873 grammar_src: inc,
874 grammar_path: self.grammar_path.unwrap(),
875 conflicts: None,
876 });
877 } else {
878 #[cfg(grmtools_extra_checks)]
879 if std::env::var("CACHE_EXPECTED").is_ok() {
880 eprintln!("outc: {}", outc);
881 eprintln!("using cache: {}", cache,);
882 panic!("The cache regenerated however, it was expected to match");
884 }
885 }
886 }
887
888 fs::remove_file(outp).ok();
895
896 let (sgraph, stable) = from_yacc(&grm, Minimiser::Pager)?;
897 if self.error_on_conflicts
898 && let Some(c) = stable.conflicts()
899 {
900 match (grm.expect(), grm.expectrr()) {
901 (Some(i), Some(j)) if i == c.sr_len() && j == c.rr_len() => (),
902 (Some(i), None) if i == c.sr_len() && 0 == c.rr_len() => (),
903 (None, Some(j)) if 0 == c.sr_len() && j == c.rr_len() => (),
904 (None, None) if 0 == c.rr_len() && 0 == c.sr_len() => (),
905 _ => {
906 let conflicts_diagnostic = yacc_diag.format_conflicts::<LexerTypesT>(
907 &grm,
908 ast_validation.ast(),
909 c,
910 &sgraph,
911 &stable,
912 );
913 return Err(Box::new(CTConflictsError {
914 conflicts_diagnostic,
915 phantom: PhantomData,
916 #[cfg(test)]
917 stable,
918 }));
919 }
920 }
921 }
922
923 if let Some(ref mut inspector_rt) = self.inspect_rt {
924 let rt: RTParserBuilder<'_, StorageT, LexerTypesT> =
925 RTParserBuilder::new(&grm, &stable);
926 let rt = if let Some(rk) = self.recoverer {
927 rt.recoverer(rk)
928 } else {
929 rt
930 };
931 inspector_rt(&mut header, rt, &rule_ids, grmp)?
932 }
933
934 let unused_keys = header.unused();
935 if !unused_keys.is_empty() {
936 return Err(format!("Unused keys in header: {}", unused_keys.join(", ")).into());
937 }
938 let missing_keys = header
939 .missing()
940 .iter()
941 .map(|s| s.as_str())
942 .collect::<Vec<_>>();
943 if !missing_keys.is_empty() {
944 return Err(format!(
945 "Required values were missing from the header: {}",
946 missing_keys.join(", ")
947 )
948 .into());
949 }
950
951 self.output_file(
952 &grm,
953 &stable,
954 &derived_mod_name,
955 outp,
956 &format!("/* CACHE INFORMATION {} */\n", cache),
957 )?;
958 let conflicts = if stable.conflicts().is_some() {
959 Some((sgraph, stable))
960 } else {
961 None
962 };
963 Ok(CTParser {
964 regenerated: true,
965 rule_ids,
966 yacc_grammar: grm,
967 grammar_src: inc,
968 grammar_path: self.grammar_path.unwrap(),
969 conflicts,
970 })
971 }
972
973 #[deprecated(
980 since = "0.11.0",
981 note = "Please use grammar_in_src_dir(), build(), and token_map() instead"
982 )]
983 #[allow(deprecated)]
984 pub fn process_file_in_src(
985 &mut self,
986 srcp: &str,
987 ) -> Result<HashMap<String, StorageT>, Box<dyn Error>> {
988 let mut inp = current_dir()?;
989 inp.push("src");
990 inp.push(srcp);
991 let mut outp = PathBuf::new();
992 outp.push(var("OUT_DIR").unwrap());
993 outp.push(Path::new(srcp).parent().unwrap().to_str().unwrap());
994 create_dir_all(&outp)?;
995 let mut leaf = Path::new(srcp)
996 .file_name()
997 .unwrap()
998 .to_str()
999 .unwrap()
1000 .to_owned();
1001 write!(leaf, ".{}", RUST_FILE_EXT).ok();
1002 outp.push(leaf);
1003 self.process_file(inp, outp)
1004 }
1005
1006 #[deprecated(
1042 since = "0.11.0",
1043 note = "Please use grammar_path(), output_path(), build(), and token_map() instead"
1044 )]
1045 pub fn process_file<P, Q>(
1046 &mut self,
1047 inp: P,
1048 outp: Q,
1049 ) -> Result<HashMap<String, StorageT>, Box<dyn Error>>
1050 where
1051 P: AsRef<Path>,
1052 Q: AsRef<Path>,
1053 {
1054 self.grammar_path = Some(inp.as_ref().to_owned());
1055 self.output_path = Some(outp.as_ref().to_owned());
1056 let cl: CTParserBuilder<LexerTypesT> = CTParserBuilder {
1057 grammar_path: self.grammar_path.clone(),
1058 grammar_src: None,
1059 from_ast: None,
1060 output_path: self.output_path.clone(),
1061 mod_name: self.mod_name,
1062 recoverer: self.recoverer,
1063 yacckind: self.yacckind,
1064 error_on_conflicts: self.error_on_conflicts,
1065 warnings_are_errors: self.warnings_are_errors,
1066 show_warnings: self.show_warnings,
1067 visibility: self.visibility.clone(),
1068 rust_edition: self.rust_edition,
1069 inspect_rt: None,
1070 serialisation_format: self.serialisation_format,
1071 #[cfg(test)]
1072 inspect_callback: None,
1073 phantom: PhantomData,
1074 };
1075 Ok(cl.build()?.rule_ids)
1076 }
1077
1078 fn output_file<P: AsRef<Path>>(
1079 &self,
1080 grm: &YaccGrammar<StorageT>,
1081 stable: &StateTable<StorageT>,
1082 mod_name: &str,
1083 outp_rs: P,
1084 cache: &str,
1085 ) -> Result<(), Box<dyn Error>> {
1086 let visibility = self.visibility.clone();
1087 let user_actions = if let Some(
1088 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools,
1089 ) = self.yacckind
1090 {
1091 Some(self.gen_user_actions(grm)?)
1092 } else {
1093 None
1094 };
1095 let rule_consts = self.gen_rule_consts(grm)?;
1096 let token_epp = self.gen_token_epp(grm)?;
1097 let parse_function = self.gen_parse_function(grm, stable)?;
1098 let action_wrappers = match self.yacckind.unwrap() {
1099 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1100 Some(self.gen_wrappers(grm)?)
1101 }
1102 YaccKind::Original(YaccOriginalActionKind::NoAction)
1103 | YaccKind::Original(YaccOriginalActionKind::GenericParseTree) => None,
1104 _ => unreachable!(),
1105 };
1106
1107 let additional_decls =
1108 if let Some(YaccKind::Original(YaccOriginalActionKind::GenericParseTree)) =
1109 self.yacckind
1110 {
1111 Some(quote! {
1114 #[allow(unused_imports)]
1115 pub use ::lrpar::parser::_deprecated_moved_::Node;
1116 })
1117 } else {
1118 None
1119 };
1120
1121 let mod_name =
1122 match syn::parse_str::<proc_macro2::Ident>(mod_name) {
1123 Ok(s) => s,
1124 Err(e) => return Err(format!(
1125 "CTParserBuilder::mod_name(\"{}\") is not a valid rust identifier due to '{}'",
1126 mod_name, e
1127 )
1128 .into()),
1129 };
1130 let out_tokens = quote! {
1131 #visibility mod #mod_name {
1132 #user_actions
1134 mod _parser_ {
1135 #![allow(clippy::type_complexity)]
1136 #![allow(clippy::unnecessary_wraps)]
1137 #![deny(unsafe_code)]
1138 #[allow(unused_imports)]
1139 use super::*;
1140 #additional_decls
1141 #parse_function
1142 #rule_consts
1143 #token_epp
1144 #action_wrappers
1145 } #[allow(unused_imports)]
1147 pub use _parser_::*;
1148 #[allow(unused_imports)]
1149 use ::lrpar::Lexeme;
1150 } };
1152 let unformatted = out_tokens.to_string();
1154 let outs = syn::parse_str(&unformatted)
1155 .map(|syntax_tree| prettyplease::unparse(&syntax_tree))
1156 .unwrap_or(unformatted);
1157 let mut f = File::create(outp_rs)?;
1158 f.write_all(outs.as_bytes())?;
1159 f.write_all(cache.as_bytes())?;
1160 Ok(())
1161 }
1162
1163 fn rebuild_cache(&self, derived_mod_name: &'_ str, grm: &YaccGrammar<StorageT>) -> TokenStream {
1166 let Self {
1173 grammar_path,
1176 grammar_src: _,
1178 from_ast: _,
1180 mod_name,
1181 recoverer,
1182 yacckind,
1183 output_path: _,
1184 error_on_conflicts,
1185 warnings_are_errors,
1186 show_warnings,
1187 visibility,
1188 rust_edition,
1189 serialisation_format,
1190 inspect_rt: _,
1191 #[cfg(test)]
1192 inspect_callback: _,
1193 phantom: _,
1194 } = self;
1195 let build_time = env!("VERGEN_BUILD_TIMESTAMP");
1196 let grammar_path = grammar_path.as_ref().unwrap().to_string_lossy();
1197 let mod_name = QuoteOption(mod_name.as_deref());
1198 let visibility = visibility.to_variant_tokens();
1199 let rust_edition = rust_edition.to_variant_tokens();
1200 let yacckind = yacckind.expect("is_some() by this point");
1201 let rule_map = grm
1202 .iter_tidxs()
1203 .map(|tidx| {
1204 QuoteTuple((
1205 usize::from(tidx),
1206 grm.token_name(tidx).unwrap_or("<unknown>"),
1207 ))
1208 })
1209 .collect::<Vec<_>>();
1210 let cache_info = quote! {
1211 BUILD_TIME = #build_time
1212 DERIVED_MOD_NAME = #derived_mod_name
1213 ENCODING_CONFIG = #serialisation_format
1214 GRAMMAR_PATH = #grammar_path
1215 MOD_NAME = #mod_name
1216 RECOVERER = #recoverer
1217 YACC_KIND = #yacckind
1218 ERROR_ON_CONFLICTS = #error_on_conflicts
1219 SHOW_WARNINGS = #show_warnings
1220 WARNINGS_ARE_ERRORS = #warnings_are_errors
1221 RUST_EDITION = #rust_edition
1222 RULE_IDS_MAP = [#(#rule_map,)*]
1223 VISIBILITY = #visibility
1224
1225 };
1226 let cache_info_str = cache_info.to_string();
1227 quote!(#cache_info_str)
1228 }
1229
1230 fn gen_parse_function(
1232 &self,
1233 grm: &YaccGrammar<StorageT>,
1234 stable: &StateTable<StorageT>,
1235 ) -> Result<TokenStream, Box<dyn Error>> {
1236 let storaget = str::parse::<TokenStream>(type_name::<StorageT>())?;
1237 let lexertypest = str::parse::<TokenStream>(type_name::<LexerTypesT>())?;
1238 let recoverer = self.recoverer;
1239 let run_parser = match self.yacckind.unwrap() {
1240 YaccKind::Original(YaccOriginalActionKind::GenericParseTree) => {
1241 quote! {
1242 ::lrpar::RTParserBuilder::new(grm, stable)
1243 .recoverer(#recoverer)
1244 .parse_map(
1245 lexer,
1246 &|lexeme| Node::Term{lexeme},
1247 &|ridx, nodes| Node::Nonterm{ridx, nodes}
1248 )
1249 }
1250 }
1251 YaccKind::Original(YaccOriginalActionKind::NoAction) => {
1252 quote! {
1253 ::lrpar::RTParserBuilder::new(grm, stable)
1254 .recoverer(#recoverer)
1255 .parse_map(lexer, &|_| (), &|_, _| ()).1
1256 }
1257 }
1258 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1259 let actionskind = str::parse::<TokenStream>(ACTIONS_KIND)?;
1260 let parsed_parse_generics = make_generics(grm.parse_generics().as_deref())?;
1261 let (_, type_generics, _) = parsed_parse_generics.split_for_impl();
1262 let (action_fn_parse_param, action_fn_parse_param_ty) = match grm.parse_param() {
1265 Some((name, ty)) => {
1266 let name = str::parse::<TokenStream>(name)?;
1267 let ty = str::parse::<TokenStream>(ty)?;
1268 (quote!(#name), quote!(#ty))
1269 }
1270 None => (quote!(()), quote!(())),
1271 };
1272 let wrappers = grm.iter_pidxs().map(|pidx| {
1273 let pidx = usize::from(pidx);
1274 format_ident!("{}wrapper_{}", ACTION_PREFIX, pidx)
1275 });
1276 let edition_lifetime = if self.rust_edition != RustEdition::Rust2015 {
1277 quote!('_,)
1278 } else {
1279 quote!()
1280 };
1281 let ridx = usize::from(self.user_start_ridx(grm));
1282 let action_ident = format_ident!("{}{}", ACTIONS_KIND_PREFIX, ridx);
1283
1284 quote! {
1285 let actions: ::std::vec::Vec<
1286 &dyn Fn(
1287 ::cfgrammar::RIdx<#storaget>,
1288 &'lexer dyn ::lrpar::NonStreamingLexer<'input, #lexertypest>,
1289 ::cfgrammar::Span,
1290 ::std::vec::Drain<#edition_lifetime ::lrpar::parser::AStackType<<#lexertypest as ::lrpar::LexerTypes>::LexemeT, #actionskind #type_generics>>,
1291 #action_fn_parse_param_ty
1292 ) -> #actionskind #type_generics
1293 > = ::std::vec![#(&#wrappers,)*];
1294 match ::lrpar::RTParserBuilder::new(grm, stable)
1295 .recoverer(#recoverer)
1296 .parse_actions(lexer, &actions, #action_fn_parse_param) {
1297 (Some(#actionskind::#action_ident(x)), y) => (Some(x), y),
1298 (None, y) => (None, y),
1299 _ => unreachable!()
1300 }
1301 }
1302 }
1303 kind => panic!("YaccKind {:?} not supported", kind),
1304 };
1305
1306 let parsed_parse_generics: Generics = match self.yacckind.unwrap() {
1307 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1308 make_generics(grm.parse_generics().as_deref())?
1309 }
1310 _ => make_generics(None)?,
1311 };
1312 let (generics, _, where_clause) = parsed_parse_generics.split_for_impl();
1313
1314 let parse_fn_parse_param = match self.yacckind.unwrap() {
1316 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1317 if let Some((name, tyname)) = grm.parse_param() {
1318 let name = str::parse::<TokenStream>(name)?;
1319 let tyname = str::parse::<TokenStream>(tyname)?;
1320 Some(quote! {#name: #tyname})
1321 } else {
1322 None
1323 }
1324 }
1325 _ => None,
1326 };
1327 let parse_fn_return_ty = match self.yacckind.unwrap() {
1328 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1329 let actiont = grm
1330 .actiontype(self.user_start_ridx(grm))
1331 .as_ref()
1332 .map(|at| str::parse::<TokenStream>(at))
1333 .transpose()?;
1334 quote! {
1335 (::std::option::Option<#actiont>, ::std::vec::Vec<::lrpar::LexParseError<#storaget, #lexertypest>>)
1336 }
1337 }
1338 YaccKind::Original(YaccOriginalActionKind::GenericParseTree) => quote! {
1339 (::std::option::Option<Node<<#lexertypest as ::lrpar::LexerTypes>::LexemeT, #storaget>>,
1340 ::std::vec::Vec<::lrpar::LexParseError<#storaget, #lexertypest>>)
1341 },
1342 YaccKind::Original(YaccOriginalActionKind::NoAction) => quote! {
1343 ::std::vec::Vec<::lrpar::LexParseError<#storaget, #lexertypest>>
1344 },
1345 _ => unreachable!(),
1346 };
1347
1348 let serialisation_format = self
1349 .serialisation_format
1350 .expect("Should already have a default value");
1351 let (grm_data, stable_data): (Vec<u8>, Vec<u8>) = match serialisation_format {
1353 SerialisationFormat::FixedSizeInteger => {
1354 let config = wincode::config::Configuration::default().with_fixint_encoding();
1355 let grm = wincode::config::serialize(grm, config)?;
1356 let stable = wincode::config::serialize(stable, config)?;
1357 (grm, stable)
1358 }
1359 SerialisationFormat::VariableSizedInteger => {
1360 let config = wincode::config::Configuration::default().with_varint_encoding();
1361 let grm = wincode::config::serialize(grm, config)?;
1362 let stable = wincode::config::serialize(stable, config)?;
1363 (grm, stable)
1364 }
1365 };
1366 let serialisation_format_str = quote!(serialisation_format).to_string();
1367 Ok(quote! {
1368 const __GRM_DATA: &[u8] = &[#(#grm_data,)*];
1369 const __STABLE_DATA: &[u8] = &[#(#stable_data,)*];
1370 const __SERIALISATION_FORMAT: ::lrpar::ctbuilder::SerialisationFormat = #serialisation_format;
1371
1372 fn __lrpar_parser_data() -> &'static ::lrpar::ParserData<#storaget> {
1373 static DATA: ::std::sync::OnceLock<::lrpar::ParserData<#storaget>>
1374 = ::std::sync::OnceLock::new();
1375 DATA.get_or_init(
1376 || {
1377 match __SERIALISATION_FORMAT {
1380 ::lrpar::ctbuilder::SerialisationFormat::FixedSizeInteger => {
1381 ::lrpar::ctbuilder::_reconstitute(__GRM_DATA, __STABLE_DATA, ::lrpar::ctbuilder::wincode::config::Configuration::default().with_fixint_encoding())
1382 }
1383 ::lrpar::ctbuilder::SerialisationFormat::VariableSizedInteger => {
1384 ::lrpar::ctbuilder::_reconstitute(__GRM_DATA, __STABLE_DATA, ::lrpar::ctbuilder::wincode::config::Configuration::default().with_varint_encoding())
1385 }
1386 _ => {
1387 panic!("Parser source was generated using unknown `SerialisationFormat`: {:?}", #serialisation_format_str)
1388 }
1389 }
1390 }
1391 )
1392 }
1393
1394 #[allow(dead_code)]
1395 pub fn parse #generics (
1396 lexer: &'lexer dyn ::lrpar::NonStreamingLexer<'input, #lexertypest>,
1397 #parse_fn_parse_param
1398 ) -> #parse_fn_return_ty
1399 #where_clause
1400 {
1401 let __data = __lrpar_parser_data();
1402 let grm = __data.grm();
1403 let stable = __data.stable();
1404 #run_parser
1405 }
1406 })
1407 }
1408
1409 fn gen_rule_consts(
1410 &self,
1411 grm: &YaccGrammar<StorageT>,
1412 ) -> Result<TokenStream, proc_macro2::LexError> {
1413 let mut toks = TokenStream::new();
1414 for ridx in grm.iter_rules() {
1415 if !grm.rule_to_prods(ridx).contains(&grm.start_prod()) {
1416 let r_const = format_ident!("R_{}", grm.rule_name_str(ridx).to_ascii_uppercase());
1417 let storage_ty = str::parse::<TokenStream>(type_name::<StorageT>())?;
1418 let ridx = UnsuffixedUsize(usize::from(ridx));
1419 toks.extend(quote! {
1420 #[allow(dead_code)]
1421 pub const #r_const: #storage_ty = #ridx;
1422 });
1423 }
1424 }
1425 Ok(toks)
1426 }
1427
1428 fn gen_token_epp(
1429 &self,
1430 grm: &YaccGrammar<StorageT>,
1431 ) -> Result<TokenStream, proc_macro2::LexError> {
1432 let mut tidxs = Vec::new();
1433 for tidx in grm.iter_tidxs() {
1434 tidxs.push(QuoteOption(grm.token_epp(tidx)));
1435 }
1436 let const_epp_ident = format_ident!("{}EPP", GLOBAL_PREFIX);
1437 let storage_ty = str::parse::<TokenStream>(type_name::<StorageT>())?;
1438 Ok(quote! {
1439 const #const_epp_ident: &[::std::option::Option<&str>] = &[
1440 #(#tidxs,)*
1441 ];
1442
1443 #[allow(dead_code)]
1446 pub fn token_epp<'a>(tidx: ::cfgrammar::TIdx<#storage_ty>) -> ::std::option::Option<&'a str> {
1447 #const_epp_ident[usize::from(tidx)]
1448 }
1449 })
1450 }
1451
1452 fn gen_wrappers(&self, grm: &YaccGrammar<StorageT>) -> Result<TokenStream, Box<dyn Error>> {
1454 let parsed_parse_generics = make_generics(grm.parse_generics().as_deref())?;
1455 let (generics, type_generics, where_clause) = parsed_parse_generics.split_for_impl();
1456
1457 let (parse_paramname, parse_paramdef);
1458 match grm.parse_param() {
1459 Some((name, tyname)) => {
1460 parse_paramname = str::parse::<TokenStream>(name)?;
1461 let ty = str::parse::<TokenStream>(tyname)?;
1462 parse_paramdef = quote!(#parse_paramname: #ty);
1463 }
1464 None => {
1465 parse_paramname = quote!(());
1466 parse_paramdef = quote! {_: ()};
1467 }
1468 };
1469
1470 let mut wrappers = TokenStream::new();
1471 for pidx in grm.iter_pidxs() {
1472 let ridx = grm.prod_to_rule(pidx);
1473
1474 let wrapper_fn = format_ident!("{}wrapper_{}", ACTION_PREFIX, usize::from(pidx));
1478 let ridx_var = format_ident!("{}ridx", ACTION_PREFIX);
1479 let lexer_var = format_ident!("{}lexer", ACTION_PREFIX);
1480 let span_var = format_ident!("{}span", ACTION_PREFIX);
1481 let args_var = format_ident!("{}args", ACTION_PREFIX);
1482 let storaget = str::parse::<TokenStream>(type_name::<StorageT>())?;
1483 let lexertypest = str::parse::<TokenStream>(type_name::<LexerTypesT>())?;
1484 let actionskind = str::parse::<TokenStream>(ACTIONS_KIND)?;
1485 let edition_lifetime = if self.rust_edition != RustEdition::Rust2015 {
1486 Some(quote!('_,))
1487 } else {
1488 None
1489 };
1490 let mut wrapper_fn_body = TokenStream::new();
1491 if grm.action(pidx).is_some() {
1492 for i in 0..grm.prod(pidx).len() {
1494 let arg = format_ident!("{}arg_{}", ACTION_PREFIX, i + 1);
1495 wrapper_fn_body.extend(match grm.prod(pidx)[i] {
1496 Symbol::Rule(ref_ridx) => {
1497 let ref_ridx = usize::from(ref_ridx);
1498 let actionvariant = format_ident!("{}{}", ACTIONS_KIND_PREFIX, ref_ridx);
1499 quote! {
1500 #[allow(clippy::let_unit_value)]
1501 let #arg = match #args_var.next().unwrap() {
1502 ::lrpar::parser::AStackType::ActionType(#actionskind::#type_generics::#actionvariant(x)) => x,
1503 _ => unreachable!()
1504 };
1505 }
1506 }
1507 Symbol::Token(_) => {
1508 quote! {
1509 let #arg = match #args_var.next().unwrap() {
1510 ::lrpar::parser::AStackType::Lexeme(l) => {
1511 if l.faulty() {
1512 Err(l)
1513 } else {
1514 Ok(l)
1515 }
1516 },
1517 ::lrpar::parser::AStackType::ActionType(_) => unreachable!()
1518 };
1519 }
1520 }
1521 })
1522 }
1523
1524 let args = (0..grm.prod(pidx).len())
1526 .map(|i| format_ident!("{}arg_{}", ACTION_PREFIX, i + 1))
1527 .collect::<Vec<_>>();
1528 let action_fn = format_ident!("{}action_{}", ACTION_PREFIX, usize::from(pidx));
1529 let actionsvariant = format_ident!("{}{}", ACTIONS_KIND_PREFIX, usize::from(ridx));
1530
1531 wrapper_fn_body.extend(match grm.actiontype(ridx) {
1532 Some(s) if s == "()" => {
1533 quote! {
1537 #action_fn(#ridx_var, #lexer_var, #span_var, #parse_paramname, #(#args,)*);
1538 #actionskind::#type_generics::#actionsvariant(())
1539 }
1540 }
1541 _ => {
1542 quote! {
1543 #actionskind::#type_generics::#actionsvariant(#action_fn(#ridx_var, #lexer_var, #span_var, #parse_paramname, #(#args,)*))
1544 }
1545 }
1546 })
1547 } else if pidx == grm.start_prod() {
1548 wrapper_fn_body.extend(quote!(unreachable!()));
1549 } else {
1550 unreachable!(
1551 "Production in rule '{}' must have an action body, which should have been handled by gen_user_actions.",
1552 grm.rule_name_str(grm.prod_to_rule(pidx))
1553 );
1554 };
1555
1556 let attrib = if pidx == grm.start_prod() {
1557 Some(quote!(#[allow(unused_variables)]))
1559 } else {
1560 None
1561 };
1562 wrappers.extend(quote! {
1563 #attrib
1564 fn #wrapper_fn #generics (
1565 #ridx_var: ::cfgrammar::RIdx<#storaget>,
1566 #lexer_var: &'lexer dyn ::lrpar::NonStreamingLexer<'input, #lexertypest>,
1567 #span_var: ::cfgrammar::Span,
1568 mut #args_var: ::std::vec::Drain<#edition_lifetime ::lrpar::parser::AStackType<<#lexertypest as ::lrpar::LexerTypes>::LexemeT, #actionskind #type_generics>>,
1569 #parse_paramdef
1570 ) -> #actionskind #type_generics
1571 #where_clause
1572 {
1573 #wrapper_fn_body
1574 }
1575 })
1576 }
1577 let mut actionskindvariants = Vec::new();
1578 let actionskindhidden = format_ident!("_{}", ACTIONS_KIND_HIDDEN);
1579 let actionskind = str::parse::<TokenStream>(ACTIONS_KIND).unwrap();
1580 let mut phantom_data_type = Vec::new();
1581 for ridx in grm.iter_rules() {
1582 if let Some(actiont) = grm.actiontype(ridx) {
1583 let actionskindvariant =
1584 format_ident!("{}{}", ACTIONS_KIND_PREFIX, usize::from(ridx));
1585 let actiont = str::parse::<TokenStream>(actiont).unwrap();
1586 actionskindvariants.push(quote! {
1587 #actionskindvariant(#actiont)
1588 })
1589 }
1590 }
1591 for lifetime in parsed_parse_generics.lifetimes() {
1592 let lifetime = &lifetime.lifetime;
1593 phantom_data_type.push(quote! { &#lifetime () });
1594 }
1595 for type_param in parsed_parse_generics.type_params() {
1596 let ident = &type_param.ident;
1597 phantom_data_type.push(quote! { #ident });
1598 }
1599 actionskindvariants.push(quote! {
1600 #actionskindhidden(::std::marker::PhantomData<(#(#phantom_data_type,)*)>)
1601 });
1602 wrappers.extend(quote! {
1603 #[allow(dead_code)]
1604 enum #actionskind #generics #where_clause {
1605 #(#actionskindvariants,)*
1606 }
1607 });
1608 Ok(wrappers)
1609 }
1610
1611 fn gen_user_actions(&self, grm: &YaccGrammar<StorageT>) -> Result<TokenStream, Box<dyn Error>> {
1613 let programs = grm
1614 .programs()
1615 .as_ref()
1616 .map(|s| str::parse::<TokenStream>(s))
1617 .transpose()?;
1618 let mut action_fns = TokenStream::new();
1619 let parsed_parse_generics = make_generics(grm.parse_generics().as_deref())?;
1621 let (generics, _, where_clause) = parsed_parse_generics.split_for_impl();
1622 let (parse_paramname, parse_paramdef, parse_param_unit);
1623 match grm.parse_param() {
1624 Some((name, tyname)) => {
1625 parse_param_unit = tyname.trim() == "()";
1626 parse_paramname = str::parse::<TokenStream>(name)?;
1627 let ty = str::parse::<TokenStream>(tyname)?;
1628 parse_paramdef = quote!(#parse_paramname: #ty);
1629 }
1630 None => {
1631 parse_param_unit = true;
1632 parse_paramname = quote!(());
1633 parse_paramdef = quote! {_: ()};
1634 }
1635 };
1636 for pidx in grm.iter_pidxs() {
1637 if pidx == grm.start_prod() {
1638 continue;
1639 }
1640
1641 let mut args = Vec::with_capacity(grm.prod(pidx).len());
1643 for i in 0..grm.prod(pidx).len() {
1644 let argt = match grm.prod(pidx)[i] {
1645 Symbol::Rule(ref_ridx) => {
1646 let action_type = grm.actiontype(ref_ridx)
1647 .as_ref()
1648 .expect("actiontype should have been checked during complete_and_validate for this YaccKind");
1649 str::parse::<TokenStream>(action_type)?
1650 }
1651 Symbol::Token(_) => {
1652 let lexemet =
1653 str::parse::<TokenStream>(type_name::<LexerTypesT::LexemeT>())?;
1654 quote!(::std::result::Result<#lexemet, #lexemet>)
1655 }
1656 };
1657 let arg = format_ident!("{}arg_{}", ACTION_PREFIX, i + 1);
1658 args.push(quote!(mut #arg: #argt));
1659 }
1660
1661 let returnt = {
1665 let actiont = grm.actiontype(grm.prod_to_rule(pidx)).as_ref().unwrap();
1666 if actiont == "()" {
1667 None
1668 } else {
1669 let actiont = str::parse::<TokenStream>(actiont)?;
1670 Some(quote!( -> #actiont))
1671 }
1672 };
1673 let action_fn = format_ident!("{}action_{}", ACTION_PREFIX, usize::from(pidx));
1674 let lexer_var = format_ident!("{}lexer", ACTION_PREFIX);
1675 let span_var = format_ident!("{}span", ACTION_PREFIX);
1676 let ridx_var = format_ident!("{}ridx", ACTION_PREFIX);
1677 let storaget = str::parse::<TokenStream>(type_name::<StorageT>())?;
1678 let lexertypest = str::parse::<TokenStream>(type_name::<LexerTypesT>())?;
1679 let bind_parse_param = if !parse_param_unit {
1680 Some(quote! {let _ = #parse_paramname;})
1681 } else {
1682 None
1683 };
1684
1685 let pre_action = grm.action(pidx).as_ref().expect("action code should have been checked during complete_and_validate for this YaccKind");
1688 let mut last = 0;
1689 let mut outs = String::new();
1690 loop {
1691 match pre_action[last..].find('$') {
1692 Some(off) => {
1693 if pre_action[last + off..].starts_with("$$") {
1694 outs.push_str(&pre_action[last..last + off + "$".len()]);
1695 last = last + off + "$$".len();
1696 } else if pre_action[last + off..].starts_with("$lexer") {
1697 outs.push_str(&pre_action[last..last + off]);
1698 write!(outs, "{prefix}lexer", prefix = ACTION_PREFIX).ok();
1699 last = last + off + "$lexer".len();
1700 } else if pre_action[last + off..].starts_with("$span") {
1701 outs.push_str(&pre_action[last..last + off]);
1702 write!(outs, "{prefix}span", prefix = ACTION_PREFIX).ok();
1703 last = last + off + "$span".len();
1704 } else if last + off + 1 < pre_action.len()
1705 && pre_action[last + off + 1..].starts_with(|c: char| c.is_numeric())
1706 {
1707 outs.push_str(&pre_action[last..last + off]);
1708 write!(outs, "{prefix}arg_", prefix = ACTION_PREFIX).ok();
1709 last = last + off + "$".len();
1710 } else {
1711 unreachable!("action variables checked during complete_and_validate");
1712 }
1713 }
1714 None => {
1715 outs.push_str(&pre_action[last..]);
1716 break;
1717 }
1718 }
1719 }
1720
1721 let action_body = str::parse::<TokenStream>(&outs)?;
1722 action_fns.extend(quote! {
1723 #[allow(clippy::too_many_arguments)]
1724 fn #action_fn #generics (
1725 #ridx_var: ::cfgrammar::RIdx<#storaget>,
1726 #lexer_var: &'lexer dyn ::lrpar::NonStreamingLexer<'input, #lexertypest>,
1727 #span_var: ::cfgrammar::Span,
1728 #parse_paramdef,
1729 #(#args,)*
1730 ) #returnt
1731 #where_clause
1732 {
1733 #bind_parse_param
1734 #action_body
1735 }
1736 })
1737 }
1738 Ok(quote! {
1739 #programs
1740 #action_fns
1741 })
1742 }
1743
1744 fn user_start_ridx(&self, grm: &YaccGrammar<StorageT>) -> RIdx<StorageT> {
1748 debug_assert_eq!(grm.prod(grm.start_prod()).len(), 1);
1749 match grm.prod(grm.start_prod())[0] {
1750 Symbol::Rule(ridx) => ridx,
1751 _ => unreachable!(),
1752 }
1753 }
1754}
1755
1756#[doc(hidden)]
1759pub struct ParserData<StorageT: Eq + Hash> {
1760 grm: YaccGrammar<StorageT>,
1761 stable: StateTable<StorageT>,
1762}
1763
1764impl<StorageT: Eq + Hash> ParserData<StorageT> {
1765 pub fn grm(&self) -> &YaccGrammar<StorageT> {
1766 &self.grm
1767 }
1768
1769 pub fn stable(&self) -> &StateTable<StorageT> {
1770 &self.stable
1771 }
1772}
1773
1774#[doc(hidden)]
1777pub fn _reconstitute<
1778 C: wincode::config::Config + Clone + Copy,
1779 StorageT: SchemaReadOwned<C, Dst = StorageT> + Eq + Hash + PrimInt + Unsigned + 'static,
1780>(
1781 grm_buf: &[u8],
1782 stable_buf: &[u8],
1783 config: C,
1784) -> ParserData<StorageT> {
1785 let grm: YaccGrammar<StorageT> = wincode::config::deserialize_from(grm_buf, config).unwrap();
1786 let stable = wincode::config::deserialize_from(stable_buf, config).unwrap();
1787 ParserData { grm, stable }
1788}
1789
1790pub struct CTParser<StorageT = u32>
1792where
1793 StorageT: Eq + Hash,
1794{
1795 regenerated: bool,
1796 rule_ids: HashMap<String, StorageT>,
1797 yacc_grammar: YaccGrammar<StorageT>,
1798 grammar_src: String,
1799 grammar_path: PathBuf,
1800 conflicts: Option<(StateGraph<StorageT>, StateTable<StorageT>)>,
1801}
1802
1803impl<StorageT> CTParser<StorageT>
1804where
1805 StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
1806 usize: AsPrimitive<StorageT>,
1807{
1808 pub fn regenerated(&self) -> bool {
1810 self.regenerated
1811 }
1812
1813 pub fn token_map(&self) -> &HashMap<String, StorageT> {
1816 &self.rule_ids
1817 }
1818
1819 #[allow(private_interfaces)]
1825 pub fn conflicts(
1826 &self,
1827 _: crate::unstable::UnstableApi,
1828 ) -> Option<(
1829 &YaccGrammar<StorageT>,
1830 &StateGraph<StorageT>,
1831 &StateTable<StorageT>,
1832 &Conflicts<StorageT>,
1833 )> {
1834 if let Some((sgraph, stable)) = &self.conflicts {
1835 return Some((
1836 &self.yacc_grammar,
1837 sgraph,
1838 stable,
1839 stable.conflicts().unwrap(),
1840 ));
1841 }
1842 None
1843 }
1844
1845 #[doc(hidden)]
1846 pub fn yacc_grammar(&self) -> &YaccGrammar<StorageT> {
1847 &self.yacc_grammar
1848 }
1849 #[doc(hidden)]
1850 pub fn grammar_src(&self) -> &str {
1851 &self.grammar_src
1852 }
1853 #[doc(hidden)]
1854 pub fn grammar_path(&self) -> &Path {
1855 self.grammar_path.as_path()
1856 }
1857}
1858
1859fn indent(indent: &str, s: &str) -> String {
1870 format!("{indent}{}\n", s.trim_end_matches('\n')).replace('\n', &format!("\n{}", indent))
1871}
1872
1873fn make_generics(parse_generics: Option<&str>) -> Result<Generics, Box<dyn Error>> {
1874 if let Some(parse_generics) = parse_generics {
1875 let tokens = str::parse::<TokenStream>(parse_generics)?;
1876 match syn::parse2(quote!(<'lexer, 'input: 'lexer, #tokens>)) {
1877 Ok(res) => Ok(res),
1878 Err(err) => Err(format!("unable to parse %parse-generics: {}", err).into()),
1879 }
1880 } else {
1881 Ok(parse_quote!(<'lexer, 'input: 'lexer>))
1882 }
1883}
1884
1885#[cfg(all(not(target_arch = "wasm32"), test))]
1887mod test {
1888 use std::{fs::File, io::Write, path::PathBuf};
1889
1890 use super::{CTConflictsError, CTParserBuilder};
1891 use crate::test_utils::TestLexerTypes;
1892 use cfgrammar::yacc::{YaccKind, YaccOriginalActionKind};
1893 use tempfile::TempDir;
1894
1895 #[test]
1896 fn test_conflicts() {
1897 let temp = TempDir::new().unwrap();
1898 let mut file_path = PathBuf::from(temp.as_ref());
1899 file_path.push("grm.y");
1900 let mut f = File::create(&file_path).unwrap();
1901 let _ = f.write_all(
1902 "%start A
1903%%
1904A : 'a' 'b' | B 'b';
1905B : 'a' | C;
1906C : 'a';"
1907 .as_bytes(),
1908 );
1909
1910 match CTParserBuilder::<TestLexerTypes>::new()
1911 .error_on_conflicts(false)
1912 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
1913 .grammar_path(file_path.to_str().unwrap())
1914 .output_path(file_path.with_extension("ignored"))
1915 .build()
1916 .unwrap()
1917 .conflicts(crate::unstable::UnstableApi)
1918 {
1919 Some((_, _, _, conflicts)) => {
1920 assert_eq!(conflicts.sr_len(), 1);
1921 assert_eq!(conflicts.rr_len(), 1);
1922 }
1923 None => panic!("Expected error data"),
1924 }
1925 }
1926
1927 #[test]
1928 fn test_conflicts_error() {
1929 let temp = TempDir::new().unwrap();
1930 let mut file_path = PathBuf::from(temp.as_ref());
1931 file_path.push("grm.y");
1932 let mut f = File::create(&file_path).unwrap();
1933 let _ = f.write_all(
1934 "%start A
1935%%
1936A : 'a' 'b' | B 'b';
1937B : 'a' | C;
1938C : 'a';"
1939 .as_bytes(),
1940 );
1941
1942 match CTParserBuilder::<TestLexerTypes>::new()
1943 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
1944 .grammar_path(file_path.to_str().unwrap())
1945 .output_path(file_path.with_extension("ignored"))
1946 .build()
1947 {
1948 Ok(_) => panic!("Expected error"),
1949 Err(e) => {
1950 let cs = e.downcast_ref::<CTConflictsError<u16>>();
1951 assert_eq!(cs.unwrap().stable.conflicts().unwrap().rr_len(), 1);
1952 assert_eq!(cs.unwrap().stable.conflicts().unwrap().sr_len(), 1);
1953 }
1954 }
1955 }
1956
1957 #[test]
1958 fn test_expect_error() {
1959 let temp = TempDir::new().unwrap();
1960 let mut file_path = PathBuf::from(temp.as_ref());
1961 file_path.push("grm.y");
1962 let mut f = File::create(&file_path).unwrap();
1963 let _ = f.write_all(
1964 "%start A
1965%expect 2
1966%%
1967A: 'a' 'b' | B 'b';
1968B: 'a';"
1969 .as_bytes(),
1970 );
1971
1972 match CTParserBuilder::<TestLexerTypes>::new()
1973 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
1974 .grammar_path(file_path.to_str().unwrap())
1975 .output_path(file_path.with_extension("ignored"))
1976 .build()
1977 {
1978 Ok(_) => panic!("Expected error"),
1979 Err(e) => {
1980 let cs = e.downcast_ref::<CTConflictsError<u16>>();
1981 assert_eq!(cs.unwrap().stable.conflicts().unwrap().rr_len(), 0);
1982 assert_eq!(cs.unwrap().stable.conflicts().unwrap().sr_len(), 1);
1983 }
1984 }
1985 }
1986
1987 #[test]
1988 fn test_expectrr_error() {
1989 let temp = TempDir::new().unwrap();
1990 let mut file_path = PathBuf::from(temp.as_ref());
1991 file_path.push("grm.y");
1992 let mut f = File::create(&file_path).unwrap();
1993 let _ = f.write_all(
1994 "%start A
1995%expect 1
1996%expect-rr 2
1997%%
1998A : 'a' 'b' | B 'b';
1999B : 'a' | C;
2000C : 'a';"
2001 .as_bytes(),
2002 );
2003
2004 match CTParserBuilder::<TestLexerTypes>::new()
2005 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
2006 .grammar_path(file_path.to_str().unwrap())
2007 .output_path(file_path.with_extension("ignored"))
2008 .build()
2009 {
2010 Ok(_) => panic!("Expected error"),
2011 Err(e) => {
2012 let cs = e.downcast_ref::<CTConflictsError<u16>>();
2013 assert_eq!(cs.unwrap().stable.conflicts().unwrap().rr_len(), 1);
2014 assert_eq!(cs.unwrap().stable.conflicts().unwrap().sr_len(), 1);
2015 }
2016 }
2017 }
2018
2019 #[test]
2020 fn test_invalid_identifier_in_derived_mod_name() {
2023 let temp = TempDir::new().unwrap();
2024 let mut file_path = PathBuf::from(temp.as_ref());
2025 file_path.push("contains-a-dash.y");
2026 let mut f = File::create(&file_path).unwrap();
2027 let _ = f.write_all(
2028 "%start A
2029%%
2030A : 'a';"
2031 .as_bytes(),
2032 );
2033 match CTParserBuilder::<TestLexerTypes>::new()
2034 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
2035 .grammar_path(file_path.to_str().unwrap())
2036 .output_path(file_path.with_extension("ignored"))
2037 .build()
2038 {
2039 Ok(_) => panic!("Expected error"),
2040 Err(e) => {
2041 let err_string = e.to_string();
2042 assert_eq!(
2043 err_string,
2044 "CTParserBuilder::mod_name(\"contains-a-dash_y\") is not a valid rust identifier due to 'unexpected token'"
2045 );
2046 }
2047 }
2048 }
2049
2050 #[cfg(test)]
2051 #[test]
2052 fn test_recoverer_header() -> Result<(), Box<dyn std::error::Error>> {
2053 use crate::RecoveryKind as RK;
2054 #[rustfmt::skip]
2055 let recovery_kinds = [
2056 (Some(RK::None), Some(RK::None), Some(RK::None)),
2059 (Some(RK::None), Some(RK::CPCTPlus), Some(RK::None)),
2060 (Some(RK::CPCTPlus), Some(RK::CPCTPlus), Some(RK::CPCTPlus)),
2061 (Some(RK::CPCTPlus), Some(RK::None), Some(RK::CPCTPlus)),
2062 (None, Some(RK::CPCTPlus), Some(RK::CPCTPlus)),
2063 (None, Some(RK::None), Some(RK::None)),
2064 (None, None, Some(RK::CPCTPlus)),
2065 (Some(RK::None), None, Some(RK::None)),
2066 (Some(RK::CPCTPlus), None, Some(RK::CPCTPlus)),
2067 ];
2068
2069 for (i, (builder_arg, header_arg, expected_rk)) in
2070 recovery_kinds.iter().cloned().enumerate()
2071 {
2072 let y_src = if let Some(header_arg) = header_arg {
2073 format!(
2074 "\
2075 %grmtools{{yacckind: Original(NoAction), recoverer: {}}} \
2076 %% \
2077 start: ; \
2078 ",
2079 match header_arg {
2080 RK::None => "RecoveryKind::None",
2081 RK::CPCTPlus => "RecoveryKind::CPCTPlus",
2082 }
2083 )
2084 } else {
2085 r#"
2086 %grmtools{yacckind: Original(NoAction)}
2087 %%
2088 Start: ;
2089 "#
2090 .to_string()
2091 };
2092 let out_dir = std::env::var("OUT_DIR").unwrap();
2093 let y_path = format!("{out_dir}/recoverykind_test_{i}.y");
2094 let y_out_path = format!("{y_path}.rs");
2095 std::fs::File::create(y_path.clone()).unwrap();
2096 std::fs::write(y_path.clone(), y_src).unwrap();
2097 let mut cp_builder = CTParserBuilder::<TestLexerTypes>::new();
2098 cp_builder = cp_builder
2099 .output_path(y_out_path.clone())
2100 .grammar_path(y_path.clone());
2101 cp_builder = if let Some(builder_arg) = builder_arg {
2102 cp_builder.recoverer(builder_arg)
2103 } else {
2104 cp_builder
2105 }
2106 .inspect_recoverer(Box::new(move |rk| {
2107 if matches!(
2108 (rk, expected_rk),
2109 (RK::None, Some(RK::None)) | (RK::CPCTPlus, Some(RK::CPCTPlus))
2110 ) {
2111 Ok(())
2112 } else {
2113 panic!("Unexpected recovery kind")
2114 }
2115 }));
2116 cp_builder.build()?;
2117 }
2118 Ok(())
2119 }
2120}