Better error handling
This commit is contained in:
parent
d8ffd3153d
commit
3b6180ad7d
7 changed files with 66 additions and 135 deletions
|
@ -1,11 +1,19 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use simplexpr::dynval::DynVal;
|
||||
|
||||
fn main() {
|
||||
let mut files = codespan_reporting::files::SimpleFiles::new();
|
||||
|
||||
let input = "12 + \"hi\" * foo ) ? bar == baz : false";
|
||||
let input = "12 + foo * 2 < 2 ? bar == true : false";
|
||||
|
||||
let _ = files.add("foo.eww", input);
|
||||
let ast = simplexpr::parser::parse_string(input);
|
||||
match ast {
|
||||
|
||||
let mut vars = HashMap::new();
|
||||
vars.insert("foo".to_string(), "2".into());
|
||||
|
||||
match ast.and_then(|x| x.eval(&vars).map_err(|e| e.into())) {
|
||||
Ok(ast) => {
|
||||
println!("{:?}", ast);
|
||||
}
|
||||
|
|
23
src/ast.rs
23
src/ast.rs
|
@ -5,6 +5,20 @@ use serde::{Deserialize, Serialize};
|
|||
#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct Span(pub usize, pub usize);
|
||||
|
||||
pub trait MaybeSpanned {
|
||||
fn try_span(&self) -> Option<Span>;
|
||||
}
|
||||
|
||||
impl MaybeSpanned for Span {
|
||||
fn try_span(&self) -> Option<Span> {
|
||||
Some(*self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span_between(a: impl MaybeSpanned, b: impl MaybeSpanned) -> Option<Span> {
|
||||
Some(Span(a.try_span()?.0, b.try_span()?.1))
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Span {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}..{}", self.0, self.1)
|
||||
|
@ -51,6 +65,11 @@ pub enum SimplExpr {
|
|||
JsonAccess(Span, Box<SimplExpr>, Box<SimplExpr>),
|
||||
FunctionCall(Span, String, Vec<SimplExpr>),
|
||||
}
|
||||
impl MaybeSpanned for SimplExpr {
|
||||
fn try_span(&self) -> Option<Span> {
|
||||
Some(self.span())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SimplExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
@ -68,6 +87,10 @@ impl std::fmt::Display for SimplExpr {
|
|||
}
|
||||
}
|
||||
impl SimplExpr {
|
||||
pub fn literal(span: Span, s: String) -> Self {
|
||||
Self::Literal(span, DynVal(s, Some(span)))
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
SimplExpr::Literal(span, _) => *span,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ast::Span;
|
||||
use crate::ast::{MaybeSpanned, Span};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{convert::TryFrom, fmt, iter::FromIterator};
|
||||
|
@ -6,7 +6,7 @@ use std::{convert::TryFrom, fmt, iter::FromIterator};
|
|||
pub type Result<T> = std::result::Result<T, ConversionError>;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("Type error: Failed to turn {value} into a {target_type}")]
|
||||
#[error("Failed to turn {value} into a {target_type}")]
|
||||
pub struct ConversionError {
|
||||
value: DynVal,
|
||||
target_type: &'static str,
|
||||
|
@ -17,11 +17,21 @@ impl ConversionError {
|
|||
fn new(value: DynVal, target_type: &'static str, source: Box<dyn std::error::Error>) -> Self {
|
||||
ConversionError { value, target_type, source: Some(source) }
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
self.value.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize, Default)]
|
||||
pub struct DynVal(pub String, pub Option<Span>);
|
||||
|
||||
impl MaybeSpanned for DynVal {
|
||||
fn try_span(&self) -> Option<Span> {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for DynVal {
|
||||
fn from(s: String) -> Self {
|
||||
DynVal(s, None)
|
||||
|
@ -30,7 +40,7 @@ impl From<String> for DynVal {
|
|||
|
||||
impl fmt::Display for DynVal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
write!(f, "\"{}\"", self.0)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for DynVal {
|
||||
|
@ -106,6 +116,10 @@ impl From<&serde_json::Value> for DynVal {
|
|||
}
|
||||
|
||||
impl DynVal {
|
||||
pub fn at(self, span: Span) -> Self {
|
||||
DynVal(self.0, Some(span))
|
||||
}
|
||||
|
||||
pub fn from_string(s: String) -> Self {
|
||||
DynVal(s, None)
|
||||
}
|
||||
|
|
10
src/error.rs
10
src/error.rs
|
@ -6,16 +6,13 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||
pub enum Error {
|
||||
#[error("Parse error: {source}")]
|
||||
ParseError { source: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError> },
|
||||
#[error("Conversion error: {source}")]
|
||||
ConversionError {
|
||||
#[from]
|
||||
source: dynval::ConversionError,
|
||||
},
|
||||
#[error("Conversion error: {0}")]
|
||||
ConversionError(#[from] dynval::ConversionError),
|
||||
#[error("At: {0}: {1}")]
|
||||
Spanned(Span, Box<dyn std::error::Error>),
|
||||
|
||||
#[error(transparent)]
|
||||
Eval(crate::eval::EvalError),
|
||||
Eval(#[from] crate::eval::EvalError),
|
||||
|
||||
#[error(transparent)]
|
||||
Other(#[from] Box<dyn std::error::Error>),
|
||||
|
@ -31,6 +28,7 @@ impl Error {
|
|||
Self::ParseError { source } => get_parse_error_span(source),
|
||||
Self::Spanned(span, _) => Some(*span),
|
||||
Self::Eval(err) => err.span(),
|
||||
Self::ConversionError(err) => err.span(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
13
src/eval.rs
13
src/eval.rs
|
@ -11,10 +11,10 @@ pub enum EvalError {
|
|||
#[error("Invalid regex: {0}")]
|
||||
InvalidRegex(#[from] regex::Error),
|
||||
|
||||
#[error("got unresolved variable {0}")]
|
||||
#[error("got unresolved variable `{0}`")]
|
||||
UnresolvedVariable(VarName),
|
||||
|
||||
#[error("Conversion error: {0}")]
|
||||
#[error("Type error: {0}")]
|
||||
ConversionError(#[from] ConversionError),
|
||||
|
||||
#[error("Incorrect number of arguments given to function: {0}")]
|
||||
|
@ -34,6 +34,7 @@ impl EvalError {
|
|||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
EvalError::Spanned(span, _) => Some(*span),
|
||||
EvalError::ConversionError(err) => err.span(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -103,10 +104,11 @@ impl SimplExpr {
|
|||
}
|
||||
|
||||
pub fn eval(self, values: &HashMap<VarName, DynVal>) -> Result<DynVal, EvalError> {
|
||||
match self {
|
||||
let span = self.span();
|
||||
let value = match self {
|
||||
SimplExpr::Literal(_, x) => Ok(x),
|
||||
SimplExpr::VarRef(span, ref name) => {
|
||||
values.get(name).cloned().ok_or_else(|| EvalError::UnresolvedVariable(name.to_string()).at(span))
|
||||
Ok(values.get(name).cloned().ok_or_else(|| EvalError::UnresolvedVariable(name.to_string()).at(span))?.at(span))
|
||||
}
|
||||
SimplExpr::BinOp(_, a, op, b) => {
|
||||
let a = a.eval(values)?;
|
||||
|
@ -167,7 +169,8 @@ impl SimplExpr {
|
|||
let args = args.into_iter().map(|a| a.eval(values)).collect::<Result<_, EvalError>>()?;
|
||||
call_expr_function(&function_name, args).map_err(|e| e.at(span))
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(value?.at(span))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
|
||||
use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*};
|
||||
use crate::parser::lexer::{Token, LexicalError};
|
||||
use crate::parser::lalrpop_helpers::*;
|
||||
|
||||
|
||||
grammar;
|
||||
|
||||
extern {
|
||||
type Location = usize;
|
||||
type Error = LexicalError;
|
||||
|
||||
enum Token {
|
||||
"+" => Token::Plus,
|
||||
"-" => Token::Minus,
|
||||
"*" => Token::Times,
|
||||
"/" => Token::Div,
|
||||
"%" => Token::Mod,
|
||||
"==" => Token::Equals,
|
||||
"!=" => Token::NotEquals,
|
||||
"&&" => Token::And,
|
||||
"||" => Token::Or,
|
||||
">" => Token::GT,
|
||||
"<" => Token::LT,
|
||||
"?:" => Token::Elvis,
|
||||
"=~" => Token::RegexMatch,
|
||||
|
||||
"!" => Token::Not,
|
||||
|
||||
"," => Token::Comma,
|
||||
"?" => Token::Question,
|
||||
":" => Token::Colon,
|
||||
"(" => Token::LPren,
|
||||
")" => Token::RPren,
|
||||
"[" => Token::LBrack,
|
||||
"]" => Token::RBrack,
|
||||
"." => Token::Dot,
|
||||
|
||||
"true" => Token::True,
|
||||
"false" => Token::False,
|
||||
|
||||
"identifier" => Token::Ident(<String>),
|
||||
"number" => Token::NumLit(<String>),
|
||||
"string" => Token::StrLit(<String>),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Comma<T>: Vec<T> = {
|
||||
<mut v:(<T> ",")*> <e:T?> => match e {
|
||||
None => v,
|
||||
Some(e) => {
|
||||
v.push(e);
|
||||
v
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub Expr: SimplExpr = {
|
||||
#[precedence(level="0")]
|
||||
<Literal>,
|
||||
<l:@L> <ident:"identifier"> <r:@R> => VarRef(Span(l, r), ident.to_string()),
|
||||
"(" <ExprReset> ")",
|
||||
|
||||
#[precedence(level="1")] #[assoc(side="right")]
|
||||
<l:@L> <ident:"identifier"> "(" <args: Comma<ExprReset>> ")" <r:@R> => FunctionCall(Span(l, r), ident, args),
|
||||
<l:@L> <value:Expr> "[" <index: ExprReset> "]" <r:@R> => JsonAccess(Span(l, r), b(value), b(index)),
|
||||
|
||||
<l:@L> <value:Expr> "." <lit_l:@L> <index:"identifier"> <r:@R> => {
|
||||
JsonAccess(Span(l, r), b(value), b(Literal(Span(lit_l, r), index.into())))
|
||||
},
|
||||
|
||||
#[precedence(level="2")] #[assoc(side="right")]
|
||||
<l:@L> "!" <e:Expr> <r:@R> => UnaryOp(Span(l, r), Not, b(e)),
|
||||
|
||||
#[precedence(level="3")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "*" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Times, b(re)),
|
||||
<l:@L> <le:Expr> "/" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Div, b(re)),
|
||||
<l:@L> <le:Expr> "%" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Mod, b(re)),
|
||||
|
||||
#[precedence(level="4")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "+" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Plus, b(re)),
|
||||
<l:@L> <le:Expr> "-" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Minus, b(re)),
|
||||
|
||||
#[precedence(level="5")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "==" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Equals, b(re)),
|
||||
<l:@L> <le:Expr> "!=" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), NotEquals, b(re)),
|
||||
<l:@L> <le:Expr> "<" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), GT, b(re)),
|
||||
<l:@L> <le:Expr> ">" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), LT, b(re)),
|
||||
<l:@L> <le:Expr> "=~" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), RegexMatch, b(re)),
|
||||
|
||||
#[precedence(level="6")] #[assoc(side="left")]
|
||||
<l:@L> <le:Expr> "&&" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), And, b(re)),
|
||||
<l:@L> <le:Expr> "||" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Or, b(re)),
|
||||
<l:@L> <le:Expr> "?:" <re:Expr> <r:@R> => BinOp(Span(l, r), b(le), Elvis, b(re)),
|
||||
|
||||
#[precedence(level="7")] #[assoc(side="right")]
|
||||
<l:@L> <cond:Expr> "?" <then:ExprReset> ":" <els:Expr> <r:@R> => {
|
||||
IfElse(Span(l, r), b(cond), b(then), b(els))
|
||||
},
|
||||
};
|
||||
|
||||
ExprReset = <Expr>;
|
||||
|
||||
Literal: SimplExpr = {
|
||||
<l:@L> <x:StrLit> <r:@R> => Literal(Span(l, r), x.into()),
|
||||
<l:@L> <x:"number"> <r:@R> => Literal(Span(l, r), x.into()),
|
||||
<l:@L> "true" <r:@R> => Literal(Span(l, r), "true".into()),
|
||||
<l:@L> "false" <r:@R> => Literal(Span(l, r), "false".into()),
|
||||
}
|
||||
|
||||
StrLit: String = {
|
||||
<x:"string"> => x[1..x.len() - 1].to_owned(),
|
||||
};
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
use crate::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*};
|
||||
use crate::parser::lexer::{Token, LexicalError};
|
||||
use crate::parser::lalrpop_helpers::*;
|
||||
|
@ -103,10 +102,10 @@ pub Expr: SimplExpr = {
|
|||
ExprReset = <Expr>;
|
||||
|
||||
Literal: SimplExpr = {
|
||||
<l:@L> <x:StrLit> <r:@R> => Literal(Span(l, r), x.into()),
|
||||
<l:@L> <x:"number"> <r:@R> => Literal(Span(l, r), x.into()),
|
||||
<l:@L> "true" <r:@R> => Literal(Span(l, r), "true".into()),
|
||||
<l:@L> "false" <r:@R> => Literal(Span(l, r), "false".into()),
|
||||
<l:@L> <x:StrLit> <r:@R> => SimplExpr::literal(Span(l, r), x),
|
||||
<l:@L> <x:"number"> <r:@R> => SimplExpr::literal(Span(l, r), x),
|
||||
<l:@L> "true" <r:@R> => SimplExpr::literal(Span(l, r), "true".into()),
|
||||
<l:@L> "false" <r:@R> => SimplExpr::literal(Span(l, r), "false".into()),
|
||||
}
|
||||
|
||||
StrLit: String = {
|
||||
|
|
Loading…
Add table
Reference in a new issue