use std::{borrow::Cow, fmt::Display};
use device_driver_common::{
span::{SpanExt, Spanned},
specifiers::{Access, AddressMode, BaseType, ByteOrder, Integer},
};
use logos::Logos;
pub fn lex(source: &str) -> Vec<Spanned<Token<'_>>> {
Token::lexer(source)
.spanned()
.map(|(token, span)| match token {
Ok(token) => token.with_span(span),
Err(()) => Token::Error.with_span(span),
})
.collect()
}
#[derive(Debug, Clone, Copy, PartialEq, Logos)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[logos(skip r"[ \t\r\n]+")] #[logos(skip(r"//[^\n]*", allow_greedy = true))] pub enum Token<'src> {
#[regex(r"///[^\n]*", allow_greedy = true, callback = |lex| lex.slice().trim_start_matches("///"))]
DocCommentLine(&'src str),
#[regex(r"\p{XID_Start}[\p{XID_Continue}-]*")]
Ident(&'src str),
#[token("{")]
CurlyOpen,
#[token("}")]
CurlyClose,
#[token("[")]
BracketOpen,
#[token("]")]
BracketClose,
#[token(",")]
Comma,
#[token(":")]
Colon,
#[token("_")]
Underscore,
#[token("->")]
Arrow,
#[token("*")]
Star,
#[token("try")]
Try,
#[token("as")]
As,
#[token("allow")]
Allow,
#[token("default")]
Default,
#[token("catch-all")]
CatchAll,
#[token("stride")]
Stride,
#[regex(r"-?[0-9][_0-9]*")] #[regex(r"-?0b[_0-1]+")] #[regex(r"-?0o[_0-7]+")] #[regex(r"-?0x[_0-9a-fA-F]+")] Num(&'src str),
#[token("RW", |_| Access::RW)]
#[token("RO", |_| Access::RO)]
#[token("WO", |_| Access::WO)]
Access(Access),
#[token("BE", |_| ByteOrder::BE)]
#[token("LE", |_| ByteOrder::LE)]
ByteOrder(ByteOrder),
#[token("uint", |_| BaseType::Uint)]
#[token("int", |_| BaseType::Int)]
#[token("bool", |_| BaseType::Bool)]
BaseType(BaseType),
#[token("u8", |_| Integer::U8)]
#[token("u16", |_| Integer::U16)]
#[token("u32", |_| Integer::U32)]
#[token("u64", |_| Integer::U64)]
#[token("i8", |_| Integer::I8)]
#[token("i16", |_| Integer::I16)]
#[token("i32", |_| Integer::I32)]
#[token("i64", |_| Integer::I64)]
Integer(Integer),
#[token("mapped", |_| AddressMode::Mapped)]
#[token("indexed", |_| AddressMode::Indexed)]
AddressMode(AddressMode),
#[regex(r#""[^"]*""#, callback = |lex| lex.slice().strip_prefix('"').unwrap().strip_suffix('"').unwrap())]
String(&'src str),
#[regex(r"\S", priority = 0)] Unexpected(&'src str),
Error, }
impl Display for Token<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Token::DocCommentLine(_) => write!(f, "doc comment"),
Token::Ident(_) => write!(f, "identifier"),
Token::CurlyOpen => write!(f, "{{"),
Token::CurlyClose => write!(f, "}}"),
Token::BracketOpen => write!(f, "["),
Token::BracketClose => write!(f, "]"),
Token::Colon => write!(f, ":"),
Token::Underscore => write!(f, "_"),
Token::Comma => write!(f, ","),
Token::Arrow => write!(f, "->"),
Token::Star => write!(f, "*"),
Token::Try => write!(f, "try"),
Token::As => write!(f, "as"),
Token::Allow => write!(f, "allow"),
Token::Default => write!(f, "default"),
Token::CatchAll => write!(f, "catch-all"),
Token::Stride => write!(f, "stride"),
Token::Num(_) => write!(f, "number"),
Token::Access(_) => write!(f, "access specifier"),
Token::ByteOrder(_) => write!(f, "byte order"),
Token::BaseType(_) => write!(f, "base type"),
Token::Integer(_) => write!(f, "integer type"),
Token::AddressMode(_) => write!(f, "address mode"),
Token::String(_) => write!(f, "string"),
Token::Unexpected(val) => write!(f, "{}", val.escape_debug()),
Token::Error => write!(f, "ERROR"),
}
}
}
impl<'src> Token<'src> {
fn get_human_string(&self) -> Cow<'static, str> {
match self {
Token::DocCommentLine(line) => format!("///{line}").into(),
Token::Ident(ident) => format!("#{ident}").into(),
Token::CurlyOpen => "{".into(),
Token::CurlyClose => "}".into(),
Token::BracketOpen => "[".into(),
Token::BracketClose => "]".into(),
Token::Colon => ":".into(),
Token::Underscore => "_".into(),
Token::Comma => ",".into(),
Token::Arrow => "->".into(),
Token::Try => "try".into(),
Token::Star => "*".into(),
Token::As => "as".into(),
Token::Num(n) => n.to_string().into(),
Token::Access(val) => val.to_string().into(),
Token::ByteOrder(val) => val.to_string().into(),
Token::BaseType(val) => val.to_string().into(),
Token::Integer(val) => val.to_string().into(),
Token::AddressMode(val) => val.to_string().into(),
Token::Allow => "allow".into(),
Token::Default => "default".into(),
Token::CatchAll => "catch-all".into(),
Token::Stride => "stride".into(),
Token::Unexpected(raw) => format!("!{raw}").into(),
Token::Error => "UNEXPECTED".into(),
Token::String(val) => format!("\"{val}\"").into(),
}
}
fn get_print_format(&self) -> (bool, bool, i32) {
match self {
Token::DocCommentLine(_) => (true, true, 0),
Token::Comma => (false, true, 0),
Token::CurlyOpen | Token::BracketOpen => (false, true, 1),
Token::CurlyClose | Token::BracketClose => (true, false, -1),
_ => (false, false, 0),
}
}
pub fn formatted_print<'a, I: Iterator<Item = &'a Token<'src>>>(
stream: &mut impl std::fmt::Write,
tokens: I,
) -> Result<(), std::fmt::Error>
where
'src: 'a,
{
let mut indent = 0i32;
for token in tokens {
let (newline_before, newline_after, indent_change) = token.get_print_format();
indent += indent_change;
if newline_before {
write!(
stream,
"\n{:width$}",
"",
width = indent.max(0) as usize * 4
)?;
}
write!(stream, "{} ", token.get_human_string())?;
if newline_after {
write!(
stream,
"\n{:width$}",
"",
width = indent.max(0) as usize * 4
)?;
}
}
Ok(())
}
}