1mod cursor;
8mod lexer;
9mod token;
10
11use wolf_diag::{Diagnostic, codes};
12use wolf_span::{FileId, Span};
13
14pub use token::{Align, Comment, FPart, FormatSpec, Token, TokenKind};
15
16#[derive(Debug, Default)]
18pub struct Lexed {
19 pub tokens: Vec<Token>,
21 pub comments: Vec<Comment>,
23 pub diagnostics: Vec<Diagnostic>,
25}
26
27pub fn lex(file: FileId, text: &str) -> Lexed {
29 lexer::Lexer::new(file, text).run()
30}
31
32#[derive(Debug)]
34pub struct InvalidUtf8 {
35 pub lossy: String,
37 pub offset: usize,
39}
40
41impl InvalidUtf8 {
42 pub fn diagnostic(&self, file: FileId) -> Diagnostic {
45 let start = u32::try_from(self.offset).unwrap_or(u32::MAX);
46 let end = start.saturating_add(3);
48 Diagnostic::new(codes::E0101, "file is not valid UTF-8")
49 .primary(Span::new(file, start, end), "invalid byte sequence here")
50 .note("Wolfpack source files must be encoded in UTF-8")
51 }
52}
53
54pub fn decode(bytes: Vec<u8>) -> Result<String, InvalidUtf8> {
56 match String::from_utf8(bytes) {
57 Ok(text) => Ok(text),
58 Err(error) => {
59 let offset = error.utf8_error().valid_up_to();
60 let lossy = String::from_utf8_lossy(error.as_bytes()).into_owned();
61 Err(InvalidUtf8 { lossy, offset })
62 }
63 }
64}
65
66pub fn dump(text: &str, tokens: &[Token]) -> String {
68 let mut out = String::new();
69 for token in tokens {
70 let source = text.get(token.span.range()).unwrap_or("");
71 let kind = match &token.kind {
72 TokenKind::Ident => format!("Ident {source:?}"),
73 TokenKind::Int(value) => format!("Int {value}"),
74 TokenKind::Float(value) => format!("Float {value:?}"),
75 TokenKind::Str(value) => format!("Str {value:?}"),
76 TokenKind::FStr(parts) => {
77 let parts: Vec<String> = parts
78 .iter()
79 .map(|part| match part {
80 FPart::Text(text) => format!("{text:?}"),
81 FPart::Expr(tokens, _, None) => {
82 format!("{{{}}}", tokens.len().saturating_sub(1))
83 }
84 FPart::Expr(tokens, _, Some(_)) => {
85 format!("{{{}:spec}}", tokens.len().saturating_sub(1))
86 }
87 })
88 .collect();
89 format!("FStr [{}]", parts.join(", "))
90 }
91 TokenKind::DocOuter(value) => format!("DocOuter {value:?}"),
92 TokenKind::DocInner(value) => format!("DocInner {value:?}"),
93 TokenKind::Error => format!("Error {source:?}"),
94 other => match other.spelling() {
95 Some(spelling) => spelling.to_string(),
96 None => format!("{other:?}"),
97 },
98 };
99 out.push_str(&format!(
100 "{}..{} {}\n",
101 token.span.start, token.span.end, kind
102 ));
103 }
104 out
105}