difftreelog
feat(evaluator) tailstrict calls
in: master
4 files changed
crates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/error.rs
+++ b/crates/jsonnet-evaluator/src/error.rs
@@ -9,6 +9,8 @@
RuntimeError(String),
StackOverflow,
+ FractionalIndex,
+ DivisionByZero,
}
#[derive(Clone, Debug)]
crates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -135,7 +135,12 @@
// Num X Num
(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),
- (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),
+ (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {
+ if *v2 <= f64::EPSILON {
+ create_error(crate::Error::DivisionByZero)?
+ }
+ Val::Num(v1 / v2)
+ }
(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),
@@ -305,6 +310,7 @@
})
}
+#[inline(always)]
pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {
use Expr::*;
let locexpr = expr.clone();
@@ -356,11 +362,15 @@
create_error(crate::Error::NoSuchField(s))?
}
}
- (Val::Arr(v), Val::Num(n)) => v
- .get(n as usize)
- .unwrap_or_else(|| panic!("out of bounds"))
- .clone()
- .unwrap_if_lazy()?,
+ (Val::Arr(v), Val::Num(n)) => {
+ if n.fract() > f64::EPSILON {
+ create_error(crate::Error::FractionalIndex)?
+ }
+ v.get(n as usize)
+ .unwrap_or_else(|| panic!("out of bounds"))
+ .clone()
+ .unwrap_if_lazy()?
+ }
(Val::Str(s), Val::Num(n)) => {
Val::Str(s.chars().skip(n as usize).take(1).collect())
}
@@ -511,29 +521,52 @@
panic!("bad trace call");
}
}
+ ("std", "pow") => {
+ assert_eq!(args.len(), 2);
+ if let (Val::Num(a), Val::Num(b)) = (
+ evaluate(context.clone(), &args[0].1)?,
+ evaluate(context, &args[1].1)?,
+ ) {
+ Val::Num(a.powf(b))
+ } else {
+ panic!("bad pow call");
+ }
+ }
(ns, name) => panic!("Intristic not found: {}.{}", ns, name),
},
- Val::Func(f) => push(locexpr, "function call".to_owned(), || {
- f.evaluate(
- args.clone()
- .into_iter()
- .map(move |a| {
- (
- a.clone().0,
- if *tailstrict {
- Val::Lazy(LazyVal::new_resolved(
- evaluate(context.clone(), &a.1).unwrap(),
+ Val::Func(f) => {
+ let body = #[inline(always)]
+ || {
+ f.evaluate(
+ args.clone()
+ .into_iter()
+ .map(
+ #[inline(always)]
+ move |a| {
+ Ok((
+ a.clone().0,
+ if *tailstrict {
+ Val::Lazy(LazyVal::new_resolved(evaluate(
+ context.clone(),
+ &a.1,
+ )?))
+ } else {
+ Val::Lazy(lazy_val!(
+ closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))
+ ))
+ },
))
- } else {
- Val::Lazy(lazy_val!(
- closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1))
- ))
},
)
- })
- .collect(),
- )
- })?,
+ .collect::<Result<Vec<_>>>()?,
+ )
+ };
+ if *tailstrict {
+ body()?
+ } else {
+ push(locexpr, "function call".to_owned(), body)?
+ }
+ }
_ => panic!("{:?} is not a function", value),
}
}
@@ -578,9 +611,7 @@
)?
} else {
match cond_else {
- Some(v) => push(v.clone(), "if condition 'else' branch".to_owned(), || {
- evaluate(context, v)
- })?,
+ Some(v) => evaluate(context, v)?,
None => Val::Null,
}
}
crates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth2#![feature(type_alias_impl_trait)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5#![feature(stmt_expr_attributes)]5mod ctx;6mod ctx;6mod dynamic;7mod dynamic;7mod error;8mod error;51 }52 }52}53}5455pub struct EvaluationSettings {56 max_stack_frames: usize,57 max_stack_trace_size: usize,58}59impl Default for EvaluationSettings {60 fn default() -> Self {61 EvaluationSettings {62 max_stack_frames: 500,63 max_stack_trace_size: 20,64 }65 }66}536754pub struct FileData(String, LocExpr, Option<Val>);68pub struct FileData(String, LocExpr, Option<Val>);55#[derive(Default)]69#[derive(Default)]61 files: RefCell<HashMap<PathBuf, FileData>>,75 files: RefCell<HashMap<PathBuf, FileData>>,62 globals: RefCell<HashMap<String, Val>>,76 globals: RefCell<HashMap<String, Val>>,7778 settings: EvaluationSettings,63}79}648065thread_local! {81thread_local! {82 /// Contains state for currently executing file83 /// Global state is fine there66 pub static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)84 pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)67}85}86#[inline(always)]68pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {87pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {69 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))88 EVAL_STATE.with(89 #[inline(always)]90 |s| f(s.borrow().as_ref().unwrap()),91 )70}92}71pub(crate) fn create_error<T>(err: Error) -> Result<T> {93pub(crate) fn create_error<T>(err: Error) -> Result<T> {72 with_state(|s| s.error(err))94 with_state(|s| s.error(err))73}95}96#[inline(always)]74pub(crate) fn push<T>(e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {97pub(crate) fn push<T>(e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {75 with_state(|s| s.push(e, comment, f))98 with_state(|s| s.push(e, comment, f))76}99}77100101/// Maintains stack trace and import resolution78#[derive(Default, Clone)]102#[derive(Default, Clone)]79pub struct EvaluationState(Rc<EvaluationStateInternals>);103pub struct EvaluationState(Rc<EvaluationStateInternals>);80impl EvaluationState {104impl EvaluationState {189 Context::new().extend(new_bindings, None, None, None)213 Context::new().extend(new_bindings, None, None, None)190 }214 }191215216 #[inline(always)]192 pub fn push<T>(&self, e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {217 pub fn push<T>(&self, e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {193 {218 {194 let mut stack = self.0.stack.borrow_mut();219 let mut stack = self.0.stack.borrow_mut();195 if stack.len() > 500 {220 if stack.len() > self.0.settings.max_stack_frames {196 drop(stack);221 drop(stack);197 return self.error(Error::StackOverflow);222 return self.error(Error::StackOverflow);198 } else {223 } else {240 .borrow()241 .iter()242 .rev()243 .take(self.0.settings.max_stack_trace_size)244 .cloned()245 .collect(),246 )crates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -78,6 +78,8 @@
}
impl FuncDesc {
// TODO: Check for unset variables
+ /// This function is always inlined to make tailstrict work
+ #[inline(always)]
pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Result<Val> {
let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
let future_ctx = Context::new_future();