use crate::grammar::repr as r;
use crate::rust::RustWrite;
use std::io::{self, Write};
pub fn emit_action_code<W: Write>(grammar: &r::Grammar, rust: &mut RustWrite<W>) -> io::Result<()> {
for (i, defn) in grammar.action_fn_defns.iter().enumerate() {
rust!(rust, "");
if !grammar.parameters.is_empty() {
rust!(rust, "#[allow(unused_variables)]");
}
match defn.kind {
r::ActionFnDefnKind::User(ref data) => {
emit_user_action_code(grammar, rust, i, defn, data)?
}
r::ActionFnDefnKind::Lookaround(ref variant) => {
emit_lookaround_action_code(grammar, rust, i, defn, variant)?
}
r::ActionFnDefnKind::Inline(ref data) => {
emit_inline_action_code(grammar, rust, i, defn, data)?
}
}
}
Ok(())
}
fn ret_type_string(grammar: &r::Grammar, defn: &r::ActionFnDefn) -> String {
if defn.fallible {
format!(
"Result<{},{}lalrpop_util::ParseError<{},{},{}>>",
defn.ret_type,
grammar.prefix,
grammar.types.terminal_loc_type(),
grammar.types.terminal_token_type(),
grammar.types.error_type()
)
} else {
format!("{}", defn.ret_type)
}
}
fn emit_user_action_code<W: Write>(
grammar: &r::Grammar,
rust: &mut RustWrite<W>,
index: usize,
defn: &r::ActionFnDefn,
data: &r::UserActionFnDefn,
) -> io::Result<()> {
let ret_type = ret_type_string(grammar, defn);
let mut arguments: Vec<String> = data
.arg_patterns
.iter()
.zip(
data.arg_types
.iter()
.cloned()
.map(|t| grammar.types.spanned_type(t)),
)
.map(|(name, ty)| format!("(_, {name}, _): {ty}"))
.collect();
if data.arg_patterns.is_empty() {
arguments.extend(vec![
format!(
"{}lookbehind: &{}",
grammar.prefix,
grammar.types.terminal_loc_type()
),
format!(
"{}lookahead: &{}",
grammar.prefix,
grammar.types.terminal_loc_type()
),
]);
}
rust!(
rust,
"#[allow(clippy::too_many_arguments, clippy::needless_lifetimes, clippy::just_underscores_and_digits)]"
);
rust.fn_header(
&r::Visibility::Priv,
format!("{}action{}", grammar.prefix, index),
)
.with_grammar(grammar)
.with_parameters(arguments)
.with_return_type(ret_type)
.emit()?;
rust!(rust, "{{");
if data.code != "()" {
rust!(rust, "{}", data.code);
}
rust!(rust, "}}");
Ok(())
}
fn emit_lookaround_action_code<W: Write>(
grammar: &r::Grammar,
rust: &mut RustWrite<W>,
index: usize,
_defn: &r::ActionFnDefn,
data: &r::LookaroundActionFnDefn,
) -> io::Result<()> {
rust!(rust, "#[allow(clippy::needless_lifetimes)]");
rust.fn_header(
&r::Visibility::Priv,
format!("{}action{}", grammar.prefix, index),
)
.with_grammar(grammar)
.with_parameters(vec![
format!(
"{}lookbehind: &{}",
grammar.prefix,
grammar.types.terminal_loc_type()
),
format!(
"{}lookahead: &{}",
grammar.prefix,
grammar.types.terminal_loc_type()
),
])
.with_return_type(format!("{}", grammar.types.terminal_loc_type()))
.emit()?;
rust!(rust, "{{");
match *data {
r::LookaroundActionFnDefn::Lookahead => {
rust!(rust, "*{}lookahead", grammar.prefix);
}
r::LookaroundActionFnDefn::Lookbehind => {
rust!(rust, "*{}lookbehind", grammar.prefix);
}
}
rust!(rust, "}}");
Ok(())
}
fn emit_inline_action_code<W: Write>(
grammar: &r::Grammar,
rust: &mut RustWrite<W>,
index: usize,
defn: &r::ActionFnDefn,
data: &r::InlineActionFnDefn,
) -> io::Result<()> {
let ret_type = ret_type_string(grammar, defn);
let arg_types: Vec<_> = data
.symbols
.iter()
.flat_map(|sym| match *sym {
r::InlinedSymbol::Original(ref s) => vec![s.clone()],
r::InlinedSymbol::Inlined(_, ref syms) => syms.clone(),
})
.map(|s| s.ty(&grammar.types))
.collect();
let num_flat_args = arg_types.len();
let mut arguments: Vec<_> = arg_types
.iter()
.map(|&t| grammar.types.spanned_type(t.clone()))
.enumerate()
.map(|(i, t)| format!("{}{}: {}", grammar.prefix, i, t))
.collect();
if arguments.is_empty() {
arguments.extend(vec![
format!(
"{}lookbehind: &{}",
grammar.prefix,
grammar.types.terminal_loc_type()
),
format!(
"{}lookahead: &{}",
grammar.prefix,
grammar.types.terminal_loc_type()
),
]);
}
rust!(
rust,
"#[allow(clippy::too_many_arguments, clippy::needless_lifetimes,
clippy::just_underscores_and_digits)]"
);
rust.fn_header(
&r::Visibility::Priv,
format!("{}action{}", grammar.prefix, index),
)
.with_grammar(grammar)
.with_parameters(arguments)
.with_return_type(ret_type)
.emit()?;
rust!(rust, "{{");
let mut arg_counter = 0;
let mut temp_counter = 0;
for symbol in &data.symbols {
match *symbol {
r::InlinedSymbol::Original(_) => {
arg_counter += 1;
}
r::InlinedSymbol::Inlined(_, ref syms) => {
if !syms.is_empty() {
rust!(
rust,
"let {}start{} = {}{}.0;",
grammar.prefix,
temp_counter,
grammar.prefix,
arg_counter
);
let last_arg_index = arg_counter + syms.len() - 1;
rust!(
rust,
"let {}end{} = {}{}.2;",
grammar.prefix,
temp_counter,
grammar.prefix,
last_arg_index
);
} else {
if arg_counter > 0 {
rust!(
rust,
"let {}start{} = {}{}.2;",
grammar.prefix,
temp_counter,
grammar.prefix,
arg_counter - 1
);
} else if num_flat_args > 0 {
rust!(
rust,
"let {}start{} = {}{}.0;",
grammar.prefix,
temp_counter,
grammar.prefix,
arg_counter
);
} else {
rust!(
rust,
"let {}start{} = *{}lookbehind;",
grammar.prefix,
temp_counter,
grammar.prefix
);
}
if arg_counter < num_flat_args {
rust!(
rust,
"let {}end{} = {}{}.0;",
grammar.prefix,
temp_counter,
grammar.prefix,
arg_counter
);
} else if num_flat_args > 0 {
rust!(
rust,
"let {}end{} = {}{}.2;",
grammar.prefix,
temp_counter,
grammar.prefix,
num_flat_args - 1
);
} else {
rust!(
rust,
"let {}end{} = *{}lookahead;",
grammar.prefix,
temp_counter,
grammar.prefix
);
}
}
temp_counter += 1;
arg_counter += syms.len();
}
}
}
let mut arg_counter = 0;
let mut temp_counter = 0;
let annotate = !grammar.non_lifetime_type_parameters().is_empty();
let lparen = if annotate { "::<" } else { "(" };
for symbol in &data.symbols {
match *symbol {
r::InlinedSymbol::Original(_) => {
arg_counter += 1;
}
r::InlinedSymbol::Inlined(inlined_action, ref syms) => {
rust!(
rust,
"let {}temp{} = {}action{}{}",
grammar.prefix,
temp_counter,
grammar.prefix,
inlined_action.index(),
lparen
);
for t in grammar.non_lifetime_type_parameters() {
rust!(rust, "{},", t);
}
if annotate {
rust!(rust, ">(");
}
for parameter in &grammar.parameters {
rust!(rust, "{},", parameter.name);
}
for i in 0..syms.len() {
rust!(rust, "{}{},", grammar.prefix, arg_counter + i);
}
if syms.is_empty() {
rust!(rust, "&{}start{},", grammar.prefix, temp_counter);
rust!(rust, "&{}end{},", grammar.prefix, temp_counter);
}
if grammar.action_is_fallible(inlined_action) {
rust!(rust, ")?;");
} else {
rust!(rust, ");");
}
rust!(
rust,
"let {}temp{} = ({}start{}, {}temp{}, {}end{});",
grammar.prefix,
temp_counter,
grammar.prefix,
temp_counter,
grammar.prefix,
temp_counter,
grammar.prefix,
temp_counter
);
temp_counter += 1;
arg_counter += syms.len();
}
}
}
let final_action_fallible = grammar.action_is_fallible(data.action);
let (ok_begin, ok_end) = match (defn.fallible, final_action_fallible) {
(true, true) | (false, false) => ("", ""),
(true, false) => ("Ok(", ")"),
(false, true) => unreachable!(),
};
rust!(
rust,
"{}{}action{}{}",
ok_begin,
grammar.prefix,
data.action.index(),
lparen
);
for t in grammar.non_lifetime_type_parameters() {
rust!(rust, "{},", t);
}
if annotate {
rust!(rust, ">(");
}
for parameter in &grammar.parameters {
rust!(rust, "{},", parameter.name);
}
let mut arg_counter = 0;
let mut temp_counter = 0;
for symbol in &data.symbols {
match *symbol {
r::InlinedSymbol::Original(_) => {
rust!(rust, "{}{},", grammar.prefix, arg_counter);
arg_counter += 1;
}
r::InlinedSymbol::Inlined(_, ref syms) => {
rust!(rust, "{}temp{},", grammar.prefix, temp_counter);
temp_counter += 1;
arg_counter += syms.len();
}
}
}
assert!(!data.symbols.is_empty());
rust!(rust, "){}", ok_end);
rust!(rust, "}}");
Ok(())
}