mod cursor;
mod lexer;
mod token;
use wolf_diag::{Diagnostic, codes};
use wolf_span::{FileId, Span};
pub use token::{Comment, Token, TokenKind};
#[derive(Debug, Default)]
pub struct Lexed {
pub tokens: Vec<Token>,
pub comments: Vec<Comment>,
pub diagnostics: Vec<Diagnostic>,
}
pub fn lex(file: FileId, text: &str) -> Lexed {
lexer::Lexer::new(file, text).run()
}
#[derive(Debug)]
pub struct InvalidUtf8 {
pub lossy: String,
pub offset: usize,
}
impl InvalidUtf8 {
pub fn diagnostic(&self, file: FileId) -> Diagnostic {
let start = u32::try_from(self.offset).unwrap_or(u32::MAX);
let end = start.saturating_add(3);
Diagnostic::new(codes::E0101, "file is not valid UTF-8")
.primary(Span::new(file, start, end), "invalid byte sequence here")
.note("Wolfpack source files must be encoded in UTF-8")
}
}
pub fn decode(bytes: Vec<u8>) -> Result<String, InvalidUtf8> {
match String::from_utf8(bytes) {
Ok(text) => Ok(text),
Err(error) => {
let offset = error.utf8_error().valid_up_to();
let lossy = String::from_utf8_lossy(error.as_bytes()).into_owned();
Err(InvalidUtf8 { lossy, offset })
}
}
}
pub fn dump(text: &str, tokens: &[Token]) -> String {
let mut out = String::new();
for token in tokens {
let source = text.get(token.span.range()).unwrap_or("");
let kind = match &token.kind {
TokenKind::Ident => format!("Ident {source:?}"),
TokenKind::Int(value) => format!("Int {value}"),
TokenKind::Float(value) => format!("Float {value:?}"),
TokenKind::Str(value) => format!("Str {value:?}"),
TokenKind::DocOuter(value) => format!("DocOuter {value:?}"),
TokenKind::DocInner(value) => format!("DocInner {value:?}"),
TokenKind::Error => format!("Error {source:?}"),
other => match other.spelling() {
Some(spelling) => spelling.to_string(),
None => format!("{other:?}"),
},
};
out.push_str(&format!(
"{}..{} {}\n",
token.span.start, token.span.end, kind
));
}
out
}