add error reporting stuffs

This commit is contained in:
Schrottkatze 2024-01-07 14:26:42 +01:00
parent 970ae0e449
commit 2d007c977c
6 changed files with 223 additions and 22 deletions

17
Cargo.lock generated
View file

@ -171,6 +171,16 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.0" version = "1.0.0"
@ -1112,6 +1122,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.1" version = "0.2.1"
@ -1145,6 +1161,7 @@ name = "webthing"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"codespan-reporting",
"cosmic-text", "cosmic-text",
"env_logger", "env_logger",
"font-kit", "font-kit",

View file

@ -14,3 +14,4 @@ font-kit = { version = "0.11", features = [ "loader-freetype" ]}
simple_logger = "4.3.3" simple_logger = "4.3.3"
logos = "0.13.0" logos = "0.13.0"
cosmic-text = "0.10.0" cosmic-text = "0.10.0"
codespan-reporting = "0.11.1"

View file

@ -15,3 +15,4 @@ more text
(heading level=2 "heading level 2 too") (heading level=2 "heading level 2 too")
(heading level=3 "heading level 3 too") (heading level=3 "heading level 3 too")

159
src/error.rs Normal file
View file

@ -0,0 +1,159 @@
use std::{collections::HashMap, ops::Range, process};
use codespan_reporting::{
diagnostic::{Diagnostic, Label, Severity},
files::SimpleFiles,
term::{
self,
termcolor::{ColorChoice, StandardStream},
},
};
pub type Span = Range<usize>;
type FileId = usize;
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub struct FileIdent<'a> {
id: FileId,
name: &'a str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DiagnosticKind {
InvalidToken,
}
pub enum DiagCriticality {
/// Bugs in this program, unreachable code being reached, etc.
Bug,
// TODO: Better names
/// Critical errors that cannot be recovered from, prompt immediate exit
CriticalImmediate,
/// Critical, this program must exit after this processing step, but can continue and may find other diagnostics
CriticalFinishStep,
/// A warning, about to be deprecated syntax etc.
Warn,
/// Information/advice
Info,
Help,
}
impl DiagCriticality {
fn mk_diag_base(&self) -> Diagnostic<FileId> {
Diagnostic::new(match self {
DiagCriticality::Bug => Severity::Bug,
DiagCriticality::CriticalImmediate => Severity::Error,
DiagCriticality::CriticalFinishStep => Severity::Error,
DiagCriticality::Warn => Severity::Warning,
DiagCriticality::Info => Severity::Note,
DiagCriticality::Help => Severity::Help,
})
}
}
impl DiagnosticKind {
pub fn criticality(&self) -> DiagCriticality {
match self {
DiagnosticKind::InvalidToken => DiagCriticality::CriticalFinishStep,
}
}
fn into_diag(self, locs: &[Span], file_id: &FileIdent) -> Diagnostic<FileId> {
self.criticality()
.mk_diag_base()
.with_labels(
locs.iter()
.map(|span| Label::primary(file_id.id, span.clone()))
.collect(),
)
.with_message(match self {
DiagnosticKind::InvalidToken => {
if locs.len() > 1 {
"invalid tokens"
} else {
"invalid token"
}
}
})
}
}
pub struct DiagnosticReporter<'a> {
files: SimpleFiles<&'a str, &'a str>,
errs: HashMap<FileIdent<'a>, HashMap<DiagnosticKind, Vec<Span>>>,
}
impl<'a> DiagnosticReporter<'a> {
pub fn new() -> Self {
Self {
files: SimpleFiles::new(),
errs: HashMap::new(),
}
}
pub fn register_file(&mut self, name: &'a str, content: &'a str) -> FileIdent<'a> {
FileIdent {
id: self.files.add(name, content),
name,
}
}
pub fn report_diagnostic(&mut self, file_id: FileIdent<'a>, loc: Span, kind: DiagnosticKind) {
self.errs
.entry(file_id)
.or_insert_with(|| HashMap::with_capacity(1));
if !self.errs[&file_id].contains_key(&kind) {
self.errs.get_mut(&file_id).unwrap().insert(kind, vec![loc]);
} else {
self.errs
.get_mut(&file_id)
.unwrap()
.get_mut(&kind)
.unwrap()
.push(loc)
}
match kind.criticality() {
DiagCriticality::Bug | DiagCriticality::CriticalImmediate => self.finish_and_exit(),
DiagCriticality::CriticalFinishStep
| DiagCriticality::Warn
| DiagCriticality::Info
| DiagCriticality::Help => {}
}
}
pub fn fail_step_if_failed(&self) {
for (_, diags) in self.errs.iter() {
for kind in diags.keys() {
if matches!(
kind.criticality(),
DiagCriticality::Bug
| DiagCriticality::CriticalImmediate
| DiagCriticality::CriticalFinishStep
) {
self.finish_and_exit()
}
}
}
}
fn finish_and_exit(&self) -> ! {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
for (file_id, diags) in self.errs.iter() {
for (kind, locs) in diags {
let writer = &mut writer.lock();
term::emit(writer, &config, &self.files, &kind.into_diag(locs, file_id))
.expect("can't even report errors :c");
}
}
process::exit(1);
}
}

View file

@ -11,6 +11,9 @@ use parse::parse_the_shit_out_of_this_but_manually;
use render::render_text; use render::render_text;
use crate::error::DiagnosticReporter;
mod error;
mod parse; mod parse;
mod render; mod render;
@ -34,9 +37,17 @@ fn main() {
args.x, args.y args.x, args.y
); );
let txt = fs::read_to_string(args.file).unwrap(); let mut reporter = DiagnosticReporter::new();
dbg!(parse_the_shit_out_of_this_but_manually(&txt)); let txt = fs::read_to_string(args.file.clone()).unwrap();
let fid = reporter.register_file(args.file.file_name().unwrap().to_str().unwrap(), &txt);
dbg!(parse_the_shit_out_of_this_but_manually(
&txt,
&mut reporter,
&fid
));
let mut dt = DrawTarget::new(args.x.into(), args.y.into()); let mut dt = DrawTarget::new(args.x.into(), args.y.into());

View file

@ -2,9 +2,11 @@ use std::collections::HashMap;
use logos::{Lexer, Logos}; use logos::{Lexer, Logos};
use crate::error::{DiagnosticKind, DiagnosticReporter, FileIdent};
#[derive(Logos, Debug, PartialEq)] #[derive(Logos, Debug, PartialEq)]
pub enum Token { pub enum Token {
#[regex("[a-zA-Z\\d\\w]+", |lex| lex.slice().to_owned())] #[regex("[a-zA-Z\\d\\s]+", |lex| lex.slice().to_owned())]
Text(String), Text(String),
#[token("(")] #[token("(")]
ParenOpen, ParenOpen,
@ -40,35 +42,45 @@ pub enum LanguageStructureThingy {
}, },
} }
pub fn parse_the_shit_out_of_this_but_manually(text: &str) -> Vec<LanguageStructureThingy> { pub fn parse_the_shit_out_of_this_but_manually<'a>(
let mut lex = Token::lexer(text); text: &str,
reporter: &mut DiagnosticReporter<'a>,
file_id: &'a FileIdent,
) -> Vec<LanguageStructureThingy> {
let mut lex = Token::lexer(text).spanned();
let mut r = Vec::new(); let mut r = Vec::new();
loop { while let Some((res, loc)) = lex.next() {
match lex.next() { match res {
Some(Ok(Token::Text(s))) => r.push(LanguageStructureThingy::Text(s)), Ok(Token::Text(s)) => r.push(LanguageStructureThingy::Text(s)),
Some(Ok(Token::Newline)) => r.push(LanguageStructureThingy::Text("\n".to_owned())), Ok(Token::Newline) => r.push(LanguageStructureThingy::Text("\n".to_owned())),
Some(Ok(Token::ParenOpen)) => hehe_sexpression_funy(&mut r, &mut lex), Ok(Token::ParenOpen) => hehe_sexpression_funy(&mut r, &mut lex),
Some(Ok(Token::ParenClose)) => todo!(), Ok(Token::String(_)) => todo!(),
Some(Ok(Token::String(_))) => todo!(), Ok(Token::Equals) => todo!(),
Some(Ok(Token::Equals)) => todo!(), Ok(Token::Asterisk) => todo!(),
Some(Ok(Token::Asterisk)) => todo!(), Ok(Token::Underscore) => todo!(),
Some(Ok(Token::Underscore)) => todo!(), Ok(Token::Backslash) => todo!(),
Some(Ok(Token::Backslash)) => todo!(), Ok(Token::WavyThing) => todo!(),
Some(Ok(Token::WavyThing)) => todo!(), Ok(Token::Sparkles) => todo!(),
Some(Ok(Token::Sparkles)) => todo!(), Ok(Token::HeadingLevelIndicator) => todo!(),
Some(Ok(Token::HeadingLevelIndicator)) => todo!(), Err(()) | Ok(Token::ParenClose) => {
Some(Err(e)) => panic!("mauuu~ :(, e: {e:?}"), reporter.report_diagnostic(*file_id, loc, DiagnosticKind::InvalidToken)
None => break, }
} }
} }
reporter.fail_step_if_failed();
// match lex.next() {
// Some(Err(e)) => panic!("mauuu~ :(, e: {e:?}"),
// None => break,
// }
r r
} }
fn hehe_sexpression_funy(r: &mut Vec<LanguageStructureThingy>, lex: &mut Lexer<'_, Token>) { fn hehe_sexpression_funy(r: &mut Vec<LanguageStructureThingy>, lex: &mut Lexer<'_, Token>) {
if let Some(Ok(Token::Text(s))) = lex.next() { if let Some(Ok(Token::Text(s))) = lex.next() {
let strs = s.trim_start().split_whitespace().collect::<Vec<&str>>(); let strs = s.split_whitespace().collect::<Vec<&str>>();
let name = strs[0].to_owned(); let name = strs[0].to_owned();
let mut attrs = HashMap::new(); let mut attrs = HashMap::new();