(First of all, I feel I couldn't give proper title with this. Please feel free to change to or purpose better title)
I'm refactoring my code; it gets operator kind from token-sequence, as below:
Before refactoring
pub(in self) trait FromParser: Sized {
type Err;
fn read(parser: &Parser) -> Result<Self, Self::Err>;
}
#[derive(Copy, Clone)]
enum RelationCheckExpressionOps {
Less,
LessEqual,
More,
MoreEqual,
Spaceship,
}
impl FromParser for RelationCheckExpressionOps {
type Err = anyhow::Error;
fn read(parser: &Parser) -> Result<Self, Self::Err> {
match parser.lexer.peek() {
Token::SymLess => {
parser.lexer.next();
Ok(Self::Less)
}
Token::PartLessEq => {
parser.lexer.next();
Ok(Self::LessEqual)
}
Token::SymMore => {
parser.lexer.next();
Ok(Self::More)
}
Token::PartMoreEq => {
parser.lexer.next();
Ok(Self::MoreEqual)
}
Token::PartLessEqMore => {
parser.lexer.next();
Ok(Self::Spaceship)
}
other => anyhow::bail!("excess token: {token:?}", token = other)
}
}
}
// And so on, bunch of same operations for
// different operator precedence group (Additive, Multitive, etc.)
where parser.lexer.next()
consumes current token and parser.lexer.peek()
does not.
My current approach for refactoring is defining macros that match on match
-clause-like arms, and generate whole match
-clause on their count:
After refactoring
macro_rules! operator_from_parser {
($name:ty, $token1:ident => $variant1:ident) => {
impl FromParser for $name {
type Err = anyhow::Error;
fn read(parser: &Parser) -> Result<Self, Self::Err> {
match parser.lexer.peek(){
Token::$token1 => {
parser.lexer.next();
Ok(Self::$variant1)
},
other => excess_token!(other)
}
}
}
};
($name:ty, $token1:ident => $variant1:ident, $token2:ident => $variant2:ident) => {
impl FromParser for $name {
type Err = anyhow::Error;
fn read(parser: &Parser) -> Result<Self, Self::Err> {
match parser.lexer.peek() {
Token::$token1 => {
parser.lexer.next();
Ok(Self::$variant1)
},
Token::$token2 => {
parser.lexer.next();
Ok(Self::$variant2)
},
other => excess_token!(other)
}
}
}
};
($name:ty, $token1:ident => $variant1:ident, $token2:ident => $variant2:ident, $token3:ident => $variant3:ident) => {
impl FromParser for $name {
type Err = anyhow::Error;
fn read(parser: &Parser) -> Result<Self, Self::Err> {
match parser.lexer.peek() {
Token::$token1 => {
parser.lexer.next();
Ok(Self::$variant1)
},
Token::$token2 => {
parser.lexer.next();
Ok(Self::$variant2)
},
Token::$token3 => {
parser.lexer.next();
Ok(Self::$variant3)
},
other => excess_token!(other)
}
}
}
};
($name:ty, $token1:ident => $variant1:ident, $token2:ident => $variant2:ident, $token3:ident => $variant3:ident, $token4:ident => $variant4:ident) => {
impl FromParser for $name {
type Err = anyhow::Error;
fn read(parser: &Parser) -> Result<Self, Self::Err> {
match parser.lexer.peek(){
Token::$token1 => {
parser.lexer.next();
Ok(Self::$variant1)
},
Token::$token2 => {
parser.lexer.next();
Ok(Self::$variant2)
},
Token::$token3 => {
parser.lexer.next();
Ok(Self::$variant3)
},
Token::$token4 => {
parser.lexer.next();
Ok(Self::$variant4)
},
other => excess_token!(other)
}
}
}
};
($name:ty, $token1:ident => $variant1:ident, $token2:ident => $variant2:ident, $token3:ident => $variant3:ident, $token4:ident => $variant4:ident, $token5:ident => $variant5:ident) => {
impl FromParser for $name {
type Err = anyhow::Error;
fn read(parser: &Parser) -> Result<Self, Self::Err> {
match parser.lexer.peek() {
Token::$token1 => {
parser.lexer.next();
Ok(Self::$variant1)
},
Token::$token2 => {
parser.lexer.next();
Ok(Self::$variant2)
},
Token::$token3 => {
parser.lexer.next();
Ok(Self::$variant3)
},
Token::$token4 => {
parser.lexer.next();
Ok(Self::$variant4)
},
Token::$token5 => {
parser.lexer.next();
Ok(Self::$variant5)
},
other => excess_token!(other)
}
}
}
};
}
#[derive(Copy, Clone)]
enum RelationCheckExpressionOps {
Less,
LessEqual,
More,
MoreEqual,
Spaceship,
}
operator_from_parser!(RelationCheckExpressionOps, SymLess => Less, PartLessEq => LessEqual, SymMore => More, PartMoreEq => MoreEqual, PartLessEqMore => Spaceship);
I'd like to ask: can they be more elegant? It's ugly to repeating arm-matching.
What I've tried:
-
$($token:ident => $variant:ident,)+
inoperator_from_parser
, but macros cannot be expanded inmatch
-clause arms. I feel it does not go well.