Skip to content

Commit

Permalink
Change CTParserBuilder src gen to use the quote crate.
Browse files Browse the repository at this point in the history
  • Loading branch information
ratmice committed Mar 4, 2025
1 parent d70b294 commit 3641e71
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 27 deletions.
30 changes: 30 additions & 0 deletions cfgrammar/src/lib/yacc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub use self::{
grammar::{AssocKind, Precedence, SentenceGenerator, YaccGrammar},
parser::{YaccGrammarError, YaccGrammarErrorKind, YaccGrammarWarning, YaccGrammarWarningKind},
};
use proc_macro2::TokenStream;
use quote::quote;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -39,6 +41,18 @@ pub enum YaccKind {
Eco,
}

impl quote::ToTokens for YaccKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(match *self {
YaccKind::Grmtools => quote!(::cfgrammar::yacc::YaccKind::Grmtools),
YaccKind::Original(action_kind) => {
quote!(::cfgrammar::yacc::YaccKind::Original(#action_kind))
}
YaccKind::Eco => quote!(::cfgrammar::yacc::YaccKind::Eco),
})
}
}

#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum YaccOriginalActionKind {
Expand All @@ -50,3 +64,19 @@ pub enum YaccOriginalActionKind {
/// Do not do execute actions of any sort.
NoAction,
}

impl quote::ToTokens for YaccOriginalActionKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(match *self {
YaccOriginalActionKind::UserAction => {
quote!(::cfgrammar::yacc::YaccOriginalActionKind::UserAction)
}
YaccOriginalActionKind::GenericParseTree => {
quote!(::cfgrammar::yacc::YaccOriginalActionKind::GenericParseTree)
}
YaccOriginalActionKind::NoAction => {
quote!(::cfgrammar::yacc::YaccOriginalActionKind::NoAction)
}
})
}
}
4 changes: 3 additions & 1 deletion lrpar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ indexmap.workspace = true
lazy_static.workspace = true
num-traits.workspace = true
packedvec.workspace = true
proc-macro2.workspace = true
quote.workspace = true
regex.workspace = true
serde = { workspace = true, features = ["derive"] }
static_assertions.workspace = true
vob.workspace = true
regex.workspace = true

[dev-dependencies]
tempfile = "3.0"
80 changes: 54 additions & 26 deletions lrpar/src/lib/ctbuilder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ use filetime::FileTime;
use lazy_static::lazy_static;
use lrtable::{from_yacc, statetable::Conflicts, Minimiser, StateGraph, StateTable};
use num_traits::{AsPrimitive, PrimInt, Unsigned};
use proc_macro2::{Literal, TokenStream};
use quote::{quote, ToTokens, TokenStreamExt};
use regex::Regex;
use serde::{de::DeserializeOwned, Serialize};

Expand All @@ -52,6 +54,34 @@ struct CTConflictsError<StorageT: Eq + Hash> {
stable: StateTable<StorageT>,
}

/// The quote impl of `ToTokens` for `Option` prints an empty string for `None`
/// and the inner value for `Some(inner_value)`.
///
/// This wrapper instead emits both `Some` and `None` variants.
/// See: [quote #20](https://github.com/dtolnay/quote/issues/20)
struct QuoteOption<T>(Option<T>);

impl<T: ToTokens> ToTokens for QuoteOption<T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(match self.0 {
Some(ref t) => quote! { ::std::option::Option::Some(#t) },
None => quote! { ::std::option::Option::None },
});
}
}

/// The quote impl of `ToTokens` for `usize` prints literal values
/// including a type suffix for example `0usize`.
///
/// This wrapper omits the type suffix emitting `0` instead.
struct UnsuffixedUsize(usize);

impl ToTokens for UnsuffixedUsize {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append(Literal::usize_unsuffixed(self.0))
}
}

impl<StorageT> fmt::Display for CTConflictsError<StorageT>
where
StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
Expand Down Expand Up @@ -733,17 +763,23 @@ where
// Record the time that this version of lrpar was built. If the source code changes and
// rustc forces a recompile, this will change this value, causing anything which depends on
// this build of lrpar to be recompiled too.
writeln!(cache, " Build time: {:?}", env!("VERGEN_BUILD_TIMESTAMP")).ok();

writeln!(cache, " Grammar path: {:?}", self.grammar_path).ok();
writeln!(cache, " Mod name: {:?}", self.mod_name).ok();
writeln!(cache, " Recoverer: {:?}", self.recoverer).ok();
writeln!(cache, " YaccKind: {:?}", self.yacckind).ok();
writeln!(cache, " Visibility: {:?}", self.visibility.cow_str()).ok();
let build_time = env!("VERGEN_BUILD_TIMESTAMP");
let grammar_path = self.grammar_path.as_ref().unwrap().to_string_lossy();
let mod_name = self.mod_name;
let recoverer = self.recoverer;
let yacckind = self.yacckind;
let visibility = self.visibility.cow_str();
let error_on_conflicts = self.error_on_conflicts;
writeln!(cache, " Build time: {}", quote!(#build_time)).ok();
writeln!(cache, " Grammar path: {}", quote!(#grammar_path)).ok();
writeln!(cache, " Mod name: {}", quote!(#mod_name)).ok();
writeln!(cache, " Recoverer: {}", quote!(#recoverer)).ok();
writeln!(cache, " YaccKind: {}", quote!(#yacckind)).ok();
writeln!(cache, " Visibility: {}", quote!(#visibility)).ok();
writeln!(
cache,
" Error on conflicts: {:?}\n",
self.error_on_conflicts
" Error on conflicts: {}\n",
quote!(#error_on_conflicts)
)
.ok();

Expand Down Expand Up @@ -840,11 +876,8 @@ where
let wrappers = grm
.iter_pidxs()
.map(|pidx| {
format!(
"&{prefix}wrapper_{}",
usize::from(pidx),
prefix = ACTION_PREFIX
)
let pidx = UnsuffixedUsize(usize::from(pidx));
format!("&{prefix}wrapper_{}", quote!(#pidx), prefix = ACTION_PREFIX)
})
.collect::<Vec<_>>()
.join(",\n ");
Expand All @@ -867,6 +900,7 @@ where
wrappers = wrappers,
edition_lifetime = if self.rust_edition != RustEdition::Rust2015 { "'_, " } else { "" },
).ok();
let ridx = UnsuffixedUsize(usize::from(self.user_start_ridx(grm)));
write!(
outs,
"
Expand All @@ -880,7 +914,7 @@ where
parse_param = parse_param,
actionskind = ACTIONS_KIND,
actionskindprefix = ACTIONS_KIND_PREFIX,
ridx = usize::from(self.user_start_ridx(grm)),
ridx = quote!(#ridx),
recoverer = recoverer,
)
.ok();
Expand Down Expand Up @@ -920,7 +954,7 @@ where
if !grm.rule_to_prods(ridx).contains(&grm.start_prod()) {
write!(
outs,
" #[allow(dead_code)]\n pub const R_{}: {} = {:?};\n",
" #[allow(dead_code)]\n pub const R_{}: {} = {};\n",
grm.rule_name_str(ridx).to_ascii_uppercase(),
type_name::<StorageT>(),
usize::from(ridx)
Expand All @@ -934,10 +968,8 @@ where
fn gen_token_epp(&self, grm: &YaccGrammar<StorageT>) -> String {
let mut tidxs = Vec::new();
for tidx in grm.iter_tidxs() {
match grm.token_epp(tidx) {
Some(n) => tidxs.push(format!("Some(\"{}\")", str_escape(n))),
None => tidxs.push("None".to_string()),
}
let tok_epp = QuoteOption(grm.token_epp(tidx));
tidxs.push(format!("{}", quote!(#tok_epp)));
}
format!(
" const {prefix}EPP: &[::std::option::Option<&str>] = &[{}];
Expand Down Expand Up @@ -966,6 +998,7 @@ where
};
for pidx in grm.iter_pidxs() {
let ridx = grm.prod_to_rule(pidx);
let pidx_num = UnsuffixedUsize(usize::from(pidx));

// Iterate over all $-arguments and replace them with their respective
// element from the argument vector (e.g. $1 is replaced by args[0]). At
Expand All @@ -977,7 +1010,7 @@ where
mut {prefix}args: ::std::vec::Drain<{edition_lifetime} ::lrpar::parser::AStackType<<{lexertypest} as ::lrpar::LexerTypes>::LexemeT, {actionskind}<'input>>>,
{parse_paramdef})
-> {actionskind}<'input> {{",
usize::from(pidx),
quote!(#pidx_num),
storaget = type_name::<StorageT>(),
lexertypest = type_name::<LexerTypesT>(),
prefix = ACTION_PREFIX,
Expand Down Expand Up @@ -1242,11 +1275,6 @@ where
}
}

/// Return a version of the string `s` which is safe to embed in source code as a string.
fn str_escape(s: &str) -> String {
s.replace('\\', "\\\\").replace('"', "\\\"")
}

/// This function is called by generated files; it exists so that generated files don't require a
/// dependency on serde and rmps.
#[doc(hidden)]
Expand Down
11 changes: 11 additions & 0 deletions lrpar/src/lib/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use cactus::Cactus;
use cfgrammar::{yacc::YaccGrammar, RIdx, Span, TIdx};
use lrtable::{Action, StIdx, StateTable};
use num_traits::{AsPrimitive, PrimInt, Unsigned};
use proc_macro2::TokenStream;
use quote::quote;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -621,6 +623,15 @@ pub enum RecoveryKind {
None,
}

impl quote::ToTokens for RecoveryKind {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(match *self {
RecoveryKind::CPCTPlus => quote!(::lrpar::RecoveryKind::CPCTPlus),
RecoveryKind::None => quote!(::lrpar::RecoveryKind::None),
})
}
}

/// A lexing or parsing error. Although the two are quite distinct in terms of what can be reported
/// to users, both can (at least conceptually) occur at any point of the intertwined lexing/parsing
/// process.
Expand Down

0 comments on commit 3641e71

Please sign in to comment.