use femstache_parser::nodes::{Access, BinOp, Expr, ExprValue, Literal};
use crate::{
TemplateInput,
env::TemplateEnv,
error::{EvalError, EvalResult},
value::Value,
};
pub fn eval_expr<'i, I: TemplateInput>(env: &TemplateEnv<'i, I>, expr: &Expr) -> EvalResult<Value> {
match expr {
Expr::Value(v) => match v {
ExprValue::Ident(ident) => env
.get(&ident.data)
.ok_or_else(|| EvalError::VariableNotFound(ident.clone())),
ExprValue::Literal(literal) => Ok(match literal {
Literal::Null(_) => Value::Null,
Literal::True(_) => Value::Bool(true),
Literal::False(_) => Value::Bool(false),
Literal::String(s) => Value::String(s.as_str().to_owned()),
Literal::List(l) => {
let mut items = Vec::new();
for item in &l.items {
let value = eval_expr(env, &item)?;
items.push(value);
}
Value::List(items)
}
}),
ExprValue::Group(group) => {
let r = eval_expr(env, &group.expr)?;
Ok(r)
}
},
Expr::Access(st, Access::Member(ma)) => {
let st = eval_expr(env, st)?;
let Some(fields) = st.as_struct() else {
return Err(EvalError::InvalidType);
};
let Some(field) = fields.get(&ma.ident.data) else {
return Err(EvalError::NoMember(ma.ident.clone()));
};
Ok(field.clone())
}
Expr::Access(st, Access::Dict(da)) => {
let st = eval_expr(env, st)?;
let key = eval_expr(env, &da.expr)?;
let Some(map) = st.as_dict() else {
return Err(EvalError::InvalidType);
};
let Some(key_str) = key.as_str() else {
return Err(EvalError::InvalidType);
};
let Some(field) = map.get(key_str) else {
return Err(EvalError::NoDictKey(key_str.to_owned()));
};
Ok(field.clone())
}
Expr::BinOp(lhs, binop, rhs) => {
let lhs = eval_expr(env, lhs)?;
match binop {
BinOp::Eq(_) => {
let rhs = eval_expr(env, rhs)?;
Ok(Value::Bool(lhs == rhs))
}
BinOp::Neq(_) => {
let rhs = eval_expr(env, rhs)?;
Ok(Value::Bool(lhs != rhs))
}
BinOp::PlusPlus(_) => {
let rhs = eval_expr(env, rhs)?;
let Some(ls) = lhs.as_str() else {
return Err(EvalError::InvalidType);
};
let Some(rs) = rhs.as_str() else {
return Err(EvalError::InvalidType);
};
let mut full = String::with_capacity(ls.len() + rs.len());
full.push_str(ls);
full.push_str(rs);
Ok(Value::String(full))
}
}
}
}
}