never sseen such clean error handling

This commit is contained in:
elkowar 2021-07-02 17:31:55 +02:00
parent a06927e356
commit 76ca07b4dd
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
8 changed files with 204 additions and 98 deletions

57
Cargo.lock generated
View file

@ -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"

View file

@ -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"

1
rust-toolchain Normal file
View file

@ -0,0 +1 @@
nightly

14
rustfmt.toml Normal file
View file

@ -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"

View file

@ -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<Self, AstError>;
fn from_expr(e: Expr) -> AstResult<Self>;
}
impl FromExpr for Expr {
fn from_expr(e: Expr) -> Result<Self, AstError> {
fn from_expr(e: Expr) -> AstResult<Self> {
Ok(e)
}
}
@ -25,14 +24,14 @@ pub enum DefType {
}
impl FromExpr for DefType {
fn from_expr(e: Expr) -> Result<Self, AstError> {
if let Expr::Symbol(_, sym) = e {
fn from_expr(e: Expr) -> AstResult<Self> {
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<T> {
}
impl<T: FromExpr> FromExpr for Definitional<T> {
fn from_expr(e: Expr) -> Result<Self, AstError> {
if let Expr::List(span, list) = e {
fn from_expr(e: Expr) -> AstResult<Self> {
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::<Result<Vec<_>, AstError>>()?;
Ok(Definitional {
def_type,
name,
attrs,
children,
})
} else {
Err(AstError::UnexpectedNode)
}
let children = iter.map(|x| T::from_expr(x)).collect::<AstResult<Vec<_>>>()?;
Definitional { def_type, name, attrs, children }
})
}
}
#[derive(Debug, Eq, PartialEq)]
@ -75,27 +66,20 @@ pub struct Element<T> {
}
impl FromExpr for Element<Expr> {
fn from_expr(e: Expr) -> Result<Self, AstError> {
if let Expr::List(span, list) = e {
fn from_expr(e: Expr) -> AstResult<Self> {
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<I: Iterator<Item = Expr>>(
iter: &mut itertools::PutBack<I>,
) -> HashMap<String, Expr> {
fn parse_key_values<I: Iterator<Item = Expr>>(iter: &mut itertools::PutBack<I>) -> HashMap<String, Expr> {
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::<Expr>::from_expr(
parser.parse("(box :bar 12 :baz \"hi\" foo (bar))").unwrap()
)
.unwrap(),
Element::<Expr>::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())]),
],
}
);

View file

@ -1,18 +1,52 @@
use crate::Expr;
use crate::{Expr, ExprType, Span};
use thiserror::Error;
#[derive(Debug, PartialEq, Eq)]
pub type AstResult<T> = Result<T, AstError>;
#[derive(Debug, PartialEq, Eq, Error)]
pub enum AstError {
UnexpectedNode,
InvalidDefinition,
WrongExprType(Expr),
MissingNode,
#[error("Definition invalid")]
InvalidDefinition(Option<Span>),
#[error("Expected a {1}, but got nothing")]
MissingNode(Option<Span>, ExprType),
#[error("Wrong type of expression: Expected {1} but got {2}")]
WrongExprType(Option<Span>, ExprType, Expr),
}
pub fn spanned(span: Span, err: impl Into<AstError>) -> 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<T> {
fn or_missing(self) -> Result<T, AstError>;
fn or_missing(self, t: ExprType) -> Result<T, AstError>;
}
impl<T> OptionAstErrorExt<T> for Option<T> {
fn or_missing(self) -> Result<T, AstError> {
self.ok_or(AstError::MissingNode)
fn or_missing(self, t: ExprType) -> Result<T, AstError> {
self.ok_or(AstError::MissingNode(None, t))
}
}
pub trait AstResultExt<T> {
fn at(self, span: Span) -> Result<T, AstError>;
}
impl<T, E: Into<AstError>> AstResultExt<T> for Result<T, E> {
fn at(self, span: Span) -> Result<T, AstError> {
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)
}};
}

View file

@ -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<Expr>),
@ -32,28 +50,50 @@ pub enum Expr {
Symbol(Span, String),
Str(Span, String),
Number(Span, i32),
Comment,
Comment(Span),
}
impl From<Expr> 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<String> = Expr::Str(_, x) => x);
as_func!(as_symbol<String> = Expr::Symbol(_, x) => x);
as_func!(ExprType::Str, as_str<String> = Expr::Str(_, x) => x);
fn is_keyword(&self) -> bool {
as_func!(ExprType::Symbol, as_symbol<String> = Expr::Symbol(_, x) => x);
as_func!(ExprType::List, as_list<Vec<Expr>> = 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");
}

View file

@ -15,7 +15,7 @@ pub Expr: Expr = {
<x:Symbol> => x,
<l:@L> <x:StrLit> <r:@R> => Expr::Str(Span(l, r), x),
<l:@L> <x:Num> <r:@R> => Expr::Number(Span(l, r), x),
Comment => Expr::Comment,
<l:@L> Comment <r:@R> => Expr::Comment(Span(l, r)),
};
Keyword: Expr = <l:@L> <x:r":[^\s]+"> <r:@R> => Expr::Keyword(Span(l, r), x.to_string());