use std::fmt::{self, Display};
use phf::{phf_map, Map};
macro_rules! define_symbol {
(lengths = $lengths:expr, $( $group:ident: { $( $string:literal => $name:ident, )+ }, )*) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Symbol {
$( $( $name, )+ )*
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum SymbolGroup {
$( $group, )*
}
impl Symbol {
pub(crate) const SYMBOLS: Map<&'static str, Self> = phf_map! {
$( $($string => Self::$name,)+ )*
};
pub fn parse(input: &str) -> Option<(Symbol, usize)> {
for &len in $lengths.iter().rev() {
let symbol = input.get(0..len).and_then(|start| Self::SYMBOLS.get(start));
if let Some(&v) = symbol {
return Some((v, len));
}
}
None
}
pub fn as_str(self) -> &'static str {
match self {
$( $( Self::$name => $string, )+ )*
}
}
pub fn group(self) -> SymbolGroup {
match self {
$(
$( Self::$name => SymbolGroup::$group, )+
)*
}
}
}
impl Display for Symbol {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "`{}`", self.as_str())
}
}
}
}
define_symbol! {
lengths = [1, 2, 3, 4, 5, 6, 8],
Operator: {
"+" => Add,
"-" => Sub,
"*" => Mul,
"/" => Div,
"%" => Rem,
"^" => Pow,
"#" => Len,
"&" => BitAnd,
"~" => BitXor,
"|" => BitOr,
"<" => Less,
">" => Greater,
"=" => Assign,
"(" => RoundBracketLeft,
")" => RoundBracketRight,
"{" => CurlyBracketLeft,
"}" => CurlyBracketRight,
"[" => SquareBracketLeft,
"]" => SquareBracketRight,
":" => Colon,
";" => Semicolon,
"," => Comma,
"." => Period,
"<<" => ShiftLeft,
">>" => ShiftRight,
"//" => IntDiv,
"==" => Equal,
"~=" => NotEqual,
"<=" => LessEqual,
">=" => GreaterEqual,
"::" => DoubleColon,
".." => DoublePeriod,
"..." => TriplePeriod,
},
Keyword: {
"do" => Do,
"if" => If,
"in" => In,
"or" => Or,
"and" => And,
"end" => End,
"for" => For,
"not" => Not,
"else" => Else,
"goto" => Goto,
"then" => Then,
"break" => Break,
"local" => Local,
"until" => Until,
"while" => While,
"elseif" => ElseIf,
"repeat" => Repeat,
"return" => Return,
"function" => Function,
},
Value: {
"nil" => Nil,
"false" => False,
"true" => True,
},
}