From 76ca07b4ddff0db5a0d3c9a29796b3356147cb68 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 2 Jul 2021 17:31:55 +0200 Subject: [PATCH] never sseen such clean error handling --- Cargo.lock | 57 ++++++++++++++++++++++--- Cargo.toml | 4 +- rust-toolchain | 1 + rustfmt.toml | 14 ++++++ src/config.rs | 68 ++++++++++------------------- src/error.rs | 52 +++++++++++++++++++---- src/main.rs | 104 ++++++++++++++++++++++++++++++--------------- src/parser.lalrpop | 2 +- 8 files changed, 204 insertions(+), 98 deletions(-) create mode 100644 rust-toolchain create mode 100644 rustfmt.toml diff --git a/Cargo.lock b/Cargo.lock index 3cde16d..3aaeee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "anyhow" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" - [[package]] name = "arrayref" version = "0.3.6" @@ -280,12 +274,12 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" name = "nomwut" version = "0.1.0" dependencies = [ - "anyhow", "itertools", "lalrpop", "lalrpop-util", "maplit", "regex", + "thiserror", ] [[package]] @@ -319,6 +313,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "redox_syscall" version = "0.1.57" @@ -383,6 +395,17 @@ dependencies = [ "precomputed-hash", ] +[[package]] +name = "syn" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "term" version = "0.5.2" @@ -394,6 +417,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "thiserror" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tiny-keccak" version = "2.0.2" diff --git a/Cargo.toml b/Cargo.toml index 11018a4..dfcdf09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ build = "build.rs" lalrpop-util = "0.19.5" regex = "1" itertools = "0.10" -anyhow = "1.0" -maplit = "*" +thiserror = "1.0" +maplit = "1.0" [build-dependencies] lalrpop = "0.19.5" diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..bf867e0 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..edce9c8 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,14 @@ +unstable_features = true +fn_single_line = false +max_width = 130 +reorder_impl_items = true +merge_imports = true +normalize_comments = true +use_field_init_shorthand = true +#wrap_comments = true +combine_control_expr = false +condense_wildcard_suffixes = true +format_code_in_doc_comments = true +format_macro_matchers = true +format_strings = true +use_small_heuristics = "Max" diff --git a/src/config.rs b/src/config.rs index cb3127b..9941e54 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,6 @@ use std::{collections::HashMap, iter::FromIterator}; use super::*; use crate::error::*; -use anyhow::*; use itertools::Itertools; use std::collections::LinkedList; @@ -11,11 +10,11 @@ type AttrValue = String; type AttrName = String; pub trait FromExpr: Sized { - fn from_expr(e: Expr) -> Result; + fn from_expr(e: Expr) -> AstResult; } impl FromExpr for Expr { - fn from_expr(e: Expr) -> Result { + fn from_expr(e: Expr) -> AstResult { Ok(e) } } @@ -25,14 +24,14 @@ pub enum DefType { } impl FromExpr for DefType { - fn from_expr(e: Expr) -> Result { - if let Expr::Symbol(_, sym) = e { + fn from_expr(e: Expr) -> AstResult { + if let Expr::Symbol(span, sym) = e { match sym.as_str() { "defwidget" => Ok(DefType::Widget), - _ => Err(AstError::InvalidDefinition), + _ => Err(AstError::InvalidDefinition(Some(span))), } } else { - Err(AstError::UnexpectedNode) + Err(AstError::WrongExprType(Some(e.span()), ExprType::Symbol, e)) } } } @@ -45,26 +44,18 @@ pub struct Definitional { } impl FromExpr for Definitional { - fn from_expr(e: Expr) -> Result { - if let Expr::List(span, list) = e { + fn from_expr(e: Expr) -> AstResult { + spanned!(e.span(), { + let list = e.as_list()?; let mut iter = itertools::put_back(list.into_iter()); - let def_type = DefType::from_expr(iter.next().or_missing()?)?; - let name = iter.next().or_missing()?.as_str()?; + let def_type = DefType::from_expr(iter.next().or_missing(ExprType::Symbol)?)?; + let name = iter.next().or_missing(ExprType::Str)?.as_str()?; let attrs = parse_key_values(&mut iter); - let children = iter - .map(T::from_expr) - .collect::, AstError>>()?; - Ok(Definitional { - def_type, - name, - attrs, - children, - }) - } else { - Err(AstError::UnexpectedNode) - } + let children = iter.map(|x| T::from_expr(x)).collect::>>()?; + Definitional { def_type, name, attrs, children } + }) } } #[derive(Debug, Eq, PartialEq)] @@ -75,27 +66,20 @@ pub struct Element { } impl FromExpr for Element { - fn from_expr(e: Expr) -> Result { - if let Expr::List(span, list) = e { + fn from_expr(e: Expr) -> AstResult { + spanned!(e.span(), { + let list = e.as_list()?; let mut iter = itertools::put_back(list.into_iter()); - let name = iter.next().or_missing()?.as_symbol()?; + let name = iter.next().or_missing(ExprType::Str)?.as_symbol()?; let attrs = parse_key_values(&mut iter); - Ok(Element { - name, - attrs, - children: iter.collect_vec(), - }) - } else { - Err(AstError::UnexpectedNode) - } + Element { name, attrs, children: iter.collect_vec() } + }) } } -fn parse_key_values>( - iter: &mut itertools::PutBack, -) -> HashMap { +fn parse_key_values>(iter: &mut itertools::PutBack) -> HashMap { let mut data = HashMap::new(); loop { match iter.next() { @@ -126,10 +110,7 @@ mod test { fn test() { let parser = parser::ExprParser::new(); assert_eq!( - Element::::from_expr( - parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap() - ) - .unwrap(), + Element::::from_expr(parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap()).unwrap(), Element { name: "box".to_string(), attrs: maplit::hashmap! { @@ -139,10 +120,7 @@ mod test { }, children: vec![ Expr::Symbol(Span(23, 26), "foo".to_string()), - Expr::List( - Span(27, 32), - vec![Expr::Symbol(Span(28, 31), "bar".to_string())] - ), + Expr::List(Span(27, 32), vec![Expr::Symbol(Span(28, 31), "bar".to_string())]), ], } ); diff --git a/src/error.rs b/src/error.rs index 23948a0..e472763 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,18 +1,52 @@ -use crate::Expr; +use crate::{Expr, ExprType, Span}; +use thiserror::Error; -#[derive(Debug, PartialEq, Eq)] +pub type AstResult = Result; + +#[derive(Debug, PartialEq, Eq, Error)] pub enum AstError { - UnexpectedNode, - InvalidDefinition, - WrongExprType(Expr), - MissingNode, + #[error("Definition invalid")] + InvalidDefinition(Option), + #[error("Expected a {1}, but got nothing")] + MissingNode(Option, ExprType), + #[error("Wrong type of expression: Expected {1} but got {2}")] + WrongExprType(Option, ExprType, Expr), +} + +pub fn spanned(span: Span, err: impl Into) -> AstError { + use AstError::*; + match err.into() { + AstError::InvalidDefinition(None) => AstError::InvalidDefinition(Some(span)), + AstError::MissingNode(None, x) => AstError::MissingNode(Some(span), x), + AstError::WrongExprType(None, x, y) => AstError::WrongExprType(Some(span), x, y), + x => x, + } } pub trait OptionAstErrorExt { - fn or_missing(self) -> Result; + fn or_missing(self, t: ExprType) -> Result; } impl OptionAstErrorExt for Option { - fn or_missing(self) -> Result { - self.ok_or(AstError::MissingNode) + fn or_missing(self, t: ExprType) -> Result { + self.ok_or(AstError::MissingNode(None, t)) } } + +pub trait AstResultExt { + fn at(self, span: Span) -> Result; +} + +impl> AstResultExt for Result { + fn at(self, span: Span) -> Result { + self.map_err(|err| spanned(span, err)) + } +} + +#[macro_export] +macro_rules! spanned { + ($span:expr, $block:expr) => {{ + let span = $span; + let result: Result<_, AstError> = try { $block }; + result.at(span) + }}; +} diff --git a/src/main.rs b/src/main.rs index 7bc52ee..cf327f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,18 @@ #![allow(unused_imports)] #![allow(unused)] +#![feature(try_blocks)] mod config; mod error; use error::AstError; -use std::ops::Deref; +use std::{fmt::Display, ops::Deref}; use itertools::Itertools; use lalrpop_util::lalrpop_mod; -//mod lexer; +// mod lexer; lalrpop_mod!(pub parser); @@ -24,6 +25,23 @@ impl std::fmt::Display for Span { } } +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum ExprType { + List, + Table, + Keyword, + Symbol, + Str, + Number, + Comment, +} + +impl Display for ExprType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub enum Expr { List(Span, Vec), @@ -32,28 +50,50 @@ pub enum Expr { Symbol(Span, String), Str(Span, String), Number(Span, i32), - Comment, + Comment(Span), +} + +impl From for ExprType { + fn from(x: Expr) -> Self { + match x { + Expr::List(..) => ExprType::List, + Expr::Table(..) => ExprType::Table, + Expr::Keyword(..) => ExprType::Keyword, + Expr::Symbol(..) => ExprType::Symbol, + Expr::Str(..) => ExprType::Str, + Expr::Number(..) => ExprType::Number, + Expr::Comment(_) => ExprType::Number, + } + } } macro_rules! as_func { - ($name:ident<$t:ty> = $p:pat => $value:expr) => { + ($exprtype:expr, $name:ident < $t:ty > = $p:pat => $value:expr) => { fn $name(self) -> Result<$t, AstError> { match self { $p => Ok($value), - x => Err(AstError::WrongExprType(x)), + x => Err(AstError::WrongExprType(Some(x.span()), $exprtype, x)), } } }; } impl Expr { - as_func!(as_str = Expr::Str(_, x) => x); - as_func!(as_symbol = Expr::Symbol(_, x) => x); + as_func!(ExprType::Str, as_str = Expr::Str(_, x) => x); - fn is_keyword(&self) -> bool { + as_func!(ExprType::Symbol, as_symbol = Expr::Symbol(_, x) => x); + + as_func!(ExprType::List, as_list> = Expr::List(_, x) => x); + + pub fn span(&self) -> Span { match self { - Expr::Keyword(_, _) => true, - _ => false, + Expr::List(span, _) => *span, + Expr::Table(span, _) => *span, + Expr::Keyword(span, _) => *span, + Expr::Symbol(span, _) => *span, + Expr::Str(span, _) => *span, + Expr::Number(span, _) => *span, + Expr::Comment(span) => *span, } } } @@ -64,15 +104,11 @@ impl std::fmt::Display for Expr { match self { Number(_, x) => write!(f, "{}", x), List(_, x) => write!(f, "({})", x.iter().map(|e| format!("{}", e)).join(" ")), - Table(_, x) => write!( - f, - "{{{}}}", - x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ") - ), + Table(_, x) => write!(f, "{{{}}}", x.iter().map(|(k, v)| format!("{} {}", k, v)).join(" ")), Keyword(_, x) => write!(f, "{}", x), Symbol(_, x) => write!(f, "{}", x), Str(_, x) => write!(f, "{}", x), - Comment => write!(f, ""), + Comment(_) => write!(f, ""), } } } @@ -93,28 +129,28 @@ macro_rules! test_p { #[test] fn test() { - //test_p!("1"); - //test_p!("(12)"); - //test_p!("(1 2)"); - //test_p!("(1 :foo 1)"); - //test_p!("(:foo 1)"); - //test_p!("(:foo->: 1)"); - //test_p!("(foo 1)"); - //test_p!("(lol😄 1)"); + // test_p!("1"); + // test_p!("(12)"); + // test_p!("(1 2)"); + // test_p!("(1 :foo 1)"); + // test_p!("(:foo 1)"); + // test_p!("(:foo->: 1)"); + // test_p!("(foo 1)"); + // test_p!("(lol😄 1)"); - //test_p!(r#"(test "hi")"#); - //test_p!(r#"(test "h\"i")"#); - //test_p!(r#"(test " hi ")"#); + // test_p!(r#"(test "hi")"#); + // test_p!(r#"(test "h\"i")"#); + // test_p!(r#"(test " hi ")"#); - //test_p!("(+ (1 2 (* 2 5)))"); + // test_p!("(+ (1 2 (* 2 5)))"); - //test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); + // test_p!(r#"{:key value 12 "hi" (test) (1 2 3)}"#); - //test_p!(r#"; test"#); - //test_p!( - //r#"(f arg ; test - //arg2)"# + // test_p!(r#"; test"#); + // test_p!( + // r#"(f arg ; test + // arg2)"# //); - //println!("\n\n\n\n\n\n"); + // println!("\n\n\n\n\n\n"); } diff --git a/src/parser.lalrpop b/src/parser.lalrpop index 050b13b..99e511c 100644 --- a/src/parser.lalrpop +++ b/src/parser.lalrpop @@ -15,7 +15,7 @@ pub Expr: Expr = { => x, => Expr::Str(Span(l, r), x), => Expr::Number(Span(l, r), x), - Comment => Expr::Comment, + Comment => Expr::Comment(Span(l, r)), }; Keyword: Expr = => Expr::Keyword(Span(l, r), x.to_string());