never sseen such clean error handling
This commit is contained in:
parent
a06927e356
commit
76ca07b4dd
8 changed files with 204 additions and 98 deletions
57
Cargo.lock
generated
57
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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
1
rust-toolchain
Normal file
|
@ -0,0 +1 @@
|
|||
nightly
|
14
rustfmt.toml
Normal file
14
rustfmt.toml
Normal 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"
|
|
@ -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())]),
|
||||
],
|
||||
}
|
||||
);
|
||||
|
|
52
src/error.rs
52
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<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)
|
||||
}};
|
||||
}
|
||||
|
|
104
src/main.rs
104
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<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");
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Add table
Reference in a new issue