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() {
|
fn main() {
|
||||||
let mut files = codespan_reporting::files::SimpleFiles::new();
|
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 _ = files.add("foo.eww", input);
|
||||||
let ast = simplexpr::parser::parse_string(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) => {
|
Ok(ast) => {
|
||||||
println!("{:?}", 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)]
|
#[derive(Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct Span(pub usize, pub usize);
|
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 {
|
impl std::fmt::Display for Span {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}..{}", self.0, self.1)
|
write!(f, "{}..{}", self.0, self.1)
|
||||||
|
@ -51,6 +65,11 @@ pub enum SimplExpr {
|
||||||
JsonAccess(Span, Box<SimplExpr>, Box<SimplExpr>),
|
JsonAccess(Span, Box<SimplExpr>, Box<SimplExpr>),
|
||||||
FunctionCall(Span, String, Vec<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 {
|
impl std::fmt::Display for SimplExpr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
@ -68,6 +87,10 @@ impl std::fmt::Display for SimplExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl SimplExpr {
|
impl SimplExpr {
|
||||||
|
pub fn literal(span: Span, s: String) -> Self {
|
||||||
|
Self::Literal(span, DynVal(s, Some(span)))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn span(&self) -> Span {
|
pub fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
SimplExpr::Literal(span, _) => *span,
|
SimplExpr::Literal(span, _) => *span,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::ast::Span;
|
use crate::ast::{MaybeSpanned, Span};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{convert::TryFrom, fmt, iter::FromIterator};
|
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>;
|
pub type Result<T> = std::result::Result<T, ConversionError>;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[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 {
|
pub struct ConversionError {
|
||||||
value: DynVal,
|
value: DynVal,
|
||||||
target_type: &'static str,
|
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 {
|
fn new(value: DynVal, target_type: &'static str, source: Box<dyn std::error::Error>) -> Self {
|
||||||
ConversionError { value, target_type, source: Some(source) }
|
ConversionError { value, target_type, source: Some(source) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn span(&self) -> Option<Span> {
|
||||||
|
self.value.1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Serialize, Default)]
|
#[derive(Clone, Deserialize, Serialize, Default)]
|
||||||
pub struct DynVal(pub String, pub Option<Span>);
|
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 {
|
impl From<String> for DynVal {
|
||||||
fn from(s: String) -> Self {
|
fn from(s: String) -> Self {
|
||||||
DynVal(s, None)
|
DynVal(s, None)
|
||||||
|
@ -30,7 +40,7 @@ impl From<String> for DynVal {
|
||||||
|
|
||||||
impl fmt::Display for DynVal {
|
impl fmt::Display for DynVal {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", self.0)
|
write!(f, "\"{}\"", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Debug for DynVal {
|
impl fmt::Debug for DynVal {
|
||||||
|
@ -106,6 +116,10 @@ impl From<&serde_json::Value> for DynVal {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynVal {
|
impl DynVal {
|
||||||
|
pub fn at(self, span: Span) -> Self {
|
||||||
|
DynVal(self.0, Some(span))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_string(s: String) -> Self {
|
pub fn from_string(s: String) -> Self {
|
||||||
DynVal(s, None)
|
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 {
|
pub enum Error {
|
||||||
#[error("Parse error: {source}")]
|
#[error("Parse error: {source}")]
|
||||||
ParseError { source: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError> },
|
ParseError { source: lalrpop_util::ParseError<usize, lexer::Token, lexer::LexicalError> },
|
||||||
#[error("Conversion error: {source}")]
|
#[error("Conversion error: {0}")]
|
||||||
ConversionError {
|
ConversionError(#[from] dynval::ConversionError),
|
||||||
#[from]
|
|
||||||
source: dynval::ConversionError,
|
|
||||||
},
|
|
||||||
#[error("At: {0}: {1}")]
|
#[error("At: {0}: {1}")]
|
||||||
Spanned(Span, Box<dyn std::error::Error>),
|
Spanned(Span, Box<dyn std::error::Error>),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Eval(crate::eval::EvalError),
|
Eval(#[from] crate::eval::EvalError),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Other(#[from] Box<dyn std::error::Error>),
|
Other(#[from] Box<dyn std::error::Error>),
|
||||||
|
@ -31,6 +28,7 @@ impl Error {
|
||||||
Self::ParseError { source } => get_parse_error_span(source),
|
Self::ParseError { source } => get_parse_error_span(source),
|
||||||
Self::Spanned(span, _) => Some(*span),
|
Self::Spanned(span, _) => Some(*span),
|
||||||
Self::Eval(err) => err.span(),
|
Self::Eval(err) => err.span(),
|
||||||
|
Self::ConversionError(err) => err.span(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/eval.rs
13
src/eval.rs
|
@ -11,10 +11,10 @@ pub enum EvalError {
|
||||||
#[error("Invalid regex: {0}")]
|
#[error("Invalid regex: {0}")]
|
||||||
InvalidRegex(#[from] regex::Error),
|
InvalidRegex(#[from] regex::Error),
|
||||||
|
|
||||||
#[error("got unresolved variable {0}")]
|
#[error("got unresolved variable `{0}`")]
|
||||||
UnresolvedVariable(VarName),
|
UnresolvedVariable(VarName),
|
||||||
|
|
||||||
#[error("Conversion error: {0}")]
|
#[error("Type error: {0}")]
|
||||||
ConversionError(#[from] ConversionError),
|
ConversionError(#[from] ConversionError),
|
||||||
|
|
||||||
#[error("Incorrect number of arguments given to function: {0}")]
|
#[error("Incorrect number of arguments given to function: {0}")]
|
||||||
|
@ -34,6 +34,7 @@ impl EvalError {
|
||||||
pub fn span(&self) -> Option<Span> {
|
pub fn span(&self) -> Option<Span> {
|
||||||
match self {
|
match self {
|
||||||
EvalError::Spanned(span, _) => Some(*span),
|
EvalError::Spanned(span, _) => Some(*span),
|
||||||
|
EvalError::ConversionError(err) => err.span(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,10 +104,11 @@ impl SimplExpr {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(self, values: &HashMap<VarName, DynVal>) -> Result<DynVal, EvalError> {
|
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::Literal(_, x) => Ok(x),
|
||||||
SimplExpr::VarRef(span, ref name) => {
|
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) => {
|
SimplExpr::BinOp(_, a, op, b) => {
|
||||||
let a = a.eval(values)?;
|
let a = a.eval(values)?;
|
||||||
|
@ -167,7 +169,8 @@ impl SimplExpr {
|
||||||
let args = args.into_iter().map(|a| a.eval(values)).collect::<Result<_, EvalError>>()?;
|
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))
|
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::ast::{SimplExpr::{self, *}, Span, BinOp::*, UnaryOp::*};
|
||||||
use crate::parser::lexer::{Token, LexicalError};
|
use crate::parser::lexer::{Token, LexicalError};
|
||||||
use crate::parser::lalrpop_helpers::*;
|
use crate::parser::lalrpop_helpers::*;
|
||||||
|
@ -103,10 +102,10 @@ pub Expr: SimplExpr = {
|
||||||
ExprReset = <Expr>;
|
ExprReset = <Expr>;
|
||||||
|
|
||||||
Literal: SimplExpr = {
|
Literal: SimplExpr = {
|
||||||
<l:@L> <x:StrLit> <r:@R> => Literal(Span(l, r), x.into()),
|
<l:@L> <x:StrLit> <r:@R> => SimplExpr::literal(Span(l, r), x),
|
||||||
<l:@L> <x:"number"> <r:@R> => Literal(Span(l, r), x.into()),
|
<l:@L> <x:"number"> <r:@R> => SimplExpr::literal(Span(l, r), x),
|
||||||
<l:@L> "true" <r:@R> => Literal(Span(l, r), "true".into()),
|
<l:@L> "true" <r:@R> => SimplExpr::literal(Span(l, r), "true".into()),
|
||||||
<l:@L> "false" <r:@R> => Literal(Span(l, r), "false".into()),
|
<l:@L> "false" <r:@R> => SimplExpr::literal(Span(l, r), "false".into()),
|
||||||
}
|
}
|
||||||
|
|
||||||
StrLit: String = {
|
StrLit: String = {
|
||||||
|
|
Loading…
Add table
Reference in a new issue