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, Span, 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
731 let yacc_diag = SpannedDiagnosticFormatter::new(&inc, grmp);
732 let parsed_header = GrmtoolsSectionParser::new(&inc, false).parse();
733 match parsed_header {
734 Err(errs) => {
735 let mut out = String::new();
736 out.push_str(&format!(
737 "\n{ERROR}{}\n",
738 yacc_diag.file_location_msg(" parsing the `%grmtools` section", None)
739 ));
740 for e in errs {
741 out.push_str(&indent(" ", &yacc_diag.format_error(e).to_string()));
742 }
743 return Err(ErrorString(out))?;
744 }
745 Ok((parsed_header, _)) => {
746 header.merge_from(parsed_header)?;
747 self.yacckind = header
748 .get("yacckind")
749 .map(|HeaderValue(_, val)| val)
750 .map(YaccKind::try_from)
751 .transpose()?;
752 header.mark_used(&"yacckind".to_string());
753 let ast_validation = if let Some(ast) = &self.from_ast {
754 ast.clone()
755 } else if let Some(yk) = self.yacckind {
756 ASTWithValidityInfo::new(yk, &inc)
757 } else {
758 Err("Missing 'yacckind'".to_string())?
759 };
760
761 header.mark_used(&"recoverer".to_string());
762 let rk_val = header.get("recoverer").map(|HeaderValue(_, rk_val)| rk_val);
763
764 if let Some(rk_val) = rk_val {
765 self.recoverer = Some(RecoveryKind::try_from(rk_val)?);
766 } else {
767 self.recoverer = Some(RecoveryKind::CPCTPlus);
769 }
770 header.mark_used(&"serialisation_format".to_string());
771 if let Some(ec_val) = header
772 .get("serialisation_format")
773 .map(|HeaderValue(_, ec_val)| ec_val)
774 {
775 self.serialisation_format = Some(SerialisationFormat::try_from(ec_val)?);
776 } else {
777 self.serialisation_format = Some(SerialisationFormat::VariableSizedInteger);
778 }
779
780 self.yacckind = Some(ast_validation.yacc_kind());
781 let warnings = ast_validation.ast().warnings();
782 let res = YaccGrammar::<StorageT>::new_from_ast_with_validity_info(&ast_validation);
783 let grm = match res {
784 Ok(_) if self.warnings_are_errors && !warnings.is_empty() => {
785 let mut out = String::new();
786 out.push_str(&format!(
787 "\n{ERROR}{}\n",
788 yacc_diag.file_location_msg("", None)
789 ));
790 for e in warnings {
791 out.push_str(&format!(
792 "{}\n",
793 indent(" ", &yacc_diag.format_warning(e).to_string())
794 ));
795 }
796 return Err(ErrorString(out))?;
797 }
798 Ok(grm) => {
799 if !warnings.is_empty() {
800 for w in warnings {
801 let ws_loc = yacc_diag.file_location_msg("", None);
802 let ws = indent(" ", &yacc_diag.format_warning(w).to_string());
803 if std::env::var("OUT_DIR").is_ok() && self.show_warnings {
805 for line in ws_loc.lines().chain(ws.lines()) {
806 println!("cargo:warning={}", line);
807 }
808 } else if self.show_warnings {
809 eprintln!("{}", ws_loc);
810 eprintln!("{WARNING} {}", ws);
811 }
812 }
813 }
814 grm
815 }
816 Err(errs) => {
817 let mut out = String::new();
818 out.push_str(&format!(
819 "\n{ERROR}{}\n",
820 yacc_diag.file_location_msg("", None)
821 ));
822 for e in errs {
823 out.push_str(&indent(" ", &yacc_diag.format_error(e).to_string()));
824 out.push('\n');
825 }
826
827 return Err(ErrorString(out))?;
828 }
829 };
830
831 #[cfg(test)]
832 if let Some(cb) = &self.inspect_callback {
833 cb(self.recoverer.expect("has a default value"))?;
834 }
835
836 let rule_ids = grm
837 .tokens_map()
838 .iter()
839 .map(|(&n, &i)| (n.to_owned(), i.as_storaget()))
840 .collect::<HashMap<_, _>>();
841
842 let derived_mod_name = match self.mod_name {
843 Some(s) => s.to_owned(),
844 None => {
845 let mut stem = grmp.to_str().unwrap();
850 loop {
851 let new_stem = Path::new(stem).file_stem().unwrap().to_str().unwrap();
852 if stem == new_stem {
853 break;
854 }
855 stem = new_stem;
856 }
857 format!("{}_y", stem)
858 }
859 };
860
861 let cache = self.rebuild_cache(&derived_mod_name, &grm);
862
863 if let Ok(ref inmd) = fs::metadata(grmp)
871 && let Ok(ref out_rs_md) = fs::metadata(outp)
872 && FileTime::from_last_modification_time(out_rs_md)
873 > FileTime::from_last_modification_time(inmd)
874 && let Ok(outc) = read_to_string(outp)
875 {
876 if outc.contains(&cache.to_string()) {
877 return Ok(CTParser {
878 regenerated: false,
879 rule_ids,
880 yacc_grammar: grm,
881 grammar_src: inc,
882 grammar_path: self.grammar_path.unwrap(),
883 conflicts: None,
884 });
885 } else {
886 #[cfg(grmtools_extra_checks)]
887 if std::env::var("CACHE_EXPECTED").is_ok() {
888 eprintln!("outc: {}", outc);
889 eprintln!("using cache: {}", cache,);
890 panic!("The cache regenerated however, it was expected to match");
892 }
893 }
894 }
895
896 fs::remove_file(outp).ok();
903
904 let (sgraph, stable) = from_yacc(&grm, Minimiser::Pager)?;
905 if self.error_on_conflicts
906 && let Some(c) = stable.conflicts()
907 {
908 match (grm.expect(), grm.expectrr()) {
909 (Some(i), Some(j)) if i == c.sr_len() && j == c.rr_len() => (),
910 (Some(i), None) if i == c.sr_len() && 0 == c.rr_len() => (),
911 (None, Some(j)) if 0 == c.sr_len() && j == c.rr_len() => (),
912 (None, None) if 0 == c.rr_len() && 0 == c.sr_len() => (),
913 _ => {
914 let conflicts_diagnostic = yacc_diag.format_conflicts::<LexerTypesT>(
915 &grm,
916 ast_validation.ast(),
917 c,
918 &sgraph,
919 &stable,
920 );
921 return Err(Box::new(CTConflictsError {
922 conflicts_diagnostic,
923 phantom: PhantomData,
924 #[cfg(test)]
925 stable,
926 }));
927 }
928 }
929 }
930
931 if let Some(ref mut inspector_rt) = self.inspect_rt {
932 let rt: RTParserBuilder<'_, StorageT, LexerTypesT> =
933 RTParserBuilder::new(&grm, &stable);
934 let rt = if let Some(rk) = self.recoverer {
935 rt.recoverer(rk)
936 } else {
937 rt
938 };
939 inspector_rt(&mut header, rt, &rule_ids, grmp)?
940 }
941
942 let unused_keys = header.unused();
943 if !unused_keys.is_empty() {
944 return Err(format!("Unused keys in header: {}", unused_keys.join(", ")).into());
945 }
946 let missing_keys = header
947 .missing()
948 .iter()
949 .map(|s| s.as_str())
950 .collect::<Vec<_>>();
951 if !missing_keys.is_empty() {
952 return Err(format!(
953 "Required values were missing from the header: {}",
954 missing_keys.join(", ")
955 )
956 .into());
957 }
958
959 self.output_file(
960 &grm,
961 &stable,
962 &derived_mod_name,
963 outp,
964 &format!("/* CACHE INFORMATION {} */\n", cache),
965 &yacc_diag,
966 )?;
967 let conflicts = if stable.conflicts().is_some() {
968 Some((sgraph, stable))
969 } else {
970 None
971 };
972 Ok(CTParser {
973 regenerated: true,
974 rule_ids,
975 yacc_grammar: grm,
976 grammar_src: inc,
977 grammar_path: self.grammar_path.unwrap(),
978 conflicts,
979 })
980 }
981 }
982 }
983
984 #[deprecated(
991 since = "0.11.0",
992 note = "Please use grammar_in_src_dir(), build(), and token_map() instead"
993 )]
994 #[allow(deprecated)]
995 pub fn process_file_in_src(
996 &mut self,
997 srcp: &str,
998 ) -> Result<HashMap<String, StorageT>, Box<dyn Error>> {
999 let mut inp = current_dir()?;
1000 inp.push("src");
1001 inp.push(srcp);
1002 let mut outp = PathBuf::new();
1003 outp.push(var("OUT_DIR").unwrap());
1004 outp.push(Path::new(srcp).parent().unwrap().to_str().unwrap());
1005 create_dir_all(&outp)?;
1006 let mut leaf = Path::new(srcp)
1007 .file_name()
1008 .unwrap()
1009 .to_str()
1010 .unwrap()
1011 .to_owned();
1012 write!(leaf, ".{}", RUST_FILE_EXT).ok();
1013 outp.push(leaf);
1014 self.process_file(inp, outp)
1015 }
1016
1017 #[deprecated(
1053 since = "0.11.0",
1054 note = "Please use grammar_path(), output_path(), build(), and token_map() instead"
1055 )]
1056 pub fn process_file<P, Q>(
1057 &mut self,
1058 inp: P,
1059 outp: Q,
1060 ) -> Result<HashMap<String, StorageT>, Box<dyn Error>>
1061 where
1062 P: AsRef<Path>,
1063 Q: AsRef<Path>,
1064 {
1065 self.grammar_path = Some(inp.as_ref().to_owned());
1066 self.output_path = Some(outp.as_ref().to_owned());
1067 let cl: CTParserBuilder<LexerTypesT> = CTParserBuilder {
1068 grammar_path: self.grammar_path.clone(),
1069 grammar_src: None,
1070 from_ast: None,
1071 output_path: self.output_path.clone(),
1072 mod_name: self.mod_name,
1073 recoverer: self.recoverer,
1074 yacckind: self.yacckind,
1075 error_on_conflicts: self.error_on_conflicts,
1076 warnings_are_errors: self.warnings_are_errors,
1077 show_warnings: self.show_warnings,
1078 visibility: self.visibility.clone(),
1079 rust_edition: self.rust_edition,
1080 inspect_rt: None,
1081 serialisation_format: self.serialisation_format,
1082 #[cfg(test)]
1083 inspect_callback: None,
1084 phantom: PhantomData,
1085 };
1086 Ok(cl.build()?.rule_ids)
1087 }
1088
1089 fn output_file<P: AsRef<Path>>(
1090 &self,
1091 grm: &YaccGrammar<StorageT>,
1092 stable: &StateTable<StorageT>,
1093 mod_name: &str,
1094 outp_rs: P,
1095 cache: &str,
1096 diag: &SpannedDiagnosticFormatter,
1097 ) -> Result<(), Box<dyn Error>> {
1098 let visibility = self.visibility.clone();
1099 let user_actions = if let Some(
1100 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools,
1101 ) = self.yacckind
1102 {
1103 Some(self.gen_user_actions(grm, diag)?)
1104 } else {
1105 None
1106 };
1107 let rule_consts = self.gen_rule_consts(grm)?;
1108 let token_epp = self.gen_token_epp(grm)?;
1109 let parse_function = self.gen_parse_function(grm, stable)?;
1110 let action_wrappers = match self.yacckind.unwrap() {
1111 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1112 Some(self.gen_wrappers(grm)?)
1113 }
1114 YaccKind::Original(YaccOriginalActionKind::NoAction)
1115 | YaccKind::Original(YaccOriginalActionKind::GenericParseTree) => None,
1116 _ => unreachable!(),
1117 };
1118
1119 let additional_decls =
1120 if let Some(YaccKind::Original(YaccOriginalActionKind::GenericParseTree)) =
1121 self.yacckind
1122 {
1123 Some(quote! {
1126 #[allow(unused_imports)]
1127 pub use ::lrpar::parser::_deprecated_moved_::Node;
1128 })
1129 } else {
1130 None
1131 };
1132
1133 let mod_name =
1134 match syn::parse_str::<proc_macro2::Ident>(mod_name) {
1135 Ok(s) => s,
1136 Err(e) => return Err(format!(
1137 "CTParserBuilder::mod_name(\"{}\") is not a valid rust identifier due to '{}'",
1138 mod_name, e
1139 )
1140 .into()),
1141 };
1142 let out_tokens = quote! {
1143 #visibility mod #mod_name {
1144 #user_actions
1146 mod _parser_ {
1147 #![allow(clippy::type_complexity)]
1148 #![allow(clippy::unnecessary_wraps)]
1149 #![deny(unsafe_code)]
1150 #[allow(unused_imports)]
1151 use super::*;
1152 #additional_decls
1153 #parse_function
1154 #rule_consts
1155 #token_epp
1156 #action_wrappers
1157 } #[allow(unused_imports)]
1159 pub use _parser_::*;
1160 #[allow(unused_imports)]
1161 use ::lrpar::Lexeme;
1162 } };
1164 let unformatted = out_tokens.to_string();
1166 let outs = syn::parse_str(&unformatted)
1167 .map(|syntax_tree| prettyplease::unparse(&syntax_tree))
1168 .unwrap_or(unformatted);
1169 let mut f = File::create(outp_rs)?;
1170 f.write_all(outs.as_bytes())?;
1171 f.write_all(cache.as_bytes())?;
1172 Ok(())
1173 }
1174
1175 fn rebuild_cache(&self, derived_mod_name: &'_ str, grm: &YaccGrammar<StorageT>) -> TokenStream {
1178 let Self {
1185 grammar_path,
1188 grammar_src: _,
1190 from_ast: _,
1192 mod_name,
1193 recoverer,
1194 yacckind,
1195 output_path: _,
1196 error_on_conflicts,
1197 warnings_are_errors,
1198 show_warnings,
1199 visibility,
1200 rust_edition,
1201 serialisation_format,
1202 inspect_rt: _,
1203 #[cfg(test)]
1204 inspect_callback: _,
1205 phantom: _,
1206 } = self;
1207 let build_time = env!("VERGEN_BUILD_TIMESTAMP");
1208 let grammar_path = grammar_path.as_ref().unwrap().to_string_lossy();
1209 let mod_name = QuoteOption(mod_name.as_deref());
1210 let visibility = visibility.to_variant_tokens();
1211 let rust_edition = rust_edition.to_variant_tokens();
1212 let yacckind = yacckind.expect("is_some() by this point");
1213 let rule_map = grm
1214 .iter_tidxs()
1215 .map(|tidx| {
1216 QuoteTuple((
1217 usize::from(tidx),
1218 grm.token_name(tidx).unwrap_or("<unknown>"),
1219 ))
1220 })
1221 .collect::<Vec<_>>();
1222 let cache_info = quote! {
1223 BUILD_TIME = #build_time
1224 DERIVED_MOD_NAME = #derived_mod_name
1225 ENCODING_CONFIG = #serialisation_format
1226 GRAMMAR_PATH = #grammar_path
1227 MOD_NAME = #mod_name
1228 RECOVERER = #recoverer
1229 YACC_KIND = #yacckind
1230 ERROR_ON_CONFLICTS = #error_on_conflicts
1231 SHOW_WARNINGS = #show_warnings
1232 WARNINGS_ARE_ERRORS = #warnings_are_errors
1233 RUST_EDITION = #rust_edition
1234 RULE_IDS_MAP = [#(#rule_map,)*]
1235 VISIBILITY = #visibility
1236
1237 };
1238 let cache_info_str = cache_info.to_string();
1239 quote!(#cache_info_str)
1240 }
1241
1242 fn gen_parse_function(
1244 &self,
1245 grm: &YaccGrammar<StorageT>,
1246 stable: &StateTable<StorageT>,
1247 ) -> Result<TokenStream, Box<dyn Error>> {
1248 let storaget = str::parse::<TokenStream>(type_name::<StorageT>())?;
1249 let lexertypest = str::parse::<TokenStream>(type_name::<LexerTypesT>())?;
1250 let recoverer = self.recoverer;
1251 let run_parser = match self.yacckind.unwrap() {
1252 YaccKind::Original(YaccOriginalActionKind::GenericParseTree) => {
1253 quote! {
1254 ::lrpar::RTParserBuilder::new(grm, stable)
1255 .recoverer(#recoverer)
1256 .parse_map(
1257 lexer,
1258 &|lexeme| Node::Term{lexeme},
1259 &|ridx, nodes| Node::Nonterm{ridx, nodes}
1260 )
1261 }
1262 }
1263 YaccKind::Original(YaccOriginalActionKind::NoAction) => {
1264 quote! {
1265 ::lrpar::RTParserBuilder::new(grm, stable)
1266 .recoverer(#recoverer)
1267 .parse_map(lexer, &|_| (), &|_, _| ()).1
1268 }
1269 }
1270 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1271 let actionskind = str::parse::<TokenStream>(ACTIONS_KIND)?;
1272 let parsed_parse_generics = make_generics(grm.parse_generics().as_deref())?;
1273 let (_, type_generics, _) = parsed_parse_generics.split_for_impl();
1274 let (action_fn_parse_param, action_fn_parse_param_ty) = match grm.parse_param() {
1277 Some((name, ty)) => {
1278 let name = str::parse::<TokenStream>(name)?;
1279 let ty = str::parse::<TokenStream>(ty)?;
1280 (quote!(#name), quote!(#ty))
1281 }
1282 None => (quote!(()), quote!(())),
1283 };
1284 let wrappers = grm.iter_pidxs().map(|pidx| {
1285 let pidx = usize::from(pidx);
1286 format_ident!("{}wrapper_{}", ACTION_PREFIX, pidx)
1287 });
1288 let edition_lifetime = if self.rust_edition != RustEdition::Rust2015 {
1289 quote!('_,)
1290 } else {
1291 quote!()
1292 };
1293 let ridx = usize::from(self.user_start_ridx(grm));
1294 let action_ident = format_ident!("{}{}", ACTIONS_KIND_PREFIX, ridx);
1295
1296 quote! {
1297 let actions: ::std::vec::Vec<
1298 &dyn Fn(
1299 ::cfgrammar::RIdx<#storaget>,
1300 &'lexer dyn ::lrpar::NonStreamingLexer<'input, #lexertypest>,
1301 ::cfgrammar::Span,
1302 ::std::vec::Drain<#edition_lifetime ::lrpar::parser::AStackType<<#lexertypest as ::lrpar::LexerTypes>::LexemeT, #actionskind #type_generics>>,
1303 #action_fn_parse_param_ty
1304 ) -> #actionskind #type_generics
1305 > = ::std::vec![#(&#wrappers,)*];
1306 match ::lrpar::RTParserBuilder::new(grm, stable)
1307 .recoverer(#recoverer)
1308 .parse_actions(lexer, &actions, #action_fn_parse_param) {
1309 (Some(#actionskind::#action_ident(x)), y) => (Some(x), y),
1310 (None, y) => (None, y),
1311 _ => unreachable!()
1312 }
1313 }
1314 }
1315 kind => panic!("YaccKind {:?} not supported", kind),
1316 };
1317
1318 let parsed_parse_generics: Generics = match self.yacckind.unwrap() {
1319 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1320 make_generics(grm.parse_generics().as_deref())?
1321 }
1322 _ => make_generics(None)?,
1323 };
1324 let (generics, _, where_clause) = parsed_parse_generics.split_for_impl();
1325
1326 let parse_fn_parse_param = match self.yacckind.unwrap() {
1328 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1329 if let Some((name, tyname)) = grm.parse_param() {
1330 let name = str::parse::<TokenStream>(name)?;
1331 let tyname = str::parse::<TokenStream>(tyname)?;
1332 Some(quote! {#name: #tyname})
1333 } else {
1334 None
1335 }
1336 }
1337 _ => None,
1338 };
1339 let parse_fn_return_ty = match self.yacckind.unwrap() {
1340 YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools => {
1341 let actiont = grm
1342 .actiontype(self.user_start_ridx(grm))
1343 .as_ref()
1344 .map(|at| str::parse::<TokenStream>(at))
1345 .transpose()?;
1346 quote! {
1347 (::std::option::Option<#actiont>, ::std::vec::Vec<::lrpar::LexParseError<#storaget, #lexertypest>>)
1348 }
1349 }
1350 YaccKind::Original(YaccOriginalActionKind::GenericParseTree) => quote! {
1351 (::std::option::Option<Node<<#lexertypest as ::lrpar::LexerTypes>::LexemeT, #storaget>>,
1352 ::std::vec::Vec<::lrpar::LexParseError<#storaget, #lexertypest>>)
1353 },
1354 YaccKind::Original(YaccOriginalActionKind::NoAction) => quote! {
1355 ::std::vec::Vec<::lrpar::LexParseError<#storaget, #lexertypest>>
1356 },
1357 _ => unreachable!(),
1358 };
1359
1360 let serialisation_format = self
1361 .serialisation_format
1362 .expect("Should already have a default value");
1363 let (grm_data, stable_data): (Vec<u8>, Vec<u8>) = match serialisation_format {
1365 SerialisationFormat::FixedSizeInteger => {
1366 let config = wincode::config::Configuration::default().with_fixint_encoding();
1367 let grm = wincode::config::serialize(grm, config)?;
1368 let stable = wincode::config::serialize(stable, config)?;
1369 (grm, stable)
1370 }
1371 SerialisationFormat::VariableSizedInteger => {
1372 let config = wincode::config::Configuration::default().with_varint_encoding();
1373 let grm = wincode::config::serialize(grm, config)?;
1374 let stable = wincode::config::serialize(stable, config)?;
1375 (grm, stable)
1376 }
1377 };
1378 let serialisation_format_str = quote!(serialisation_format).to_string();
1379 Ok(quote! {
1380 const __GRM_DATA: &[u8] = &[#(#grm_data,)*];
1381 const __STABLE_DATA: &[u8] = &[#(#stable_data,)*];
1382 const __SERIALISATION_FORMAT: ::lrpar::ctbuilder::SerialisationFormat = #serialisation_format;
1383
1384 fn __lrpar_parser_data() -> &'static ::lrpar::ParserData<#storaget> {
1385 static DATA: ::std::sync::OnceLock<::lrpar::ParserData<#storaget>>
1386 = ::std::sync::OnceLock::new();
1387 DATA.get_or_init(
1388 || {
1389 match __SERIALISATION_FORMAT {
1392 ::lrpar::ctbuilder::SerialisationFormat::FixedSizeInteger => {
1393 ::lrpar::ctbuilder::_reconstitute(__GRM_DATA, __STABLE_DATA, ::lrpar::ctbuilder::wincode::config::Configuration::default().with_fixint_encoding())
1394 }
1395 ::lrpar::ctbuilder::SerialisationFormat::VariableSizedInteger => {
1396 ::lrpar::ctbuilder::_reconstitute(__GRM_DATA, __STABLE_DATA, ::lrpar::ctbuilder::wincode::config::Configuration::default().with_varint_encoding())
1397 }
1398 _ => {
1399 panic!("Parser source was generated using unknown `SerialisationFormat`: {:?}", #serialisation_format_str)
1400 }
1401 }
1402 }
1403 )
1404 }
1405
1406 #[allow(dead_code)]
1407 pub fn parse #generics (
1408 lexer: &'lexer dyn ::lrpar::NonStreamingLexer<'input, #lexertypest>,
1409 #parse_fn_parse_param
1410 ) -> #parse_fn_return_ty
1411 #where_clause
1412 {
1413 let __data = __lrpar_parser_data();
1414 let grm = __data.grm();
1415 let stable = __data.stable();
1416 #run_parser
1417 }
1418 })
1419 }
1420
1421 fn gen_rule_consts(
1422 &self,
1423 grm: &YaccGrammar<StorageT>,
1424 ) -> Result<TokenStream, proc_macro2::LexError> {
1425 let mut toks = TokenStream::new();
1426 for ridx in grm.iter_rules() {
1427 if !grm.rule_to_prods(ridx).contains(&grm.start_prod()) {
1428 let r_const = format_ident!("R_{}", grm.rule_name_str(ridx).to_ascii_uppercase());
1429 let storage_ty = str::parse::<TokenStream>(type_name::<StorageT>())?;
1430 let ridx = UnsuffixedUsize(usize::from(ridx));
1431 toks.extend(quote! {
1432 #[allow(dead_code)]
1433 pub const #r_const: #storage_ty = #ridx;
1434 });
1435 }
1436 }
1437 Ok(toks)
1438 }
1439
1440 fn gen_token_epp(
1441 &self,
1442 grm: &YaccGrammar<StorageT>,
1443 ) -> Result<TokenStream, proc_macro2::LexError> {
1444 let mut tidxs = Vec::new();
1445 for tidx in grm.iter_tidxs() {
1446 tidxs.push(QuoteOption(grm.token_epp(tidx)));
1447 }
1448 let const_epp_ident = format_ident!("{}EPP", GLOBAL_PREFIX);
1449 let storage_ty = str::parse::<TokenStream>(type_name::<StorageT>())?;
1450 Ok(quote! {
1451 const #const_epp_ident: &[::std::option::Option<&str>] = &[
1452 #(#tidxs,)*
1453 ];
1454
1455 #[allow(dead_code)]
1458 pub fn token_epp<'a>(tidx: ::cfgrammar::TIdx<#storage_ty>) -> ::std::option::Option<&'a str> {
1459 #const_epp_ident[usize::from(tidx)]
1460 }
1461 })
1462 }
1463
1464 fn gen_wrappers(&self, grm: &YaccGrammar<StorageT>) -> Result<TokenStream, Box<dyn Error>> {
1466 let parsed_parse_generics = make_generics(grm.parse_generics().as_deref())?;
1467 let (generics, type_generics, where_clause) = parsed_parse_generics.split_for_impl();
1468
1469 let (parse_paramname, parse_paramdef);
1470 match grm.parse_param() {
1471 Some((name, tyname)) => {
1472 parse_paramname = str::parse::<TokenStream>(name)?;
1473 let ty = str::parse::<TokenStream>(tyname)?;
1474 parse_paramdef = quote!(#parse_paramname: #ty);
1475 }
1476 None => {
1477 parse_paramname = quote!(());
1478 parse_paramdef = quote! {_: ()};
1479 }
1480 };
1481
1482 let mut wrappers = TokenStream::new();
1483 for pidx in grm.iter_pidxs() {
1484 let ridx = grm.prod_to_rule(pidx);
1485
1486 let wrapper_fn = format_ident!("{}wrapper_{}", ACTION_PREFIX, usize::from(pidx));
1490 let ridx_var = format_ident!("{}ridx", ACTION_PREFIX);
1491 let lexer_var = format_ident!("{}lexer", ACTION_PREFIX);
1492 let span_var = format_ident!("{}span", ACTION_PREFIX);
1493 let args_var = format_ident!("{}args", ACTION_PREFIX);
1494 let storaget = str::parse::<TokenStream>(type_name::<StorageT>())?;
1495 let lexertypest = str::parse::<TokenStream>(type_name::<LexerTypesT>())?;
1496 let actionskind = str::parse::<TokenStream>(ACTIONS_KIND)?;
1497 let edition_lifetime = if self.rust_edition != RustEdition::Rust2015 {
1498 Some(quote!('_,))
1499 } else {
1500 None
1501 };
1502 let mut wrapper_fn_body = TokenStream::new();
1503 if grm.action(pidx).is_some() {
1504 for i in 0..grm.prod(pidx).len() {
1506 let arg = format_ident!("{}arg_{}", ACTION_PREFIX, i + 1);
1507 wrapper_fn_body.extend(match grm.prod(pidx)[i] {
1508 Symbol::Rule(ref_ridx) => {
1509 let ref_ridx = usize::from(ref_ridx);
1510 let actionvariant = format_ident!("{}{}", ACTIONS_KIND_PREFIX, ref_ridx);
1511 quote! {
1512 #[allow(clippy::let_unit_value)]
1513 let #arg = match #args_var.next().unwrap() {
1514 ::lrpar::parser::AStackType::ActionType(#actionskind::#type_generics::#actionvariant(x)) => x,
1515 _ => unreachable!()
1516 };
1517 }
1518 }
1519 Symbol::Token(_) => {
1520 quote! {
1521 let #arg = match #args_var.next().unwrap() {
1522 ::lrpar::parser::AStackType::Lexeme(l) => {
1523 if l.faulty() {
1524 Err(l)
1525 } else {
1526 Ok(l)
1527 }
1528 },
1529 ::lrpar::parser::AStackType::ActionType(_) => unreachable!()
1530 };
1531 }
1532 }
1533 })
1534 }
1535
1536 let args = (0..grm.prod(pidx).len())
1538 .map(|i| format_ident!("{}arg_{}", ACTION_PREFIX, i + 1))
1539 .collect::<Vec<_>>();
1540 let action_fn = format_ident!("{}action_{}", ACTION_PREFIX, usize::from(pidx));
1541 let actionsvariant = format_ident!("{}{}", ACTIONS_KIND_PREFIX, usize::from(ridx));
1542
1543 wrapper_fn_body.extend(match grm.actiontype(ridx) {
1544 Some(s) if s == "()" => {
1545 quote! {
1549 #action_fn(#ridx_var, #lexer_var, #span_var, #parse_paramname, #(#args,)*);
1550 #actionskind::#type_generics::#actionsvariant(())
1551 }
1552 }
1553 _ => {
1554 quote! {
1555 #actionskind::#type_generics::#actionsvariant(#action_fn(#ridx_var, #lexer_var, #span_var, #parse_paramname, #(#args,)*))
1556 }
1557 }
1558 })
1559 } else if pidx == grm.start_prod() {
1560 wrapper_fn_body.extend(quote!(unreachable!()));
1561 } else {
1562 unreachable!(
1563 "Production in rule '{}' must have an action body, which should have been handled by gen_user_actions.",
1564 grm.rule_name_str(grm.prod_to_rule(pidx))
1565 );
1566 };
1567
1568 let attrib = if pidx == grm.start_prod() {
1569 Some(quote!(#[allow(unused_variables)]))
1571 } else {
1572 None
1573 };
1574 wrappers.extend(quote! {
1575 #attrib
1576 fn #wrapper_fn #generics (
1577 #ridx_var: ::cfgrammar::RIdx<#storaget>,
1578 #lexer_var: &'lexer dyn ::lrpar::NonStreamingLexer<'input, #lexertypest>,
1579 #span_var: ::cfgrammar::Span,
1580 mut #args_var: ::std::vec::Drain<#edition_lifetime ::lrpar::parser::AStackType<<#lexertypest as ::lrpar::LexerTypes>::LexemeT, #actionskind #type_generics>>,
1581 #parse_paramdef
1582 ) -> #actionskind #type_generics
1583 #where_clause
1584 {
1585 #wrapper_fn_body
1586 }
1587 })
1588 }
1589 let mut actionskindvariants = Vec::new();
1590 let actionskindhidden = format_ident!("_{}", ACTIONS_KIND_HIDDEN);
1591 let actionskind = str::parse::<TokenStream>(ACTIONS_KIND).unwrap();
1592 let mut phantom_data_type = Vec::new();
1593 for ridx in grm.iter_rules() {
1594 if let Some(actiont) = grm.actiontype(ridx) {
1595 let actionskindvariant =
1596 format_ident!("{}{}", ACTIONS_KIND_PREFIX, usize::from(ridx));
1597 let actiont = str::parse::<TokenStream>(actiont).unwrap();
1598 actionskindvariants.push(quote! {
1599 #actionskindvariant(#actiont)
1600 })
1601 }
1602 }
1603 for lifetime in parsed_parse_generics.lifetimes() {
1604 let lifetime = &lifetime.lifetime;
1605 phantom_data_type.push(quote! { &#lifetime () });
1606 }
1607 for type_param in parsed_parse_generics.type_params() {
1608 let ident = &type_param.ident;
1609 phantom_data_type.push(quote! { #ident });
1610 }
1611 actionskindvariants.push(quote! {
1612 #actionskindhidden(::std::marker::PhantomData<(#(#phantom_data_type,)*)>)
1613 });
1614 wrappers.extend(quote! {
1615 #[allow(dead_code)]
1616 enum #actionskind #generics #where_clause {
1617 #(#actionskindvariants,)*
1618 }
1619 });
1620 Ok(wrappers)
1621 }
1622
1623 fn gen_user_actions(
1625 &self,
1626 grm: &YaccGrammar<StorageT>,
1627 diag: &SpannedDiagnosticFormatter,
1628 ) -> Result<TokenStream, Box<dyn Error>> {
1629 let programs = grm
1630 .programs()
1631 .as_ref()
1632 .map(|s| str::parse::<TokenStream>(s))
1633 .transpose()?;
1634 let mut action_fns = TokenStream::new();
1635 let parsed_parse_generics = make_generics(grm.parse_generics().as_deref())?;
1637 let (generics, _, where_clause) = parsed_parse_generics.split_for_impl();
1638 let (parse_paramname, parse_paramdef, parse_param_unit);
1639 match grm.parse_param() {
1640 Some((name, tyname)) => {
1641 parse_param_unit = tyname.trim() == "()";
1642 parse_paramname = str::parse::<TokenStream>(name)?;
1643 let ty = str::parse::<TokenStream>(tyname)?;
1644 parse_paramdef = quote!(#parse_paramname: #ty);
1645 }
1646 None => {
1647 parse_param_unit = true;
1648 parse_paramname = quote!(());
1649 parse_paramdef = quote! {_: ()};
1650 }
1651 };
1652 for pidx in grm.iter_pidxs() {
1653 if pidx == grm.start_prod() {
1654 continue;
1655 }
1656
1657 let mut args = Vec::with_capacity(grm.prod(pidx).len());
1659 for i in 0..grm.prod(pidx).len() {
1660 let argt = match grm.prod(pidx)[i] {
1661 Symbol::Rule(ref_ridx) => {
1662 if let Some(action_type) = grm.actiontype(ref_ridx).as_ref() {
1663 str::parse::<TokenStream>(action_type)?
1664 } else {
1665 let mut s = String::from("\n");
1666 let rule_span = grm.rule_name_span(ref_ridx);
1667 s.push_str(&diag.file_location_msg("Error", Some(rule_span)));
1668 s.push('\n');
1669 s.push_str(&diag.underline_span_with_text(
1670 rule_span,
1671 "Rule missing action type".to_string(),
1672 '^',
1673 ));
1674 return Err(ErrorString(s).into());
1675 }
1676 }
1677 Symbol::Token(_) => {
1678 let lexemet =
1679 str::parse::<TokenStream>(type_name::<LexerTypesT::LexemeT>())?;
1680 quote!(::std::result::Result<#lexemet, #lexemet>)
1681 }
1682 };
1683 let arg = format_ident!("{}arg_{}", ACTION_PREFIX, i + 1);
1684 args.push(quote!(mut #arg: #argt));
1685 }
1686
1687 let returnt = {
1691 let actiont = grm.actiontype(grm.prod_to_rule(pidx)).as_ref().unwrap();
1692 if actiont == "()" {
1693 None
1694 } else {
1695 let actiont = str::parse::<TokenStream>(actiont)?;
1696 Some(quote!( -> #actiont))
1697 }
1698 };
1699 let action_fn = format_ident!("{}action_{}", ACTION_PREFIX, usize::from(pidx));
1700 let lexer_var = format_ident!("{}lexer", ACTION_PREFIX);
1701 let span_var = format_ident!("{}span", ACTION_PREFIX);
1702 let ridx_var = format_ident!("{}ridx", ACTION_PREFIX);
1703 let storaget = str::parse::<TokenStream>(type_name::<StorageT>())?;
1704 let lexertypest = str::parse::<TokenStream>(type_name::<LexerTypesT>())?;
1705 let bind_parse_param = if !parse_param_unit {
1706 Some(quote! {let _ = #parse_paramname;})
1707 } else {
1708 None
1709 };
1710
1711 let pre_action = grm.action(pidx).as_ref().ok_or_else(|| {
1714 let mut s = String::from("\n");
1715 let span = grm.prod_span(pidx);
1716 s.push_str(&diag.file_location_msg("Error", Some(span)));
1717 s.push('\n');
1718 s.push_str(&diag.underline_span_with_text(
1719 span,
1720 "Production is missing action code".to_string(),
1721 '^',
1722 ));
1723 ErrorString(s)
1724 })?;
1725 let mut last = 0;
1726 let mut outs = String::new();
1727 loop {
1728 match pre_action[last..].find('$') {
1729 Some(off) => {
1730 if pre_action[last + off..].starts_with("$$") {
1731 outs.push_str(&pre_action[last..last + off + "$".len()]);
1732 last = last + off + "$$".len();
1733 } else if pre_action[last + off..].starts_with("$lexer") {
1734 outs.push_str(&pre_action[last..last + off]);
1735 write!(outs, "{prefix}lexer", prefix = ACTION_PREFIX).ok();
1736 last = last + off + "$lexer".len();
1737 } else if pre_action[last + off..].starts_with("$span") {
1738 outs.push_str(&pre_action[last..last + off]);
1739 write!(outs, "{prefix}span", prefix = ACTION_PREFIX).ok();
1740 last = last + off + "$span".len();
1741 } else if last + off + 1 < pre_action.len()
1742 && pre_action[last + off + 1..].starts_with(|c: char| c.is_numeric())
1743 {
1744 outs.push_str(&pre_action[last..last + off]);
1745 write!(outs, "{prefix}arg_", prefix = ACTION_PREFIX).ok();
1746 last = last + off + "$".len();
1747 } else {
1748 let span = grm.action_span(pidx).unwrap();
1749 let inner_span =
1750 Span::new(span.start() + last + off + "$".len(), span.end());
1751 let mut s = String::from("\n");
1752 s.push_str(&diag.file_location_msg("Error", Some(inner_span)));
1753 s.push('\n');
1754 s.push_str(&diag.underline_span_with_text(
1755 inner_span,
1756 "Unknown text following '$'".to_string(),
1757 '^',
1758 ));
1759 return Err(ErrorString(s).into());
1760 }
1761 }
1762 None => {
1763 outs.push_str(&pre_action[last..]);
1764 break;
1765 }
1766 }
1767 }
1768
1769 let action_body = str::parse::<TokenStream>(&outs)?;
1770 action_fns.extend(quote! {
1771 #[allow(clippy::too_many_arguments)]
1772 fn #action_fn #generics (
1773 #ridx_var: ::cfgrammar::RIdx<#storaget>,
1774 #lexer_var: &'lexer dyn ::lrpar::NonStreamingLexer<'input, #lexertypest>,
1775 #span_var: ::cfgrammar::Span,
1776 #parse_paramdef,
1777 #(#args,)*
1778 ) #returnt
1779 #where_clause
1780 {
1781 #bind_parse_param
1782 #action_body
1783 }
1784 })
1785 }
1786 Ok(quote! {
1787 #programs
1788 #action_fns
1789 })
1790 }
1791
1792 fn user_start_ridx(&self, grm: &YaccGrammar<StorageT>) -> RIdx<StorageT> {
1796 debug_assert_eq!(grm.prod(grm.start_prod()).len(), 1);
1797 match grm.prod(grm.start_prod())[0] {
1798 Symbol::Rule(ridx) => ridx,
1799 _ => unreachable!(),
1800 }
1801 }
1802}
1803
1804#[doc(hidden)]
1807pub struct ParserData<StorageT: Eq + Hash> {
1808 grm: YaccGrammar<StorageT>,
1809 stable: StateTable<StorageT>,
1810}
1811
1812impl<StorageT: Eq + Hash> ParserData<StorageT> {
1813 pub fn grm(&self) -> &YaccGrammar<StorageT> {
1814 &self.grm
1815 }
1816
1817 pub fn stable(&self) -> &StateTable<StorageT> {
1818 &self.stable
1819 }
1820}
1821
1822#[doc(hidden)]
1825pub fn _reconstitute<
1826 C: wincode::config::Config + Clone + Copy,
1827 StorageT: SchemaReadOwned<C, Dst = StorageT> + Eq + Hash + PrimInt + Unsigned + 'static,
1828>(
1829 grm_buf: &[u8],
1830 stable_buf: &[u8],
1831 config: C,
1832) -> ParserData<StorageT> {
1833 let grm: YaccGrammar<StorageT> = wincode::config::deserialize_from(grm_buf, config).unwrap();
1834 let stable = wincode::config::deserialize_from(stable_buf, config).unwrap();
1835 ParserData { grm, stable }
1836}
1837
1838pub struct CTParser<StorageT = u32>
1840where
1841 StorageT: Eq + Hash,
1842{
1843 regenerated: bool,
1844 rule_ids: HashMap<String, StorageT>,
1845 yacc_grammar: YaccGrammar<StorageT>,
1846 grammar_src: String,
1847 grammar_path: PathBuf,
1848 conflicts: Option<(StateGraph<StorageT>, StateTable<StorageT>)>,
1849}
1850
1851impl<StorageT> CTParser<StorageT>
1852where
1853 StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
1854 usize: AsPrimitive<StorageT>,
1855{
1856 pub fn regenerated(&self) -> bool {
1858 self.regenerated
1859 }
1860
1861 pub fn token_map(&self) -> &HashMap<String, StorageT> {
1864 &self.rule_ids
1865 }
1866
1867 #[allow(private_interfaces)]
1873 pub fn conflicts(
1874 &self,
1875 _: crate::unstable::UnstableApi,
1876 ) -> Option<(
1877 &YaccGrammar<StorageT>,
1878 &StateGraph<StorageT>,
1879 &StateTable<StorageT>,
1880 &Conflicts<StorageT>,
1881 )> {
1882 if let Some((sgraph, stable)) = &self.conflicts {
1883 return Some((
1884 &self.yacc_grammar,
1885 sgraph,
1886 stable,
1887 stable.conflicts().unwrap(),
1888 ));
1889 }
1890 None
1891 }
1892
1893 #[doc(hidden)]
1894 pub fn yacc_grammar(&self) -> &YaccGrammar<StorageT> {
1895 &self.yacc_grammar
1896 }
1897 #[doc(hidden)]
1898 pub fn grammar_src(&self) -> &str {
1899 &self.grammar_src
1900 }
1901 #[doc(hidden)]
1902 pub fn grammar_path(&self) -> &Path {
1903 self.grammar_path.as_path()
1904 }
1905}
1906
1907fn indent(indent: &str, s: &str) -> String {
1918 format!("{indent}{}\n", s.trim_end_matches('\n')).replace('\n', &format!("\n{}", indent))
1919}
1920
1921fn make_generics(parse_generics: Option<&str>) -> Result<Generics, Box<dyn Error>> {
1922 if let Some(parse_generics) = parse_generics {
1923 let tokens = str::parse::<TokenStream>(parse_generics)?;
1924 match syn::parse2(quote!(<'lexer, 'input: 'lexer, #tokens>)) {
1925 Ok(res) => Ok(res),
1926 Err(err) => Err(format!("unable to parse %parse-generics: {}", err).into()),
1927 }
1928 } else {
1929 Ok(parse_quote!(<'lexer, 'input: 'lexer>))
1930 }
1931}
1932
1933#[cfg(all(not(target_arch = "wasm32"), test))]
1935mod test {
1936 use std::{fs::File, io::Write, path::PathBuf};
1937
1938 use super::{CTConflictsError, CTParserBuilder};
1939 use crate::test_utils::TestLexerTypes;
1940 use cfgrammar::yacc::{YaccKind, YaccOriginalActionKind};
1941 use tempfile::TempDir;
1942
1943 #[test]
1944 fn test_conflicts() {
1945 let temp = TempDir::new().unwrap();
1946 let mut file_path = PathBuf::from(temp.as_ref());
1947 file_path.push("grm.y");
1948 let mut f = File::create(&file_path).unwrap();
1949 let _ = f.write_all(
1950 "%start A
1951%%
1952A : 'a' 'b' | B 'b';
1953B : 'a' | C;
1954C : 'a';"
1955 .as_bytes(),
1956 );
1957
1958 match CTParserBuilder::<TestLexerTypes>::new()
1959 .error_on_conflicts(false)
1960 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
1961 .grammar_path(file_path.to_str().unwrap())
1962 .output_path(file_path.with_extension("ignored"))
1963 .build()
1964 .unwrap()
1965 .conflicts(crate::unstable::UnstableApi)
1966 {
1967 Some((_, _, _, conflicts)) => {
1968 assert_eq!(conflicts.sr_len(), 1);
1969 assert_eq!(conflicts.rr_len(), 1);
1970 }
1971 None => panic!("Expected error data"),
1972 }
1973 }
1974
1975 #[test]
1976 fn test_conflicts_error() {
1977 let temp = TempDir::new().unwrap();
1978 let mut file_path = PathBuf::from(temp.as_ref());
1979 file_path.push("grm.y");
1980 let mut f = File::create(&file_path).unwrap();
1981 let _ = f.write_all(
1982 "%start A
1983%%
1984A : 'a' 'b' | B 'b';
1985B : 'a' | C;
1986C : 'a';"
1987 .as_bytes(),
1988 );
1989
1990 match CTParserBuilder::<TestLexerTypes>::new()
1991 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
1992 .grammar_path(file_path.to_str().unwrap())
1993 .output_path(file_path.with_extension("ignored"))
1994 .build()
1995 {
1996 Ok(_) => panic!("Expected error"),
1997 Err(e) => {
1998 let cs = e.downcast_ref::<CTConflictsError<u16>>();
1999 assert_eq!(cs.unwrap().stable.conflicts().unwrap().rr_len(), 1);
2000 assert_eq!(cs.unwrap().stable.conflicts().unwrap().sr_len(), 1);
2001 }
2002 }
2003 }
2004
2005 #[test]
2006 fn test_expect_error() {
2007 let temp = TempDir::new().unwrap();
2008 let mut file_path = PathBuf::from(temp.as_ref());
2009 file_path.push("grm.y");
2010 let mut f = File::create(&file_path).unwrap();
2011 let _ = f.write_all(
2012 "%start A
2013%expect 2
2014%%
2015A: 'a' 'b' | B 'b';
2016B: 'a';"
2017 .as_bytes(),
2018 );
2019
2020 match CTParserBuilder::<TestLexerTypes>::new()
2021 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
2022 .grammar_path(file_path.to_str().unwrap())
2023 .output_path(file_path.with_extension("ignored"))
2024 .build()
2025 {
2026 Ok(_) => panic!("Expected error"),
2027 Err(e) => {
2028 let cs = e.downcast_ref::<CTConflictsError<u16>>();
2029 assert_eq!(cs.unwrap().stable.conflicts().unwrap().rr_len(), 0);
2030 assert_eq!(cs.unwrap().stable.conflicts().unwrap().sr_len(), 1);
2031 }
2032 }
2033 }
2034
2035 #[test]
2036 fn test_expectrr_error() {
2037 let temp = TempDir::new().unwrap();
2038 let mut file_path = PathBuf::from(temp.as_ref());
2039 file_path.push("grm.y");
2040 let mut f = File::create(&file_path).unwrap();
2041 let _ = f.write_all(
2042 "%start A
2043%expect 1
2044%expect-rr 2
2045%%
2046A : 'a' 'b' | B 'b';
2047B : 'a' | C;
2048C : 'a';"
2049 .as_bytes(),
2050 );
2051
2052 match CTParserBuilder::<TestLexerTypes>::new()
2053 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
2054 .grammar_path(file_path.to_str().unwrap())
2055 .output_path(file_path.with_extension("ignored"))
2056 .build()
2057 {
2058 Ok(_) => panic!("Expected error"),
2059 Err(e) => {
2060 let cs = e.downcast_ref::<CTConflictsError<u16>>();
2061 assert_eq!(cs.unwrap().stable.conflicts().unwrap().rr_len(), 1);
2062 assert_eq!(cs.unwrap().stable.conflicts().unwrap().sr_len(), 1);
2063 }
2064 }
2065 }
2066
2067 #[test]
2068 fn test_invalid_identifier_in_derived_mod_name() {
2071 let temp = TempDir::new().unwrap();
2072 let mut file_path = PathBuf::from(temp.as_ref());
2073 file_path.push("contains-a-dash.y");
2074 let mut f = File::create(&file_path).unwrap();
2075 let _ = f.write_all(
2076 "%start A
2077%%
2078A : 'a';"
2079 .as_bytes(),
2080 );
2081 match CTParserBuilder::<TestLexerTypes>::new()
2082 .yacckind(YaccKind::Original(YaccOriginalActionKind::GenericParseTree))
2083 .grammar_path(file_path.to_str().unwrap())
2084 .output_path(file_path.with_extension("ignored"))
2085 .build()
2086 {
2087 Ok(_) => panic!("Expected error"),
2088 Err(e) => {
2089 let err_string = e.to_string();
2090 assert_eq!(
2091 err_string,
2092 "CTParserBuilder::mod_name(\"contains-a-dash_y\") is not a valid rust identifier due to 'unexpected token'"
2093 );
2094 }
2095 }
2096 }
2097
2098 #[cfg(test)]
2099 #[test]
2100 fn test_recoverer_header() -> Result<(), Box<dyn std::error::Error>> {
2101 use crate::RecoveryKind as RK;
2102 #[rustfmt::skip]
2103 let recovery_kinds = [
2104 (Some(RK::None), Some(RK::None), Some(RK::None)),
2107 (Some(RK::None), Some(RK::CPCTPlus), Some(RK::None)),
2108 (Some(RK::CPCTPlus), Some(RK::CPCTPlus), Some(RK::CPCTPlus)),
2109 (Some(RK::CPCTPlus), Some(RK::None), Some(RK::CPCTPlus)),
2110 (None, Some(RK::CPCTPlus), Some(RK::CPCTPlus)),
2111 (None, Some(RK::None), Some(RK::None)),
2112 (None, None, Some(RK::CPCTPlus)),
2113 (Some(RK::None), None, Some(RK::None)),
2114 (Some(RK::CPCTPlus), None, Some(RK::CPCTPlus)),
2115 ];
2116
2117 for (i, (builder_arg, header_arg, expected_rk)) in
2118 recovery_kinds.iter().cloned().enumerate()
2119 {
2120 let y_src = if let Some(header_arg) = header_arg {
2121 format!(
2122 "\
2123 %grmtools{{yacckind: Original(NoAction), recoverer: {}}} \
2124 %% \
2125 start: ; \
2126 ",
2127 match header_arg {
2128 RK::None => "RecoveryKind::None",
2129 RK::CPCTPlus => "RecoveryKind::CPCTPlus",
2130 }
2131 )
2132 } else {
2133 r#"
2134 %grmtools{yacckind: Original(NoAction)}
2135 %%
2136 Start: ;
2137 "#
2138 .to_string()
2139 };
2140 let out_dir = std::env::var("OUT_DIR").unwrap();
2141 let y_path = format!("{out_dir}/recoverykind_test_{i}.y");
2142 let y_out_path = format!("{y_path}.rs");
2143 std::fs::File::create(y_path.clone()).unwrap();
2144 std::fs::write(y_path.clone(), y_src).unwrap();
2145 let mut cp_builder = CTParserBuilder::<TestLexerTypes>::new();
2146 cp_builder = cp_builder
2147 .output_path(y_out_path.clone())
2148 .grammar_path(y_path.clone());
2149 cp_builder = if let Some(builder_arg) = builder_arg {
2150 cp_builder.recoverer(builder_arg)
2151 } else {
2152 cp_builder
2153 }
2154 .inspect_recoverer(Box::new(move |rk| {
2155 if matches!(
2156 (rk, expected_rk),
2157 (RK::None, Some(RK::None)) | (RK::CPCTPlus, Some(RK::CPCTPlus))
2158 ) {
2159 Ok(())
2160 } else {
2161 panic!("Unexpected recovery kind")
2162 }
2163 }));
2164 cp_builder.build()?;
2165 }
2166 Ok(())
2167 }
2168}