1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod error;8mod evaluate;9mod obj;10mod val;1112use closure::closure;13pub use ctx::*;14pub use dynamic::*;15pub use error::*;16pub use evaluate::*;17use jsonnet_parser::*;18pub use obj::*;19use std::{cell::RefCell, collections::HashMap, rc::Rc};20pub use val::*;2122rc_fn_helper!(23 Binding,24 binding,25 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<Val>26);27rc_fn_helper!(28 LazyBinding,29 lazy_binding,30 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<LazyVal>31);32rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Result<Val>);33rc_fn_helper!(34 FunctionDefault,35 function_default,36 dyn Fn(Context, LocExpr) -> Result<Val>37);3839pub struct FileData(String, LocExpr, Option<Val>);40#[derive(Default)]41pub struct EvaluationStateInternals {42 43 stack: RefCell<Vec<StackTraceElement>>,44 45 files: RefCell<HashMap<String, FileData>>,46 globals: RefCell<HashMap<String, Val>>,47}4849thread_local! {50 pub static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)51}52pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {53 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))54}55pub(crate) fn create_error<T>(err: Error) -> Result<T> {56 with_state(|s| s.error(err))57}58pub(crate) fn push<T>(e: LocExpr, comment: String, f: impl FnOnce() -> T) -> T {59 with_state(|s| s.push(e, comment, f))60}6162#[derive(Default, Clone)]63pub struct EvaluationState(Rc<EvaluationStateInternals>);64impl EvaluationState {65 pub fn add_file(66 &self,67 name: String,68 code: String,69 ) -> std::result::Result<(), Box<dyn std::error::Error>> {70 self.0.files.borrow_mut().insert(71 name.clone(),72 FileData(73 code.clone(),74 parse(75 &code,76 &ParserSettings {77 file_name: name,78 loc_data: true,79 },80 )?,81 None,82 ),83 );8485 Ok(())86 }87 pub fn evaluate_file(&self, name: &str) -> Result<Val> {88 self.begin_state();89 let expr: LocExpr = {90 let ro_map = self.0.files.borrow();91 let value = ro_map92 .get(name)93 .unwrap_or_else(|| panic!("file not added: {:?}", name));94 if value.2.is_some() {95 return Ok(value.2.clone().unwrap());96 }97 value.1.clone()98 };99 let value = evaluate(self.create_default_context()?, &expr)?;100 {101 self.0102 .files103 .borrow_mut()104 .get_mut(name)105 .unwrap()106 .2107 .replace(value.clone());108 }109 self.end_state();110 Ok(value)111 }112113 pub fn parse_evaluate_raw(&self, code: &str) -> Result<Val> {114 let parsed = parse(115 &code,116 &ParserSettings {117 file_name: "raw.jsonnet".to_owned(),118 loc_data: true,119 },120 );121 self.begin_state();122 let value = evaluate(self.create_default_context()?, &parsed.unwrap());123 self.end_state();124 value125 }126127 pub fn add_stdlib(&self) {128 self.begin_state();129 use jsonnet_stdlib::STDLIB_STR;130 self.add_file("std.jsonnet".to_owned(), STDLIB_STR.to_owned())131 .unwrap();132 let val = self.evaluate_file("std.jsonnet").unwrap();133 self.0.globals.borrow_mut().insert("std".to_owned(), val);134 self.end_state();135 }136137 pub fn create_default_context(&self) -> Result<Context> {138 let globals = self.0.globals.borrow();139 let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();140 for (name, value) in globals.iter() {141 new_bindings.insert(142 name.clone(),143 lazy_binding!(144 closure!(clone value, |_self, _super_obj| Ok(lazy_val!(closure!(clone value, ||Ok(value.clone())))))145 ),146 );147 }148 Context::new().extend(new_bindings, None, None, None)149 }150151 pub fn push<T>(&self, e: LocExpr, comment: String, f: impl FnOnce() -> T) -> T {152 self.0153 .stack154 .borrow_mut()155 .push(StackTraceElement(e, comment));156 let result = f();157 self.0.stack.borrow_mut().pop();158 result159 }160 pub fn print_stack_trace(&self) {161 for e in self.stack_trace().0 {162 println!("{:?} - {:?}", e.0, e.1)163 }164 }165 pub fn stack_trace(&self) -> StackTrace {166 StackTrace(self.0.stack.borrow().iter().rev().cloned().collect())167 }168 pub fn error<T>(&self, err: Error) -> Result<T> {169 Err(LocError(err, self.stack_trace()))170 }171172 fn begin_state(&self) {173 EVAL_STATE.with(|v| v.borrow_mut().replace(self.clone()));174 }175 fn end_state(&self) {176 EVAL_STATE.with(|v| v.borrow_mut().take());177 }178}179180#[cfg(test)]181pub mod tests {182 use super::Val;183 use crate::EvaluationState;184 use jsonnet_parser::*;185186 #[test]187 fn eval_state_stacktrace() {188 let state = EvaluationState::default();189 state.push(190 loc_expr!(Expr::Num(0.0), true, ("test1.jsonnet".to_owned(), 10, 20)),191 "outer".to_owned(),192 || {193 state.push(194 loc_expr!(Expr::Num(0.0), true, ("test2.jsonnet".to_owned(), 30, 40)),195 "inner".to_owned(),196 || state.print_stack_trace(),197 );198 },199 );200 }201202 #[test]203 fn eval_state_standard() {204 let state = EvaluationState::default();205 state.add_stdlib();206 assert_eq!(207 state208 .parse_evaluate_raw(r#"std.assertEqual(std.base64("test"), "dGVzdA==w")"#)209 .unwrap(),210 Val::Bool(true)211 );212 }213214 macro_rules! eval {215 ($str: expr) => {216 evaluate(217 Context::new(),218 EvaluationState::default(),219 &parse(220 $str,221 &ParserSettings {222 loc_data: true,223 file_name: "test.jsonnet".to_owned(),224 },225 )226 .unwrap(),227 )228 };229 }230231 macro_rules! eval_stdlib {232 ($str: expr) => {{233 let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";234 evaluate(235 Context::new(),236 EvaluationState::default(),237 &parse(238 &(std + $str),239 &ParserSettings {240 loc_data: true,241 file_name: "test.jsonnet".to_owned(),242 },243 )244 .unwrap(),245 )246 }};247 }248249 macro_rules! assert_eval {250 ($str: expr) => {251 assert_eq!(252 evaluate(253 Context::new(),254 EvaluationState::default(),255 &parse(256 $str,257 &ParserSettings {258 loc_data: true,259 file_name: "test.jsonnet".to_owned(),260 }261 )262 .unwrap()263 ),264 Val::Bool(true)265 )266 };267 }268 macro_rules! assert_json {269 ($str: expr, $out: expr) => {270 assert_eq!(271 format!(272 "{}",273 evaluate(274 Context::new(),275 EvaluationState::default(),276 &parse(277 $str,278 &ParserSettings {279 loc_data: true,280 file_name: "test.jsonnet".to_owned(),281 }282 )283 .unwrap()284 )285 ),286 $out287 )288 };289 }290 macro_rules! assert_json_stdlib {291 ($str: expr, $out: expr) => {292 assert_eq!(format!("{}", eval_stdlib!($str)), $out)293 };294 }295 macro_rules! assert_eval_neg {296 ($str: expr) => {297 assert_eq!(298 evaluate(299 Context::new(),300 EvaluationState::default(),301 &parse(302 $str,303 &ParserSettings {304 loc_data: true,305 file_name: "test.jsonnet".to_owned(),306 }307 )308 .unwrap()309 ),310 Val::Bool(false)311 )312 };313 }314315 316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526}