git.delta.rocks / jrsonnet / refs/commits / 3100b67da51b

difftreelog

feat add standard like api to evaluator

Лач2020-07-19parent: #b14023b.patch.diff
in: master

1 file changed

modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
34 path::PathBuf,34 path::PathBuf,
35 rc::Rc,35 rc::Rc,
36};36};
37use trace::{offset_to_location, CodeLocation};37use trace::{offset_to_location, CodeLocation, CompactFormat, TraceFormat};
38pub use val::*;38pub use val::*;
3939
40type BindableFn = dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<LazyVal>;40type BindableFn = dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<LazyVal>;
58 }58 }
59}59}
6060
61#[derive(Clone)]
62pub enum ManifestFormat {
63 Yaml(usize),
64 Json(usize),
65 None,
66}
67
61pub struct EvaluationSettings {68pub struct EvaluationSettings {
62 /// Limits recursion by limiting stack frames69 /// Limits recursion by limiting stack frames
63 pub max_stack: usize,70 pub max_stack: usize,
64 /// Limit amount of stack trace items preserved71 /// Limit amount of stack trace items preserved
65 pub max_trace: usize,72 pub max_trace: usize,
66 /// Used for std.extVar73 /// Used for std.extVar
67 pub ext_vars: HashMap<Rc<str>, Val>,74 pub ext_vars: HashMap<Rc<str>, Val>,
75 /// TLA vars
76 pub tla_vars: HashMap<Rc<str>, Val>,
68 /// Global variables are inserted in default context77 /// Global variables are inserted in default context
69 pub globals: HashMap<Rc<str>, Val>,78 pub globals: HashMap<Rc<str>, Val>,
70 /// Used to resolve file locations/contents79 /// Used to resolve file locations/contents
71 pub import_resolver: Box<dyn ImportResolver>,80 pub import_resolver: Box<dyn ImportResolver>,
81 /// Used in manifestification functions
82 pub manifest_format: ManifestFormat,
83 /// Used for bindings
84 pub trace_format: Box<dyn TraceFormat>,
72}85}
73impl Default for EvaluationSettings {86impl Default for EvaluationSettings {
74 fn default() -> Self {87 fn default() -> Self {
77 max_trace: 20,90 max_trace: 20,
78 globals: Default::default(),91 globals: Default::default(),
79 ext_vars: Default::default(),92 ext_vars: Default::default(),
93 tla_vars: Default::default(),
80 import_resolver: Box::new(DummyImportResolver),94 import_resolver: Box::new(DummyImportResolver),
95 manifest_format: ManifestFormat::Json(4),
96 trace_format: Box::new(CompactFormat {
97 padding: 4,
98 resolver: trace::PathResolver::Absolute,
99 }),
81 }100 }
82 }101 }
83}102}
114 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))133 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))
115}134}
116pub fn create_error(err: Error) -> LocError {135pub fn create_error(err: Error) -> LocError {
117 with_state(|s| s.error(err))136 LocError(err, StackTrace(vec![]))
118}137}
119pub fn create_error_result<T>(err: Error) -> Result<T> {138pub fn create_error_result<T>(err: Error) -> Result<T> {
120 Err(with_state(|s| s.error(err)))139 Err(LocError(err, StackTrace(vec![])))
121}140}
122pub(crate) fn push<T>(141pub(crate) fn push<T>(
123 e: &Option<ExprLocation>,142 e: &Option<ExprLocation>,
134/// Maintains stack trace and import resolution153/// Maintains stack trace and import resolution
135#[derive(Default, Clone)]154#[derive(Default, Clone)]
136pub struct EvaluationState(Rc<EvaluationStateInternals>);155pub struct EvaluationState(Rc<EvaluationStateInternals>);
156
137impl EvaluationState {157impl EvaluationState {
138 fn data(&self) -> Ref<EvaluationData> {
139 self.0.data.borrow()
140 }
141 fn data_mut(&self) -> RefMut<EvaluationData> {
142 self.0.data.borrow_mut()
143 }
144 pub fn settings(&self) -> Ref<EvaluationSettings> {
145 self.0.settings.borrow()
146 }
147 pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {
148 self.0.settings.borrow_mut()
149 }
150
151 pub fn evaluate_file_to_json(&self, path: &PathBuf) -> std::result::Result<Rc<str>, LocError> {
152 self.import_file(&PathBuf::new(), &path)
153 .and_then(|v| v.into_json(4))
154 }
155 pub fn evaluate_snippet_to_json(
156 &self,
157 path: &PathBuf,
158 snippet: &str,
159 ) -> std::result::Result<Rc<str>, LocError> {
160 self.parse_evaluate_raw(Rc::new(path.clone()), snippet)
161 .and_then(|v| v.into_json(4))
162 }
163
164 /// Parses and adds file to loaded158 /// Parses and adds file to loaded
165 pub fn add_file(&self, path: Rc<PathBuf>, source_code: Rc<str>) -> Result<()> {159 pub fn add_file(&self, path: Rc<PathBuf>, source_code: Rc<str>) -> Result<()> {
166 self.add_parsed_file(160 self.add_parsed_file(
211 offset_to_location(&self.get_source(file).unwrap(), locs)205 offset_to_location(&self.get_source(file).unwrap(), locs)
212 }206 }
213207
214 pub fn evaluate_file(&self, name: &PathBuf) -> Result<Val> {
215 self.run_in_state(|| {
216 let expr: LocExpr = {
217 let ro_map = &self.data().files;
218 let value = ro_map
219 .get(name)
220 .unwrap_or_else(|| panic!("file not added: {:?}", name));
221 if value.evaluated.is_some() {
222 return Ok(value.evaluated.clone().unwrap());
223 }
224 value.parsed.clone()
225 };
226 let value = evaluate(self.create_default_context()?, &expr)?;
227 {
228 self.0
229 .data
230 .borrow_mut()
231 .files
232 .get_mut(name)
233 .unwrap()
234 .evaluated
235 .replace(value.clone());
236 }
237 Ok(value)
238 })
239 }
240 pub fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
241 Ok(self.settings().import_resolver.resolve_file(from, path)?)
242 }
243 pub fn load_file_contents(&self, path: &PathBuf) -> Result<Rc<str>> {
244 Ok(self.settings().import_resolver.load_file_contents(path)?)
245 }
246 pub(crate) fn import_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Val> {208 pub(crate) fn import_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Val> {
247 let file_path = self.resolve_file(from, path)?;209 let file_path = self.resolve_file(from, path)?;
248 {210 {
249 let files = &self.data().files;211 let files = &self.data().files;
250 if files.contains_key(&file_path) {212 if files.contains_key(&file_path) {
251 return self.evaluate_file(&file_path);213 return self.evaluate_loaded_file_raw(&file_path);
252 }214 }
253 }215 }
254 let contents = self.load_file_contents(&file_path)?;216 let contents = self.load_file_contents(&file_path)?;
255 self.add_file(file_path.clone(), contents)?;217 self.add_file(file_path.clone(), contents)?;
256 self.evaluate_file(&file_path)218 self.evaluate_loaded_file_raw(&file_path)
257 }219 }
258 pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<str>> {220 pub(crate) fn import_file_str(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<str>> {
259 let path = self.resolve_file(from, path)?;221 let path = self.resolve_file(from, path)?;
264 Ok(self.data().str_files.get(&path).cloned().unwrap())226 Ok(self.data().str_files.get(&path).cloned().unwrap())
265 }227 }
266228
267 /// Parses and evaluates snippet229 fn evaluate_loaded_file_raw(&self, name: &PathBuf) -> Result<Val> {
268 pub fn parse_evaluate_raw(&self, source: Rc<PathBuf>, code: &str) -> Result<Val> {
269 let parsed = parse(230 let expr: LocExpr = {
270 &code,231 let ro_map = &self.data().files;
271 &ParserSettings {232 let value = ro_map
272 file_name: source,233 .get(name)
234 .unwrap_or_else(|| panic!("file not added: {:?}", name));
273 loc_data: true,235 if let Some(ref evaluated) = value.evaluated {
236 return Ok(evaluated.clone());
274 },237 }
275 )238 value.parsed.clone()
276 .unwrap();239 };
240 let value = evaluate(self.create_default_context()?, &expr)?;
277 self.evaluate_raw(parsed)241 {
242 self.data_mut()
243 .files
244 .get_mut(name)
245 .unwrap()
246 .evaluated
247 .replace(value.clone());
248 }
249 Ok(value)
278 }250 }
279 /// Evaluates parsed expression
280 pub fn evaluate_raw(&self, code: LocExpr) -> Result<Val> {
281 self.run_in_state(|| evaluate(self.create_default_context()?, &code))
282 }
283251
284 /// Adds standard library global variable (std) to this evaluator252 /// Adds standard library global variable (std) to this evaluator
285 pub fn with_stdlib(&self) -> &Self {253 pub fn with_stdlib(&self) -> &Self {
292 builtin::get_parsed_stdlib(),260 builtin::get_parsed_stdlib(),
293 )261 )
294 .unwrap();262 .unwrap();
295 let val = self.evaluate_file(&std_path).unwrap();263 let val = self.evaluate_loaded_file_raw(&std_path).unwrap();
296 self.settings_mut().globals.insert("std".into(), val);264 self.settings_mut().globals.insert("std".into(), val);
297 });265 });
298 self266 self
321 {289 {
322 let mut data = self.data_mut();290 let mut data = self.data_mut();
323 let stack_depth = &mut data.stack_depth;291 let stack_depth = &mut data.stack_depth;
324 if *stack_depth > self.settings().max_stack {292 if *stack_depth > self.max_stack() {
325 // Error creation uses data, so i drop guard here293 // Error creation uses data, so i drop guard here
326 drop(data);294 drop(data);
327 return Err(self.error(Error::StackOverflow));295 return Err(create_error(Error::StackOverflow));
328 } else {296 } else {
329 *stack_depth += 1;297 *stack_depth += 1;
330 }298 }
331 }299 }
332 let result = f();300 let result = f();
333 self.data_mut().stack_depth -= 1;301 self.data_mut().stack_depth -= 1;
334 if let Err(mut err) = result {302 if let Err(mut err) = result {
335 (err.1).0.push(StackTraceElement(e.clone(), frame_desc()));303 (err.1).0.push(StackTraceElement {
304 location: e.clone(),
305 desc: frame_desc(),
306 });
336 return Err(err);307 return Err(err);
337 }308 }
338 result309 result
339 }310 }
340311
341 /// Creates error with stack trace
342 pub fn error(&self, err: Error) -> LocError {
343 LocError(err, StackTrace(vec![]))
344 }
345
346 /// Runs passed function in state (required, if function needs to modify stack trace)312 /// Runs passed function in state (required, if function needs to modify stack trace)
347 pub fn run_in_state<T>(&self, f: impl FnOnce() -> T) -> T {313 pub fn run_in_state<T>(&self, f: impl FnOnce() -> T) -> T {
348 EVAL_STATE.with(|v| {314 EVAL_STATE.with(|v| {
356 }322 }
357 result323 result
358 })324 })
325 }
326
327 pub fn stringify_err(&self, e: &LocError) -> String {
328 let mut out = String::new();
329 self.settings()
330 .trace_format
331 .write_trace(&mut out, self, e)
332 .unwrap();
333 out
334 }
335
336 pub fn manifest(&self, val: Val) -> Result<Rc<str>> {
337 Ok(match self.manifest_format() {
338 ManifestFormat::Yaml(padding) => val.into_yaml(padding)?,
339 ManifestFormat::Json(padding) => val.into_json(padding)?,
340 ManifestFormat::None => match val {
341 Val::Str(s) => s,
342 _ => return Err(create_error(Error::StringManifestOutputIsNotAString)),
343 },
344 })
345 }
346
347 /// If passed value is function - call with set TLA
348 pub fn with_tla(&self, val: Val) -> Result<Val> {
349 Ok(match val {
350 Val::Func(func) => func.evaluate_map(
351 self.create_default_context()?,
352 &self.settings().tla_vars,
353 true,
354 )?,
355 v => v,
356 })
357 }
358}
359
360/// Internals
361impl EvaluationState {
362 fn data(&self) -> Ref<EvaluationData> {
363 self.0.data.borrow()
364 }
365 fn data_mut(&self) -> RefMut<EvaluationData> {
366 self.0.data.borrow_mut()
367 }
368 pub fn settings(&self) -> Ref<EvaluationSettings> {
369 self.0.settings.borrow()
370 }
371 pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {
372 self.0.settings.borrow_mut()
373 }
374}
375
376/// Raw methods evaluates passed values, but not performs TLA execution
377impl EvaluationState {
378 pub fn evaluate_file_raw(&self, name: &PathBuf) -> Result<Val> {
379 self.import_file(&std::env::current_dir().expect("cwd"), &name)
380 }
381 pub fn evaluate_file_raw_nocwd(&self, name: &PathBuf) -> Result<Val> {
382 self.import_file(&PathBuf::from("."), &name)
383 }
384 /// Parses and evaluates snippet
385 pub fn evaluate_snippet_raw(&self, source: Rc<PathBuf>, code: Rc<str>) -> Result<Val> {
386 let parsed = parse(
387 &code,
388 &ParserSettings {
389 file_name: source.clone(),
390 loc_data: true,
391 },
392 )
393 .unwrap();
394 self.add_parsed_file(source, code, parsed.clone())?;
395 self.evaluate_expr_raw(parsed)
396 }
397 /// Evaluates parsed expression
398 pub fn evaluate_expr_raw(&self, code: LocExpr) -> Result<Val> {
399 evaluate(self.create_default_context()?, &code)
400 }
401}
402
403/// Settings utilities
404impl EvaluationState {
405 pub fn add_ext_var(&self, name: Rc<str>, value: Val) {
406 self.settings_mut().ext_vars.insert(name, value);
407 }
408 pub fn add_ext_str(&self, name: Rc<str>, value: Rc<str>) {
409 self.add_ext_var(name, Val::Str(value));
410 }
411 pub fn add_ext_code(&self, name: Rc<str>, code: Rc<str>) -> Result<()> {
412 let value =
413 self.evaluate_snippet_raw(Rc::new(PathBuf::from(format!("ext_code {}", name))), code)?;
414 self.add_ext_var(name, value);
415 Ok(())
416 }
417
418 pub fn add_tla(&self, name: Rc<str>, value: Val) {
419 self.settings_mut().tla_vars.insert(name, value);
420 }
421 pub fn add_tla_str(&self, name: Rc<str>, value: Rc<str>) {
422 self.add_tla(name, Val::Str(value));
423 }
424 pub fn add_tla_code(&self, name: Rc<str>, code: Rc<str>) -> Result<()> {
425 let value =
426 self.evaluate_snippet_raw(Rc::new(PathBuf::from(format!("tla_code {}", name))), code)?;
427 self.add_ext_var(name, value);
428 Ok(())
429 }
430
431 pub fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result<Rc<PathBuf>> {
432 Ok(self.settings().import_resolver.resolve_file(from, path)?)
433 }
434 pub fn load_file_contents(&self, path: &PathBuf) -> Result<Rc<str>> {
435 Ok(self.settings().import_resolver.load_file_contents(path)?)
436 }
437
438 pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {
439 Ref::map(self.settings(), |s| &*s.import_resolver)
440 }
441 pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {
442 self.settings_mut().import_resolver = resolver;
443 }
444
445 pub fn manifest_format(&self) -> ManifestFormat {
446 self.settings().manifest_format.clone()
447 }
448 pub fn set_manifest_format(&self, format: ManifestFormat) {
449 self.settings_mut().manifest_format = format;
450 }
451
452 pub fn trace_format(&self) -> Ref<dyn TraceFormat> {
453 Ref::map(self.settings(), |s| &*s.trace_format)
454 }
455 pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {
456 self.settings_mut().trace_format = format;
457 }
458
459 pub fn max_trace(&self) -> usize {
460 self.settings().max_trace
461 }
462 pub fn set_max_trace(&self, trace: usize) {
463 self.settings_mut().max_trace = trace;
464 }
465
466 pub fn max_stack(&self) -> usize {
467 self.settings().max_stack
468 }
469 pub fn set_max_stack(&self, trace: usize) {
470 self.settings_mut().max_stack = trace;
359 }471 }
360}472}
361473
393 state.with_stdlib();505 state.with_stdlib();
394 assert!(primitive_equals(506 assert!(primitive_equals(
395 &state507 &state
396 .parse_evaluate_raw(508 .evaluate_snippet_raw(
397 Rc::new(PathBuf::from("raw.jsonnet")),509 Rc::new(PathBuf::from("raw.jsonnet")),
398 r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#510 r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#.into()
399 )511 )
400 .unwrap(),512 .unwrap(),
401 &Val::Bool(true),513 &Val::Bool(true),
407 ($str: expr) => {519 ($str: expr) => {
408 EvaluationState::default()520 EvaluationState::default()
409 .with_stdlib()521 .with_stdlib()
410 .parse_evaluate_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str)522 .evaluate_snippet_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str.into())
411 .unwrap()523 .unwrap()
412 };524 };
413 }525 }
417 evaluator.with_stdlib();529 evaluator.with_stdlib();
418 evaluator.run_in_state(|| {530 evaluator.run_in_state(|| {
419 evaluator531 evaluator
420 .parse_evaluate_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str)532 .evaluate_snippet_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str.into())
421 .unwrap()533 .unwrap()
422 .into_json(0)534 .into_json(0)
423 .unwrap()535 .unwrap()