use std::collections::{ HashMap, HashSet };
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned, format_ident};
pub use self::Expr::*;
use crate::ast::*;
use crate::analysis;
pub fn report_error(span: Span, msg: String) -> TokenStream {
quote_spanned!(span=>compile_error!(#msg);)
}
pub fn report_error_expr(span: Span, msg: String) -> TokenStream {
quote_spanned!(span=> { compile_error!(#msg); panic!() })
}
fn extra_args_def(grammar: &Grammar) -> TokenStream {
let args: Vec<TokenStream> = grammar.args.iter().map(|&(ref name, ref tp)| {
quote!(, #name: #tp)
}).collect();
quote!(#(#args)*)
}
fn extra_args_call(grammar: &Grammar) -> TokenStream {
let args: Vec<TokenStream> = grammar.args.iter().map(|&(ref name, _)| {
quote!(, #name)
}).collect();
quote!(#(#args)*)
}
#[derive(Clone)]
struct Context<'a> {
rules: &'a HashMap<String, &'a Rule>,
rules_from_args: HashSet<String>,
extra_args_call: TokenStream,
extra_args_def: TokenStream,
}
pub(crate) fn compile_grammar(grammar: &Grammar) -> TokenStream {
let name = &grammar.name;
let mut items = vec![make_parse_state(&grammar)];
let analysis = analysis::check(&grammar);
let context = &Context {
rules: &analysis.rules,
rules_from_args: HashSet::new(),
extra_args_call: extra_args_call(grammar),
extra_args_def: extra_args_def(grammar),
};
let mut seen_rule_names = HashSet::new();
for item in &grammar.items {
match item {
Item::Use(tt) => items.push(tt.clone()),
Item::Rule(rule) => {
if seen_rule_names.insert(rule.name.to_string()) {
if rule.cached && !(rule.params.is_empty() && rule.ty_params.is_none()) {
items.push(report_error(rule.name.span(), format!("rules with arguments cannot use #[cache]")));
}
if rule.visibility.is_some() {
for param in &rule.params {
match ¶m.ty {
RuleParamTy::Rule(..) => items.push(
report_error(param.name.span(), format!("parameters on `pub rule` must be Rust types"))
),
_ => {}
}
}
items.push(compile_rule_export(context, rule));
}
items.push(compile_rule(context, rule));
} else {
items.push(report_error(rule.name.span(), format!("duplicate rule `{}`", rule.name)));
}
}
}
}
let doc = &grammar.doc;
let input_type = &grammar.input_type;
let visibility = &grammar.visibility;
let mut errors = Vec::new();
for rec in &analysis.left_recursion {
errors.push(report_error(rec.span, rec.msg()));
}
quote! {
#doc
#visibility mod #name {
#[allow(unused_imports)]
use super::*;
type Input = #input_type;
type PositionRepr = <Input as ::peg::Parse>::PositionRepr;
#(#errors)*
#(#items)*
}
}
}
fn make_parse_state(grammar: &Grammar) -> TokenStream {
let mut cache_fields_def: Vec<TokenStream> = Vec::new();
let mut cache_fields: Vec<Ident> = Vec::new();
for rule in grammar.iter_rules() {
if rule.cached {
let name = format_ident!("{}_cache", rule.name);
let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(()));
cache_fields_def.push(quote!{ #name: ::std::collections::HashMap<usize, ::peg::RuleResult<#ret_ty>> });
cache_fields.push(name);
}
}
quote! {
struct ParseState<'input> {
_phantom: ::std::marker::PhantomData<&'input ()>,
#(#cache_fields_def),*
}
impl<'input> ParseState<'input> {
fn new() -> ParseState<'input> {
ParseState {
_phantom: ::std::marker::PhantomData,
#(#cache_fields: ::std::collections::HashMap::new()),*
}
}
}
}
}
fn rule_params_list(rule: &Rule) -> Vec<TokenStream> {
rule.params.iter().map(|param| {
let name = ¶m.name;
match ¶m.ty {
RuleParamTy::Rust(ty) => quote!{ #name: #ty },
RuleParamTy::Rule(ty) => quote!{
#name: impl Fn(&'input Input, &mut ParseState<'input>, &mut ::peg::error::ErrorState, usize) -> ::peg::RuleResult<#ty>
},
}
}).collect()
}
fn compile_rule(context: &Context, rule: &Rule) -> TokenStream {
let ref rule_name = rule.name;
let name = format_ident!("__parse_{}", rule.name);
let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(()));
let result_used = rule.ret_type.is_some();
let ty_params = rule.ty_params.as_ref().map(|x| &x[..]).unwrap_or(&[]);
let mut context = context.clone();
context.rules_from_args.extend(rule.params.iter().map(|param| { param.name.to_string() }));
let body = compile_expr(&context, &rule.expr, result_used);
let wrapped_body = if cfg!(feature = "trace") {
let str_rule_name = rule_name.to_string();
quote!{{
let loc = ::peg::Parse::position_repr(__input, __pos);
println!("[PEG_TRACE] Attempting to match rule `{}` at {}", #str_rule_name, loc);
let __peg_result: ::peg::RuleResult<#ret_ty> = {#body};
match __peg_result {
::peg::RuleResult::Matched(epos, v) => {
let eloc = ::peg::Parse::position_repr(__input, epos);
println!("[PEG_TRACE] Matched rule `{}` at {} to {}", #str_rule_name, loc, eloc);
::peg::RuleResult::Matched(epos, v)
}
::peg::RuleResult::Failed => {
println!("[PEG_TRACE] Failed to match rule `{}` at {}", #str_rule_name, loc);
::peg::RuleResult::Failed
}
}
}}
} else { body };
let extra_args_def = &context.extra_args_def;
let rule_params = rule_params_list(rule);
if rule.cached {
let cache_field = format_ident!("{}_cache", rule.name);
let cache_trace = if cfg!(feature = "trace") {
let str_rule_name = rule.name.to_string();
quote!{
let loc = ::peg::Parse::position_repr(__input, __pos);
match &entry {
&::peg::RuleResult::Matched(..) => println!("[PEG_TRACE] Cached match of rule {} at {}", #str_rule_name, loc),
&Failed => println!("[PEG_TRACE] Cached fail of rule {} at {}", #str_rule_name, loc),
};
}
} else {
quote!()
};
quote! {
fn #name<'input #(, #ty_params)*>(__input: &'input Input, __state: &mut ParseState<'input>, __err_state: &mut ::peg::error::ErrorState, __pos: usize #extra_args_def #(, #rule_params)*) -> ::peg::RuleResult<#ret_ty> {
#![allow(non_snake_case, unused)]
if let Some(entry) = __state.#cache_field.get(&__pos) {
#cache_trace
return entry.clone();
}
let __rule_result = #wrapped_body;
__state.#cache_field.insert(__pos, __rule_result.clone());
__rule_result
}
}
} else {
quote! {
fn #name<'input #(, #ty_params)*>(__input: &'input Input, __state: &mut ParseState<'input>, __err_state: &mut ::peg::error::ErrorState, __pos: usize #extra_args_def #(, #rule_params)*) -> ::peg::RuleResult<#ret_ty> {
#![allow(non_snake_case, unused)]
#wrapped_body
}
}
}
}
fn compile_rule_export(context: &Context, rule: &Rule) -> TokenStream {
let doc = &rule.doc;
let name = &rule.name;
let ret_ty = rule.ret_type.clone().unwrap_or_else(|| quote!(()));
let visibility = &rule.visibility;
let parse_fn = format_ident!("__parse_{}", rule.name.to_string(), span = name.span());
let ty_params = rule.ty_params.as_ref().map(|x| &x[..]).unwrap_or(&[]);
let rule_params = rule_params_list(rule);
let rule_params_call: Vec<TokenStream> = rule.params.iter().map(|param| {
let param_name = ¶m.name;
quote!(#param_name)
}).collect();
let extra_args_def = &context.extra_args_def;
let extra_args_call = &context.extra_args_call;
quote! {
#doc
#visibility fn #name<'input #(, #ty_params)*>(__input: &'input Input #extra_args_def #(, #rule_params)*) -> ::std::result::Result<#ret_ty, ::peg::error::ParseError<PositionRepr>> {
#![allow(non_snake_case, unused)]
let mut __err_state = ::peg::error::ErrorState::new(::peg::Parse::start(__input));
let mut __state = ParseState::new();
match #parse_fn(__input, &mut __state, &mut __err_state, ::peg::Parse::start(__input) #extra_args_call #(, #rule_params_call)*) {
::peg::RuleResult::Matched(__pos, __value) => {
if __pos == __input.len() {
return Ok(__value)
} else {
__err_state.mark_failure(__pos, "EOF");
}
}
_ => ()
}
__state = ParseState::new();
__err_state.reparse_for_error();
match #parse_fn(__input, &mut __state, &mut __err_state, ::peg::Parse::start(__input) #extra_args_call #(, #rule_params_call)*) {
::peg::RuleResult::Matched(__pos, __value) => {
if __pos == __input.len() {
panic!("Parser is nondeterministic: succeeded when reparsing for error position");
} else {
__err_state.mark_failure(__pos, "EOF");
}
}
_ => ()
}
Err(__err_state.into_parse_error(__input))
}
}
}
fn name_or_ignore(n: Option<&Ident>) -> TokenStream {
match n {
Some(n) => quote!(#n),
None => quote!(_)
}
}
fn ordered_choice(mut rs: impl DoubleEndedIterator<Item=TokenStream>) -> TokenStream {
rs.next_back().map(|last| rs.rfold(last, |fallback, preferred| {
quote! {{
let __choice_res = #preferred;
match __choice_res {
::peg::RuleResult::Matched(__pos, __value) => ::peg::RuleResult::Matched(__pos, __value),
::peg::RuleResult::Failed => #fallback
}
}}
})).expect("ordered choice must not be empty")
}
fn labeled_seq(context: &Context, exprs: &[TaggedExpr], inner: TokenStream) -> TokenStream {
exprs.iter().rfold(inner, |then, expr| {
let value_name = expr.name.as_ref();
let name_pat = name_or_ignore(value_name);
let seq_res = compile_expr(context, &expr.expr, value_name.is_some());
quote!{{
let __seq_res = #seq_res;
match __seq_res {
::peg::RuleResult::Matched(__pos, #name_pat) => { #then }
::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
}
}}
})
}
fn cond_swap<T>(swap: bool, tup: (T, T)) -> (T, T) {
let (a, b) = tup;
if swap {
(b, a)
} else {
(a, b)
}
}
fn compile_expr(context: &Context, e: &Expr, result_used: bool) -> TokenStream {
match e {
LiteralExpr(ref s) => {
let escaped_str = s.to_string();
quote!{ match ::peg::ParseLiteral::parse_string_literal(__input, __pos, #s) {
::peg::RuleResult::Matched(__pos, __val) => ::peg::RuleResult::Matched(__pos, __val),
::peg::RuleResult::Failed => __err_state.mark_failure(__pos, #escaped_str)
}}
}
PatternExpr(ref pattern) => {
let invert = false;
let pat_str = pattern.to_string();
let (in_set, not_in_set) = cond_swap(invert, (
quote!{ ::peg::RuleResult::Matched(__next, ()) },
quote!{ __err_state.mark_failure(__pos, #pat_str) },
));
let in_set_arm = quote!( #pattern => #in_set, );
quote!{
match ::peg::ParseElem::parse_elem(__input, __pos) {
::peg::RuleResult::Matched(__next, __ch) => match __ch {
#in_set_arm
_ => #not_in_set,
}
::peg::RuleResult::Failed => __err_state.mark_failure(__pos, #pat_str)
}
}
}
RuleExpr(ref rule_name, ref rule_args) if context.rules_from_args.contains(&rule_name.to_string()) => {
if !rule_args.is_empty() {
return report_error_expr(rule_name.span(), format!("rule closure does not accept arguments"));
}
quote!{ #rule_name(__input, __state, __err_state, __pos) }
}
RuleExpr(ref rule_name, ref rule_args) => {
let rule_name_str = rule_name.to_string();
let rule_def = if let Some(rule_def) = context.rules.get(&rule_name_str) {
rule_def
} else {
return report_error_expr(rule_name.span(), format!("undefined rule `{}`", rule_name_str));
};
if result_used && rule_def.ret_type.is_none() {
let msg = format!("using result of rule `{}`, which does not return a value", rule_name_str);
return report_error_expr(rule_name.span(), msg);
}
if rule_def.params.len() != rule_args.len() {
return report_error_expr(rule_name.span(),
format!("this rule takes {} parameters but {} parameters were supplied", rule_def.params.len(), rule_args.len()));
}
for (param, arg) in rule_def.params.iter().zip(rule_args.iter()) {
match (¶m.ty, &arg) {
(RuleParamTy::Rust(..), RuleArg::Peg(..)) => {
return report_error_expr(rule_name.span(),
format!("parameter `{}` expects a value, but a PEG expression was passed", param.name));
}
(RuleParamTy::Rule(..), RuleArg::Rust(..)) => {
return report_error_expr(rule_name.span(),
format!("parameter `{}` expects a PEG expression, but a value was passed", param.name));
},
(RuleParamTy::Rule(..), RuleArg::Peg(..)) => (),
(RuleParamTy::Rust(..), RuleArg::Rust(..)) => (),
}
}
let func = format_ident!("__parse_{}", rule_name, span=rule_name.span());
let extra_args_call = &context.extra_args_call;
let rule_args_call: Vec<TokenStream> = rule_args.iter().map(|arg| {
match arg {
RuleArg::Peg(e) => {
let expr = compile_expr(context, e, true);
quote!{ |__input, __state, __err_state, __pos| { #expr } }
}
RuleArg::Rust(e) => e.clone(),
}
}).collect();
if result_used {
quote!{ #func(__input, __state, __err_state, __pos #extra_args_call #(, #rule_args_call)*) }
} else {
quote!{
match #func(__input, __state, __err_state, __pos #extra_args_call #(, #rule_args_call)*){
::peg::RuleResult::Matched(pos, _) => ::peg::RuleResult::Matched(pos, ()),
::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
}
}
}
}
MethodExpr(ref method, ref args) => {
quote!{ __input.#method(__pos, #args) }
}
ChoiceExpr(ref exprs) => {
ordered_choice(exprs.iter().map(|expr| compile_expr(context, expr, result_used)))
}
OptionalExpr(ref e) => {
let optional_res = compile_expr(context, e, result_used);
if result_used {
quote!{
match #optional_res {
::peg::RuleResult::Matched(__newpos, __value) => { ::peg::RuleResult::Matched(__newpos, Some(__value)) },
::peg::RuleResult::Failed => { ::peg::RuleResult::Matched(__pos, None) },
}
}
} else {
quote!{
match #optional_res {
::peg::RuleResult::Matched(__newpos, _) => { ::peg::RuleResult::Matched(__newpos, ()) },
::peg::RuleResult::Failed => { ::peg::RuleResult::Matched(__pos, ()) },
}
}
}
}
Repeat(ref e, ref bounds, ref sep) => {
let inner = compile_expr(context, e, result_used);
let (min, max) = match *bounds {
BoundedRepeat::None => (None, None),
BoundedRepeat::Plus => (Some(quote!(1)), None),
BoundedRepeat::Exact(ref code) => (Some(code.clone()), Some(code.clone())),
BoundedRepeat::Both(ref min, ref max) => (min.clone(), max.clone())
};
let match_sep = if let Some(sep) = sep {
let sep_inner = compile_expr(context, sep, false);
quote! {
let __pos = if __repeat_value.is_empty() { __pos } else {
let __sep_res = #sep_inner;
match __sep_res {
::peg::RuleResult::Matched(__newpos, _) => { __newpos },
::peg::RuleResult::Failed => break,
}
};
}
} else { quote!() };
let result = if result_used {
quote!( __repeat_value )
} else {
quote!( () )
};
let (repeat_vec, repeat_step) =
if result_used || min.is_some() || max.is_some() || sep.is_some() {
(Some(quote! { let mut __repeat_value = vec!(); }),
Some(quote! { __repeat_value.push(__value); }))
} else {
(None, None)
};
let max_check = max.map(|max| { quote! { if __repeat_value.len() >= #max { break } }});
let result_check = if let Some(min) = min {
quote!{
if __repeat_value.len() >= #min {
::peg::RuleResult::Matched(__repeat_pos, #result)
} else {
::peg::RuleResult::Failed
}
}
} else {
quote!{ ::peg::RuleResult::Matched(__repeat_pos, #result) }
};
quote!{{
let mut __repeat_pos = __pos;
#repeat_vec
loop {
let __pos = __repeat_pos;
#match_sep
#max_check
let __step_res = #inner;
match __step_res {
::peg::RuleResult::Matched(__newpos, __value) => {
__repeat_pos = __newpos;
#repeat_step
},
::peg::RuleResult::Failed => {
break;
}
}
}
#result_check
}}
}
PosAssertExpr(ref e) => {
let assert_res = compile_expr(context, e, result_used);
quote! {{
__err_state.suppress_fail += 1;
let __assert_res = #assert_res;
__err_state.suppress_fail -= 1;
match __assert_res {
::peg::RuleResult::Matched(_, __value) => ::peg::RuleResult::Matched(__pos, __value),
::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
}
}}
}
NegAssertExpr(ref e) => {
let assert_res = compile_expr(context, e, false);
quote! {{
__err_state.suppress_fail += 1;
let __assert_res = #assert_res;
__err_state.suppress_fail -= 1;
match __assert_res {
::peg::RuleResult::Failed => ::peg::RuleResult::Matched(__pos, ()),
::peg::RuleResult::Matched(..) => ::peg::RuleResult::Failed,
}
}}
}
ActionExpr(ref exprs, ref code, is_cond) => {
labeled_seq(context, &exprs, {
if *is_cond {
quote!{
match { #code } {
Ok(res) => ::peg::RuleResult::Matched(__pos, res),
Err(expected) => {
__err_state.mark_failure(__pos, expected);
::peg::RuleResult::Failed
},
}
}
} else {
quote!{ ::peg::RuleResult::Matched(__pos, { #code }) }
}
})
}
MatchStrExpr(ref expr) => {
let inner = compile_expr(context, expr, false);
quote! {{
let str_start = __pos;
match #inner {
::peg::RuleResult::Matched(__newpos, _) => { ::peg::RuleResult::Matched(__newpos, ::peg::ParseSlice::parse_slice(__input, str_start, __newpos)) },
::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
}
}}
}
PositionExpr => {
quote! { ::peg::RuleResult::Matched(__pos, __pos) }
}
QuietExpr(ref expr) => {
let inner = compile_expr(context, expr, result_used);
quote! {{
__err_state.suppress_fail += 1;
let res = #inner;
__err_state.suppress_fail -= 1;
res
}}
}
FailExpr(ref expected) => {
quote!{{ __err_state.mark_failure(__pos, #expected); ::peg::RuleResult::Failed }}
}
PrecedenceExpr{ ref levels } => {
let mut pre_rules = Vec::new();
let mut level_code = Vec::new();
let mut span_capture = None;
for (prec, level) in levels.iter().enumerate() {
let prec = prec as i32;
let mut post_rules = Vec::new();
for op in &level.operators {
if op.elements.len() < 1 {
return quote!(compile_error!("incomplete rule"));
}
let left_arg = &op.elements[0];
let l_arg = name_or_ignore(left_arg.name.as_ref());
let right_arg = &op.elements[op.elements.len() - 1];
let r_arg = name_or_ignore(right_arg.name.as_ref());
let action = &op.action;
let action = if let Some((lpos_name, val_name, rpos_name, wrap_action)) = &span_capture {
quote!((|#lpos_name, #val_name, #rpos_name| {#wrap_action})(__lpos, #action, __pos))
} else {
quote!({#action})
};
match (&left_arg.expr, &right_arg.expr) {
(&PositionExpr, &PositionExpr) if op.elements.len() == 3 => { match &op.elements[1].expr {
&MarkerExpr(..) => (),
_ => return quote!(compile_error!("span capture rule must be `l:position!() n:@ r:position!()"))
}
span_capture = Some((
name_or_ignore(op.elements[0].name.as_ref()),
name_or_ignore(op.elements[1].name.as_ref()),
name_or_ignore(op.elements[2].name.as_ref()),
&op.action
));
}
(&MarkerExpr(la), &MarkerExpr(ra)) if op.elements.len() >= 3 => { let new_prec = match (la, ra) {
(true, false) => prec + 1, (false, true) => prec, _ => return quote!(compile_error!("precedence rules must use `@` and `(@)` to indicate associativity"))
};
post_rules.push(
labeled_seq(context, &op.elements[1..op.elements.len()-1], {
quote!{
if let ::peg::RuleResult::Matched(__pos, #r_arg) = __recurse(__pos, #new_prec, __state, __err_state) {
let #l_arg = __infix_result;
__infix_result = #action;
::peg::RuleResult::Matched(__pos, ())
} else { ::peg::RuleResult::Failed }
}
})
);
}
(&MarkerExpr(_), _) if op.elements.len() >= 2 => { post_rules.push(
labeled_seq(context, &op.elements[1..op.elements.len()], {
quote!{
let #l_arg = __infix_result;
__infix_result = #action;
::peg::RuleResult::Matched(__pos, ())
}
})
);
}
(_, &MarkerExpr(a)) if op.elements.len() >= 2 => { let new_prec = match a {
true => prec,
false => prec + 1,
};
pre_rules.push(
labeled_seq(context, &op.elements[..op.elements.len()-1], {
quote!{
if let ::peg::RuleResult::Matched(__pos, #r_arg) = __recurse(__pos, #new_prec, __state, __err_state) {
::peg::RuleResult::Matched(__pos, #action)
} else { ::peg::RuleResult::Failed }
}
})
);
}
_ => { pre_rules.push(
labeled_seq(context, &op.elements, {
quote!{ ::peg::RuleResult::Matched(__pos, #action) }
})
);
}
};
}
if !post_rules.is_empty() {
level_code.push(quote!{
if #prec >= __min_prec {
#(
if let ::peg::RuleResult::Matched(__pos, ()) = #post_rules {
return (__infix_result, ::peg::RuleResult::Matched(__pos, ()));
}
)*
}
});
}
}
let (enter, leave) = if cfg!(feature = "trace") {
(quote!{println!("[PEG_TRACE] Entering level {}", min_prec);},
quote!{println!("[PEG_TRACE] Leaving level {}", min_prec);})
} else {
(quote!(), quote!())
};
quote!{{
fn __infix_parse<T, S>(
state: &mut S,
err_state: &mut ::peg::error::ErrorState,
min_prec: i32,
lpos: usize,
prefix_atom: &Fn(usize, &mut S, &mut ::peg::error::ErrorState, &Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<T>) -> ::peg::RuleResult<T>,
level_code: &Fn(usize, usize, i32, T, &mut S, &mut ::peg::error::ErrorState, &Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<T>) -> (T, ::peg::RuleResult<()>),
) -> ::peg::RuleResult<T> {
let initial = {
prefix_atom(lpos, state, err_state, &|pos, min_prec, state, err_state| {
__infix_parse(state, err_state, min_prec, pos, prefix_atom, level_code)
})
};
if let ::peg::RuleResult::Matched(pos, mut infix_result) = initial {
#enter
let mut repeat_pos = pos;
loop {
let (val, res) = level_code(
repeat_pos,
lpos,
min_prec,
infix_result,
state,
err_state,
&|pos, min_prec, state, err_state| {
__infix_parse(state, err_state, min_prec, pos, prefix_atom, level_code)
}
);
infix_result = val;
if let ::peg::RuleResult::Matched(pos, ()) = res {
repeat_pos = pos;
continue;
}
break;
}
#leave
::peg::RuleResult::Matched(repeat_pos, infix_result)
} else {
::peg::RuleResult::Failed
}
}
__infix_parse(__state, __err_state, 0, __pos,
&|__pos, __state, __err_state, __recurse| {
let __lpos = __pos;
#(
if let ::peg::RuleResult::Matched(__pos, __v) = #pre_rules {
return ::peg::RuleResult::Matched(__pos, __v);
}
)*
::peg::RuleResult::Failed
},
&|__pos, __lpos, __min_prec, mut __infix_result, __state, __err_state, __recurse| {
#(#level_code)*
(__infix_result, ::peg::RuleResult::Failed)
}
)
}}
}
MarkerExpr { .. } => {
return quote!(compile_error!("`@` is only allowed in `precedence!{}`"));
}
}
}