Hello rustaceans!
I've just tried integrating the logos
crate in my latest project, and I'm having a minor issue.
Here's a part of my code:
#[derive(PartialEq, Debug, Logos)]
#[logos(error = ParseError)]
pub(crate) enum MaskAtom {
#[regex(r"@\d{1,2}", octave)]
Octave(NonZeroU8),
#[regex(r"\$\d{1,3}", (normal_cmd_callback_generator("length")))]
Length(u8),
#[regex(r"!\d{1,3}", (normal_cmd_callback_generator("volume")))]
Volume(u8),
#[token(".")]
#[regex(r"[ \t\n\f\r]+", junk)]
Rest,
}
fn normal_cmd_callback_generator(
for_: &str,
) -> impl Fn(&mut Lexer<MaskAtom>) -> Result<u8, ParseError> + '_ {
move |lex| {
lex.slice()[1..].parse().map_err(|_| ParseError {
msg: format!("Expected a {} number", for_),
})
}
}
I'm using a function to generate another function to call when a length or volume pattern is found. However, I've found that I need to put the generator call between parenthesis for this code to compile (because of macro magic syntax I suppose) and this makes the callback function get generated every time a pattern is found. I can't make a scope where I generate the function and then move it in a closure and return it.
I also cannot make a playground out of it because the logos crate isn't supported but this snippet compiles fine in a cargo project with logos as the single dep:
use logos::{Lexer, Logos, Skip};
use std::fmt::Debug;
use std::num::NonZeroU8;
#[derive(Clone, PartialEq)]
pub(crate) struct ParseError {
msg: String,
}
impl Debug for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} just before {:?}", self.msg, (0, 0))
}
}
impl Default for ParseError {
fn default() -> Self {
ParseError {
msg: "Dummy error".to_string(),
}
}
}
#[derive(PartialEq, Debug, Logos)]
#[logos(error = ParseError)]
pub(crate) enum MaskAtom {
#[regex(r"@\d{1,2}", octave)]
Octave(NonZeroU8),
#[regex(r"\$\d{1,3}", (normal_cmd_callback_generator("length")))]
Length(u8),
#[regex(r"!\d{1,3}", (normal_cmd_callback_generator("volume")))]
Volume(u8),
#[token(".")]
#[regex(r"[ \t\n\f\r]+", junk)]
Rest,
}
fn normal_cmd_callback_generator(
for_: &str,
) -> impl Fn(&mut Lexer<MaskAtom>) -> Result<u8, ParseError> + '_ {
println!("generator was called!");
move |lex| {
lex.slice()[1..].parse().map_err(|_| ParseError {
msg: format!("Expected a {} number", for_),
})
}
}
fn junk(lex: &mut Lexer<MaskAtom>) -> Skip {
Skip
}
fn octave(lex: &mut Lexer<MaskAtom>) -> Result<NonZeroU8, ParseError> {
NonZeroU8::new(lex.slice()[1..].parse().map_err(|_| ParseError {
msg: "Expected an octave number".to_string(),
})?)
.ok_or(ParseError {
msg: "Octave 0 does not exist".to_string(),
})
}
fn main() {
MaskAtom::lexer("$15!45$1!123").for_each(|r| println!("Found: {:?}", r.unwrap()));
}
The entire source file is on GitHub.
So, can I give to this logos macro derive a static but generated closure? Is this even a real (performance) issue?