FromDynVal-trait

This commit is contained in:
elkowar 2021-07-20 18:51:29 +02:00
parent 7539dda162
commit 5748185fb7
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
2 changed files with 52 additions and 23 deletions

View file

@ -1,7 +1,7 @@
use crate::ast::Span; use crate::ast::Span;
use itertools::Itertools; use itertools::Itertools;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt, iter::FromIterator}; use std::{fmt, iter::FromIterator, str::FromStr};
pub type Result<T> = std::result::Result<T, ConversionError>; pub type Result<T> = std::result::Result<T, ConversionError>;
@ -34,7 +34,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 {
@ -70,32 +70,46 @@ impl std::str::FromStr for DynVal {
} }
} }
macro_rules! impl_try_from { pub trait FromDynVal: Sized {
(impl From<$typ:ty> { type Err;
$(for $for:ty => |$arg:ident| $code:expr);*; fn from_dynval(x: &DynVal) -> std::result::Result<Self, Self::Err>;
}) => { }
$(impl TryFrom<$typ> for $for {
type Error = ConversionError; impl<E, T: FromStr<Err = E>> FromDynVal for T {
fn try_from($arg: $typ) -> std::result::Result<Self, Self::Error> { $code } type Err = E;
fn from_dynval(x: &DynVal) -> std::result::Result<Self, Self::Err> {
x.0.parse()
}
}
macro_rules! impl_from_dynval {
(
$(for $for:ty => |$name:ident| $code:expr);*;
) => {
$(impl FromDynVal for $for {
type Err = ConversionError;
fn from_dynval($name: DynVal) -> std::result::Result<Self, Self::Err> { $code }
})* })*
}; };
} }
macro_rules! impl_primval_from { macro_rules! impl_dynval_from {
($($t:ty),*) => { ($($t:ty),*) => {
$(impl From<$t> for DynVal { $(impl From<$t> for DynVal {
fn from(x: $t) -> Self { DynVal(x.to_string(), None) } fn from(x: $t) -> Self { DynVal(x.to_string(), None) }
})* })*
}; };
} }
impl_try_from!(impl From<DynVal> {
for String => |x| x.as_string();
for f64 => |x| x.as_f64();
for i32 => |x| x.as_i32();
for bool => |x| x.as_bool();
//for Vec<String> => |x| x.as_vec();
});
impl_primval_from!(bool, i32, u32, f32, u8, f64, &str); // impl_from_dynval! {
// for String => |x| x.as_string();
// for f64 => |x| x.as_f64();
// for i32 => |x| x.as_i32();
// for bool => |x| x.as_bool();
////for Vec<String> => |x| x.as_vec();
//}
impl_dynval_from!(bool, i32, u32, f32, u8, f64, &str);
impl From<&serde_json::Value> for DynVal { impl From<&serde_json::Value> for DynVal {
fn from(v: &serde_json::Value) -> Self { fn from(v: &serde_json::Value) -> Self {
@ -118,6 +132,10 @@ impl DynVal {
DynVal(s, None) DynVal(s, None)
} }
pub fn read_as<E, T: FromDynVal<Err = E>>(&self) -> std::result::Result<T, E> {
T::from_dynval(self)
}
pub fn into_inner(self) -> String { pub fn into_inner(self) -> String {
self.0 self.0
} }

View file

@ -8,6 +8,9 @@ use std::collections::HashMap;
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum EvalError { pub enum EvalError {
#[error("Tried to reference variable `{0}`, but we cannot access variables here")]
NoVariablesAllowed(String),
#[error("Invalid regex: {0}")] #[error("Invalid regex: {0}")]
InvalidRegex(#[from] regex::Error), InvalidRegex(#[from] regex::Error),
@ -114,12 +117,20 @@ impl SimplExpr {
} }
} }
pub fn eval(self, values: &HashMap<VarName, DynVal>) -> Result<DynVal, EvalError> { pub fn eval_no_vars(&self) -> Result<DynVal, EvalError> {
match self.eval(&HashMap::new()) {
Ok(x) => Ok(x),
Err(EvalError::UnknownVariable(name)) => Err(EvalError::NoVariablesAllowed(name)),
Err(x) => Err(x),
}
}
pub fn eval(&self, values: &HashMap<VarName, DynVal>) -> Result<DynVal, EvalError> {
let span = self.span(); let span = self.span();
let value = match self { let value = match self {
SimplExpr::Literal(_, x) => Ok(x), SimplExpr::Literal(_, x) => Ok(x.clone()),
SimplExpr::VarRef(span, ref name) => { SimplExpr::VarRef(span, ref name) => {
Ok(values.get(name).cloned().ok_or_else(|| EvalError::UnresolvedVariable(name.to_string()).at(span))?.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)?;
@ -176,12 +187,12 @@ impl SimplExpr {
.unwrap_or(&serde_json::Value::Null); .unwrap_or(&serde_json::Value::Null);
Ok(DynVal::from(indexed_value)) Ok(DynVal::from(indexed_value))
} }
_ => Err(EvalError::CannotIndex(format!("{}", val)).at(span)), _ => Err(EvalError::CannotIndex(format!("{}", val)).at(*span)),
} }
} }
SimplExpr::FunctionCall(span, function_name, args) => { SimplExpr::FunctionCall(span, function_name, args) => {
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)) Ok(value?.at(span))