1mod cursor;
8mod lexer;
9mod token;
10
11use wolf_diag::{Diagnostic, codes};
12use wolf_span::{FileId, Span};
13
14pub use token::{Comment, FPart, 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, _) => format!("{{{}}}", tokens.len().saturating_sub(1)),
82 })
83 .collect();
84 format!("FStr [{}]", parts.join(", "))
85 }
86 TokenKind::DocOuter(value) => format!("DocOuter {value:?}"),
87 TokenKind::DocInner(value) => format!("DocInner {value:?}"),
88 TokenKind::Error => format!("Error {source:?}"),
89 other => match other.spelling() {
90 Some(spelling) => spelling.to_string(),
91 None => format!("{other:?}"),
92 },
93 };
94 out.push_str(&format!(
95 "{}..{} {}\n",
96 token.span.start, token.span.end, kind
97 ));
98 }
99 out
100}