From 84448af7149c083812ea81f861fd4c67b79b0b3d Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 3 Apr 2024 17:00:20 +0200 Subject: [PATCH] lang: funky basic error reporting stuff --- Cargo.lock | 1 + crates/lang/Cargo.toml | 3 +- crates/lang/src/err_reporting.rs | 87 ++++++++++++++++++++++++++++++++ crates/lang/src/lib.rs | 4 +- crates/lang/src/main.rs | 21 ++++++-- crates/lang/src/parser.rs | 15 ++++-- crates/lang/src/parser/tests.rs | 4 +- testfiles/test.owo | 5 +- 8 files changed, 122 insertions(+), 18 deletions(-) create mode 100644 crates/lang/src/err_reporting.rs diff --git a/Cargo.lock b/Cargo.lock index bc8692d..e762ead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -503,6 +503,7 @@ dependencies = [ name = "lang" version = "0.1.0" dependencies = [ + "ariadne", "chumsky", "clap", "indexmap", diff --git a/crates/lang/Cargo.toml b/crates/lang/Cargo.toml index 8126ffa..1cd8654 100644 --- a/crates/lang/Cargo.toml +++ b/crates/lang/Cargo.toml @@ -7,10 +7,11 @@ edition = "2021" [dependencies] logos = "0.14" -chumsky = "1.0.0-alpha.6" +chumsky = {version= "1.0.0-alpha.6", features=["label"]} petgraph = { workspace = true} indexmap = "2.2.6" clap = { version = "4", features = ["derive"] } +ariadne = "0.4.0" [lints] workspace = true diff --git a/crates/lang/src/err_reporting.rs b/crates/lang/src/err_reporting.rs new file mode 100644 index 0000000..ee66b7a --- /dev/null +++ b/crates/lang/src/err_reporting.rs @@ -0,0 +1,87 @@ +use std::{collections::HashMap, fs}; + +use ariadne::{sources, Label, Report, Source}; +use chumsky::{ + error::{self, Rich}, + ParseResult, +}; +use indexmap::IndexMap; + +use crate::{ + parser::{ast::File, Span}, + tokens::Token, +}; + +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum Stage { + Lex, + Parse, +} + +impl Stage { + fn variants() -> [Stage; 2] { + [Stage::Lex, Stage::Parse] + } +} + +pub struct ErrorCollector<'filename, 'tokens, 'src> { + files: HashMap<&'filename str, &'src str>, + raw_errors: IndexMap<(&'filename str, Stage), Vec, Span>>>, +} + +impl<'filename, 'tokens, 'src> ErrorCollector<'filename, 'tokens, 'src> { + pub fn new(files: Vec<(&'filename str, &'src str)>) -> Self { + Self { + files: HashMap::from_iter(files.clone()), + raw_errors: files + .iter() + .flat_map(|(name, _)| Stage::variants().map(|s| (name, s))) + .map(|(name, stage)| ((*name, stage), Vec::new())) + .collect(), + } + } + + pub fn insert_many( + &mut self, + file: &'filename str, + curr_stage: Stage, + mut errs: Vec, Span>>, + ) { + let err_vec = self + .raw_errors + .get_mut(&(file, curr_stage)) + .expect("filename should exist"); + err_vec.append(&mut errs); + } + + pub fn analyze_and_report(self) { + let ErrorCollector { files, raw_errors } = self; + todo!() + } + + pub fn report_raw(self) { + let ErrorCollector { files, raw_errors } = self; + + for ((file, stage), errs) in raw_errors.into_iter() { + for err in errs { + Report::build(ariadne::ReportKind::Error, file, err.span().start) + .with_message(format!("error at stage {stage:?}, {:?}", err.reason())) + .with_label( + Label::new((file, err.span().into_range())).with_message(format!( + "found: {:?}", + err.found().expect("errors should have a reason") + )), + ) + .with_help(format!( + "expected: {:?}", + err.expected().collect::>() + )) + .finish() + .print((file, Source::from(files[file]))); + } + } + } +} + +#[derive(Debug, PartialEq, Eq)] +struct Loc<'filename>(&'filename str, Span); diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index 6878991..94a1430 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -1,5 +1,3 @@ +pub mod err_reporting; pub mod parser; pub mod tokens; -pub mod err_reporting { - pub struct GlobalReporter {} -} diff --git a/crates/lang/src/main.rs b/crates/lang/src/main.rs index 3fef5ac..3e70973 100644 --- a/crates/lang/src/main.rs +++ b/crates/lang/src/main.rs @@ -1,17 +1,32 @@ use std::{fs, path::PathBuf}; use clap::Parser; -use lang::parser::parse; +use lang::{err_reporting::ErrorCollector, parser::parse}; #[derive(Parser)] struct Args { file: PathBuf, } +#[allow(clippy::unwrap_used)] fn main() { let args = Args::parse(); - let f = fs::read_to_string(args.file).expect("failed to read file"); + let n = args.file.clone(); + let f = fs::read_to_string(n.clone()).expect("failed to read file"); + let mut err_collector = ErrorCollector::new(vec![(n.to_str().unwrap(), &f)]); println!("file: {f}\n"); - println!("parsed: {:?}", parse(&f)) + let parse_res = parse(&f); + err_collector.insert_many( + args.file.to_str().unwrap(), + lang::err_reporting::Stage::Parse, + parse_res + .errors() + .into_iter() + .map(|e| e.to_owned()) + .collect::>(), + ); + + err_collector.report_raw(); + println!("res: {:?}", parse_res); } diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index 9641dbb..aae1fbb 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -21,8 +21,8 @@ pub mod ast; mod tests; use self::ast::{Expr, File}; -type Span = SimpleSpan; -type Spanned = (T, Span); +pub type Span = SimpleSpan; +pub type Spanned = (T, Span); pub fn parse<'src>(src: &'src str) -> ParseResult, Rich<'_, Token<'_>>> { let toks: Vec<_> = Token::lexer(src) @@ -43,24 +43,29 @@ fn expr_parser<'tokens, 'src: 'tokens, I: ValueInput<'tokens, Token = Token<'src Token::VarIdent(name) => (Expr::Var as fn(_) -> _, name), Token::InputIdent(name) => (Expr::InputVar as fn(_) -> _, name) } - .map_with(|(item_type, name), extra| item_type((name, extra.span()))); + .map_with(|(item_type, name), extra| item_type((name, extra.span()))) + .labelled("variable"); let attrset = word .map_with(|n, e| (n, e.span())) + .labelled("attr name") .then_ignore(just(Token::Colon)) .then(expr) + .labelled("attr body") .separated_by(just(Token::Comma)) .collect::>() .map(IndexMap::from_iter) .delimited_by(just(Token::BracketOpen), just(Token::BracketClose)) - .map_with(|v, e| (v, e.span())); + .map_with(|v, e| (v, e.span())) + .labelled("attrset"); let node = word .map_with(|v, e| (v, e.span())) .then(attrset.clone().or_not()) .map(|(name, params)| Expr::Node(name, params)) .or(var) - .or(attrset.map(Expr::AttrSet)); + .or(attrset.map(Expr::AttrSet)) + .labelled("node"); let pipeline = node .clone() diff --git a/crates/lang/src/parser/tests.rs b/crates/lang/src/parser/tests.rs index 308b09b..f8fbdc0 100644 --- a/crates/lang/src/parser/tests.rs +++ b/crates/lang/src/parser/tests.rs @@ -39,7 +39,7 @@ fn test_parse_node_with_params() { } fn test_parse_multiple_top_level_complex() { - const INPUT: &str = r#"def main = meow + const INPUT: &str = r"def main = meow | uwu [ foo: @bar , hello: world @| test [ more: params ] | yay @@ -52,7 +52,7 @@ def test = meow [ hello: $foo , world: @bar ]; -"#; +"; assert_eq!( parse(INPUT).unwrap(), File { diff --git a/testfiles/test.owo b/testfiles/test.owo index d9d05da..d35cae5 100644 --- a/testfiles/test.owo +++ b/testfiles/test.owo @@ -1,4 +1 @@ -def main = meow | test; -def test = meow [ hello: $foo, world: @bar]; - -def uwu = owo; +meow gay | uwu