add error reporting stuffs
This commit is contained in:
parent
970ae0e449
commit
2d007c977c
6 changed files with 223 additions and 22 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -171,6 +171,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
|
@ -1112,6 +1122,12 @@ version = "1.10.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
|
@ -1145,6 +1161,7 @@ name = "webthing"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"codespan-reporting",
|
||||
"cosmic-text",
|
||||
"env_logger",
|
||||
"font-kit",
|
||||
|
|
|
@ -14,3 +14,4 @@ font-kit = { version = "0.11", features = [ "loader-freetype" ]}
|
|||
simple_logger = "4.3.3"
|
||||
logos = "0.13.0"
|
||||
cosmic-text = "0.10.0"
|
||||
codespan-reporting = "0.11.1"
|
||||
|
|
|
@ -15,3 +15,4 @@ more text
|
|||
(heading level=2 "heading level 2 too")
|
||||
|
||||
(heading level=3 "heading level 3 too")
|
||||
|
||||
|
|
159
src/error.rs
Normal file
159
src/error.rs
Normal 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);
|
||||
}
|
||||
}
|
15
src/main.rs
15
src/main.rs
|
@ -11,6 +11,9 @@ use parse::parse_the_shit_out_of_this_but_manually;
|
|||
|
||||
use render::render_text;
|
||||
|
||||
use crate::error::DiagnosticReporter;
|
||||
|
||||
mod error;
|
||||
mod parse;
|
||||
mod render;
|
||||
|
||||
|
@ -34,9 +37,17 @@ fn main() {
|
|||
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());
|
||||
|
||||
|
|
52
src/parse.rs
52
src/parse.rs
|
@ -2,9 +2,11 @@ use std::collections::HashMap;
|
|||
|
||||
use logos::{Lexer, Logos};
|
||||
|
||||
use crate::error::{DiagnosticKind, DiagnosticReporter, FileIdent};
|
||||
|
||||
#[derive(Logos, Debug, PartialEq)]
|
||||
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),
|
||||
#[token("(")]
|
||||
ParenOpen,
|
||||
|
@ -40,35 +42,45 @@ pub enum LanguageStructureThingy {
|
|||
},
|
||||
}
|
||||
|
||||
pub fn parse_the_shit_out_of_this_but_manually(text: &str) -> Vec<LanguageStructureThingy> {
|
||||
let mut lex = Token::lexer(text);
|
||||
pub fn parse_the_shit_out_of_this_but_manually<'a>(
|
||||
text: &str,
|
||||
reporter: &mut DiagnosticReporter<'a>,
|
||||
file_id: &'a FileIdent,
|
||||
) -> Vec<LanguageStructureThingy> {
|
||||
let mut lex = Token::lexer(text).spanned();
|
||||
let mut r = Vec::new();
|
||||
|
||||
loop {
|
||||
match lex.next() {
|
||||
Some(Ok(Token::Text(s))) => r.push(LanguageStructureThingy::Text(s)),
|
||||
Some(Ok(Token::Newline)) => r.push(LanguageStructureThingy::Text("\n".to_owned())),
|
||||
Some(Ok(Token::ParenOpen)) => hehe_sexpression_funy(&mut r, &mut lex),
|
||||
Some(Ok(Token::ParenClose)) => todo!(),
|
||||
Some(Ok(Token::String(_))) => todo!(),
|
||||
Some(Ok(Token::Equals)) => todo!(),
|
||||
Some(Ok(Token::Asterisk)) => todo!(),
|
||||
Some(Ok(Token::Underscore)) => todo!(),
|
||||
Some(Ok(Token::Backslash)) => todo!(),
|
||||
Some(Ok(Token::WavyThing)) => todo!(),
|
||||
Some(Ok(Token::Sparkles)) => todo!(),
|
||||
Some(Ok(Token::HeadingLevelIndicator)) => todo!(),
|
||||
Some(Err(e)) => panic!("mauuu~ :(, e: {e:?}"),
|
||||
None => break,
|
||||
while let Some((res, loc)) = lex.next() {
|
||||
match res {
|
||||
Ok(Token::Text(s)) => r.push(LanguageStructureThingy::Text(s)),
|
||||
Ok(Token::Newline) => r.push(LanguageStructureThingy::Text("\n".to_owned())),
|
||||
Ok(Token::ParenOpen) => hehe_sexpression_funy(&mut r, &mut lex),
|
||||
Ok(Token::String(_)) => todo!(),
|
||||
Ok(Token::Equals) => todo!(),
|
||||
Ok(Token::Asterisk) => todo!(),
|
||||
Ok(Token::Underscore) => todo!(),
|
||||
Ok(Token::Backslash) => todo!(),
|
||||
Ok(Token::WavyThing) => todo!(),
|
||||
Ok(Token::Sparkles) => todo!(),
|
||||
Ok(Token::HeadingLevelIndicator) => todo!(),
|
||||
Err(()) | Ok(Token::ParenClose) => {
|
||||
reporter.report_diagnostic(*file_id, loc, DiagnosticKind::InvalidToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reporter.fail_step_if_failed();
|
||||
// match lex.next() {
|
||||
// Some(Err(e)) => panic!("mauuu~ :(, e: {e:?}"),
|
||||
// None => break,
|
||||
// }
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
fn hehe_sexpression_funy(r: &mut Vec<LanguageStructureThingy>, lex: &mut Lexer<'_, Token>) {
|
||||
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 mut attrs = HashMap::new();
|
||||
|
|
Loading…
Reference in a new issue