difftreelog
refactor simplify and unify builtins
in: master
14 files changed
cmds/jrsonnet/src/main.rsdiffbeforeafterboth83 std::process::exit(0);83 std::process::exit(0);84 };84 };858586 let success;86 let success = if let Some(size) = opts.debug.os_stack {87 if let Some(size) = opts.debug.os_stack {88 success = std::thread::Builder::new()87 std::thread::Builder::new()89 .stack_size(size * 1024 * 1024)88 .stack_size(size * 1024 * 1024)90 .spawn(|| main_catch(opts))89 .spawn(|| main_catch(opts))91 .expect("new thread spawned")90 .expect("new thread spawned")92 .join()91 .join()93 .expect("thread finished successfully");92 .expect("thread finished successfully")94 } else {93 } else {95 success = main_catch(opts)94 main_catch(opts)96 }95 };97 if !success {96 if !success {98 std::process::exit(1);97 std::process::exit(1);99 }98 }crates/jrsonnet-evaluator/src/builtin/format.rsdiffbeforeafterboth578 }578 }579 ConvTypeV::Char => match value.clone() {579 ConvTypeV::Char => match value.clone() {580 Val::Num(n) => tmp_out.push(580 Val::Num(n) => tmp_out581 std::char::from_u32(n as u32)581 .push(std::char::from_u32(n as u32).ok_or(InvalidUnicodeCodepointGot(n as u32))?),582 .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,583 ),584 Val::Str(s) => {582 Val::Str(s) => {585 if s.chars().count() != 1 {583 if s.chars().count() != 1 {crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth1use crate::function::StaticBuiltin;1use crate::typed::{Any, Either, Null, PositiveF64, VecVal, M1};2use crate::typed::{Any, Either, Null, PositiveF64, VecVal, M1};2use crate::{self as jrsonnet_evaluator, ObjValue};3use crate::{self as jrsonnet_evaluator, ObjValue};3use crate::{4use crate::{11use format::{format_arr, format_obj};11use format::{format_arr, format_obj};12use gcmodule::Cc;12use gcmodule::Cc;13use jrsonnet_interner::IStr;13use jrsonnet_interner::IStr;14use jrsonnet_parser::{ArgsDesc, ExprLocation};14use jrsonnet_parser::ExprLocation;15use serde::Deserialize;15use serde::Deserialize;16use serde_yaml::DeserializingQuirks;16use serde_yaml::DeserializingQuirks;17use std::collections::HashMap;17use std::{18use std::convert::{TryFrom, TryInto};18 collections::HashMap,19 convert::{TryFrom, TryInto},20 path::PathBuf,21 rc::Rc,22};231924pub mod stdlib;20pub mod stdlib;25pub use stdlib::*;21pub use stdlib::*;322833pub fn std_format(str: IStr, vals: Val) -> Result<String> {29pub fn std_format(str: IStr, vals: Val) -> Result<String> {34 push_frame(30 push_frame(35 &ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0),31 None,36 || format!("std.format of {}", str),32 || format!("std.format of {}", str),37 || {33 || {38 Ok(match vals {34 Ok(match vals {76 }72 }77}73}7879type Builtin = fn(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val>;807481type BuiltinsType = HashMap<Box<str>, Builtin>;75type BuiltinsType = HashMap<IStr, &'static dyn StaticBuiltin>;827683thread_local! {77thread_local! {84 static BUILTINS: BuiltinsType = {78 pub static BUILTINS: BuiltinsType = {85 [79 [86 ("length".into(), builtin_length as Builtin),80 ("length".into(), builtin_length::INST),87 ("type".into(), builtin_type),81 ("type".into(), builtin_type::INST),88 ("makeArray".into(), builtin_make_array),82 ("makeArray".into(), builtin_make_array::INST),89 ("codepoint".into(), builtin_codepoint),83 ("codepoint".into(), builtin_codepoint::INST),90 ("objectFieldsEx".into(), builtin_object_fields_ex),84 ("objectFieldsEx".into(), builtin_object_fields_ex::INST),91 ("objectHasEx".into(), builtin_object_has_ex),85 ("objectHasEx".into(), builtin_object_has_ex::INST),92 ("slice".into(), builtin_slice),86 ("slice".into(), builtin_slice::INST),93 ("substr".into(), builtin_substr),87 ("substr".into(), builtin_substr::INST),94 ("primitiveEquals".into(), builtin_primitive_equals),88 ("primitiveEquals".into(), builtin_primitive_equals::INST),95 ("equals".into(), builtin_equals),89 ("equals".into(), builtin_equals::INST),96 ("modulo".into(), builtin_modulo),90 ("modulo".into(), builtin_modulo::INST),97 ("mod".into(), builtin_mod),91 ("mod".into(), builtin_mod::INST),98 ("floor".into(), builtin_floor),92 ("floor".into(), builtin_floor::INST),99 ("ceil".into(), builtin_ceil),93 ("ceil".into(), builtin_ceil::INST),100 ("log".into(), builtin_log),94 ("log".into(), builtin_log::INST),101 ("pow".into(), builtin_pow),95 ("pow".into(), builtin_pow::INST),102 ("sqrt".into(), builtin_sqrt),96 ("sqrt".into(), builtin_sqrt::INST),103 ("sin".into(), builtin_sin),97 ("sin".into(), builtin_sin::INST),104 ("cos".into(), builtin_cos),98 ("cos".into(), builtin_cos::INST),105 ("tan".into(), builtin_tan),99 ("tan".into(), builtin_tan::INST),106 ("asin".into(), builtin_asin),100 ("asin".into(), builtin_asin::INST),107 ("acos".into(), builtin_acos),101 ("acos".into(), builtin_acos::INST),108 ("atan".into(), builtin_atan),102 ("atan".into(), builtin_atan::INST),109 ("exp".into(), builtin_exp),103 ("exp".into(), builtin_exp::INST),110 ("mantissa".into(), builtin_mantissa),104 ("mantissa".into(), builtin_mantissa::INST),111 ("exponent".into(), builtin_exponent),105 ("exponent".into(), builtin_exponent::INST),112 ("extVar".into(), builtin_ext_var),106 ("extVar".into(), builtin_ext_var::INST),113 ("native".into(), builtin_native),107 ("native".into(), builtin_native::INST),114 ("filter".into(), builtin_filter),108 ("filter".into(), builtin_filter::INST),115 ("map".into(), builtin_map),109 ("map".into(), builtin_map::INST),116 ("flatMap".into(), builtin_flatmap),110 ("flatMap".into(), builtin_flatmap::INST),117 ("foldl".into(), builtin_foldl),111 ("foldl".into(), builtin_foldl::INST),118 ("foldr".into(), builtin_foldr),112 ("foldr".into(), builtin_foldr::INST),119 ("sortImpl".into(), builtin_sort_impl),113 ("sort".into(), builtin_sort::INST),120 ("format".into(), builtin_format),114 ("format".into(), builtin_format::INST),121 ("range".into(), builtin_range),115 ("range".into(), builtin_range::INST),122 ("char".into(), builtin_char),116 ("char".into(), builtin_char::INST),123 ("encodeUTF8".into(), builtin_encode_utf8),117 ("encodeUTF8".into(), builtin_encode_utf8::INST),124 ("decodeUTF8".into(), builtin_decode_utf8),118 ("decodeUTF8".into(), builtin_decode_utf8::INST),125 ("md5".into(), builtin_md5),119 ("md5".into(), builtin_md5::INST),126 ("base64".into(), builtin_base64),120 ("base64".into(), builtin_base64::INST),127 ("base64DecodeBytes".into(), builtin_base64_decode_bytes),121 ("base64DecodeBytes".into(), builtin_base64_decode_bytes::INST),128 ("base64Decode".into(), builtin_base64_decode),122 ("base64Decode".into(), builtin_base64_decode::INST),129 ("trace".into(), builtin_trace),123 ("trace".into(), builtin_trace::INST),130 ("join".into(), builtin_join),124 ("join".into(), builtin_join::INST),131 ("escapeStringJson".into(), builtin_escape_string_json),125 ("escapeStringJson".into(), builtin_escape_string_json::INST),132 ("manifestJsonEx".into(), builtin_manifest_json_ex),126 ("manifestJsonEx".into(), builtin_manifest_json_ex::INST),133 ("manifestYamlDocImpl".into(), builtin_manifest_yaml_doc),127 ("manifestYamlDoc".into(), builtin_manifest_yaml_doc::INST),134 ("reverse".into(), builtin_reverse),128 ("reverse".into(), builtin_reverse::INST),135 ("id".into(), builtin_id),129 ("id".into(), builtin_id::INST),136 ("strReplace".into(), builtin_str_replace),130 ("strReplace".into(), builtin_str_replace::INST),137 ("splitLimit".into(), builtin_splitlimit),131 ("splitLimit".into(), builtin_splitlimit::INST),138 ("parseJson".into(), builtin_parse_json),132 ("parseJson".into(), builtin_parse_json::INST),139 ("parseYaml".into(), builtin_parse_yaml),133 ("parseYaml".into(), builtin_parse_yaml::INST),140 ("asciiUpper".into(), builtin_ascii_upper),134 ("asciiUpper".into(), builtin_ascii_upper::INST),141 ("asciiLower".into(), builtin_ascii_lower),135 ("asciiLower".into(), builtin_ascii_lower::INST),142 ("member".into(), builtin_member),136 ("member".into(), builtin_member::INST),143 ("count".into(), builtin_count),137 ("count".into(), builtin_count::INST),144 ].iter().cloned().collect()138 ].iter().cloned().collect()145 };139 };146}140}147141148#[jrsonnet_macros::builtin]142#[jrsonnet_macros::builtin]149fn builtin_length(x: Either<IStr, Either<VecVal, ObjValue>>) -> Result<usize> {143fn builtin_length(x: Either<IStr, Either<VecVal, Either<ObjValue, Cc<FuncVal>>>>) -> Result<usize> {150 Ok(match x {144 Ok(match x {151 Either::Left(x) => x.len(),145 Either::Left(x) => x.len(),152 Either::Right(Either::Left(x)) => x.0.len(),146 Either::Right(Either::Left(x)) => x.0.len(),153 Either::Right(Either::Right(x)) => x147 Either::Right(Either::Right(Either::Left(x))) => x154 .fields_visibility()148 .fields_visibility()155 .into_iter()149 .into_iter()156 .filter(|(_k, v)| *v)150 .filter(|(_k, v)| *v)157 .count(),151 .count(),152 Either::Right(Either::Right(Either::Right(f))) => f.args_len(),158 })153 })159}154}160155167fn builtin_make_array(sz: usize, func: Cc<FuncVal>) -> Result<VecVal> {162fn builtin_make_array(sz: usize, func: Cc<FuncVal>) -> Result<VecVal> {168 let mut out = Vec::with_capacity(sz);163 let mut out = Vec::with_capacity(sz);169 for i in 0..sz {164 for i in 0..sz {170 out.push(func.evaluate_values(&[Val::Num(i as f64)])?)165 out.push(func.evaluate_simple(&[i as f64].as_slice())?)171 }166 }172 Ok(VecVal(out))167 Ok(VecVal(out))173}168}354349355#[jrsonnet_macros::builtin]350#[jrsonnet_macros::builtin]356fn builtin_filter(func: Cc<FuncVal>, arr: ArrValue) -> Result<ArrValue> {351fn builtin_filter(func: Cc<FuncVal>, arr: ArrValue) -> Result<ArrValue> {357 arr.filter(|val| bool::try_from(func.evaluate_values(&[val.clone()])?))352 arr.filter(|val| bool::try_from(func.evaluate_simple(&[Any(val.clone())].as_slice())?))358}353}359354360#[jrsonnet_macros::builtin]355#[jrsonnet_macros::builtin]361fn builtin_map(func: Cc<FuncVal>, arr: ArrValue) -> Result<ArrValue> {356fn builtin_map(func: Cc<FuncVal>, arr: ArrValue) -> Result<ArrValue> {362 arr.map(|val| func.evaluate_values(&[val]))357 arr.map(|val| func.evaluate_simple(&[Any(val)].as_slice()))363}358}364359365#[jrsonnet_macros::builtin]360#[jrsonnet_macros::builtin]368 IndexableVal::Str(s) => {363 IndexableVal::Str(s) => {369 let mut out = String::new();364 let mut out = String::new();370 for c in s.chars() {365 for c in s.chars() {371 match func.evaluate_values(&[Val::Str(c.to_string().into())])? {366 match func.evaluate_simple(&[c.to_string()].as_slice())? {372 Val::Str(o) => out.push_str(&o),367 Val::Str(o) => out.push_str(&o),373 _ => throw!(RuntimeError(368 _ => throw!(RuntimeError(374 "in std.join all items should be strings".into()369 "in std.join all items should be strings".into()381 let mut out = Vec::new();376 let mut out = Vec::new();382 for el in a.iter() {377 for el in a.iter() {383 let el = el?;378 let el = el?;384 match func.evaluate_values(&[el])? {379 match func.evaluate_simple(&[Any(el)].as_slice())? {385 Val::Arr(o) => {380 Val::Arr(o) => {386 for oe in o.iter() {381 for oe in o.iter() {387 out.push(oe?)382 out.push(oe?)401fn builtin_foldl(func: Cc<FuncVal>, arr: ArrValue, init: Any) -> Result<Any> {396fn builtin_foldl(func: Cc<FuncVal>, arr: ArrValue, init: Any) -> Result<Any> {402 let mut acc = init.0;397 let mut acc = init.0;403 for i in arr.iter() {398 for i in arr.iter() {404 acc = func.evaluate_values(&[acc, i?])?;399 acc = func.evaluate_simple(&[Any(acc), Any(i?)].as_slice())?;405 }400 }406 Ok(Any(acc))401 Ok(Any(acc))407}402}410fn builtin_foldr(func: Cc<FuncVal>, arr: ArrValue, init: Any) -> Result<Any> {405fn builtin_foldr(func: Cc<FuncVal>, arr: ArrValue, init: Any) -> Result<Any> {411 let mut acc = init.0;406 let mut acc = init.0;412 for i in arr.iter().rev() {407 for i in arr.iter().rev() {413 acc = func.evaluate_values(&[i?, acc])?;408 acc = func.evaluate_simple(&[Any(i?), Any(acc)].as_slice())?;414 }409 }415 Ok(Any(acc))410 Ok(Any(acc))416}411}417412418#[jrsonnet_macros::builtin]413#[jrsonnet_macros::builtin]419#[allow(non_snake_case)]414#[allow(non_snake_case)]420fn builtin_sort_impl(arr: ArrValue, keyF: Cc<FuncVal>) -> Result<ArrValue> {415fn builtin_sort(arr: ArrValue, keyF: Option<Cc<FuncVal>>) -> Result<ArrValue> {421 if arr.len() <= 1 {416 if arr.len() <= 1 {422 return Ok(arr);417 return Ok(arr);423 }418 }424 Ok(ArrValue::Eager(sort::sort(arr.evaluated()?, &keyF)?))419 Ok(ArrValue::Eager(sort::sort(420 arr.evaluated()?,421 keyF.as_deref(),422 )?))425}423}426424443441444#[jrsonnet_macros::builtin]442#[jrsonnet_macros::builtin]445fn builtin_char(n: u32) -> Result<char> {443fn builtin_char(n: u32) -> Result<char> {446 Ok(std::char::from_u32(n as u32).ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?)444 Ok(std::char::from_u32(n as u32).ok_or(InvalidUnicodeCodepointGot(n as u32))?)447}445}448446449#[jrsonnet_macros::builtin]447#[jrsonnet_macros::builtin]466}464}467465468#[jrsonnet_macros::builtin]466#[jrsonnet_macros::builtin]469fn builtin_trace(#[location] loc: &ExprLocation, str: IStr, rest: Any) -> Result<Any> {467fn builtin_trace(#[location] loc: Option<&ExprLocation>, str: IStr, rest: Any) -> Result<Any> {470 eprint!("TRACE:");468 eprint!("TRACE:");469 if let Some(loc) = loc {471 with_state(|s| {470 with_state(|s| {472 let locs = s.map_source_locations(&loc.0, &[loc.1]);471 let locs = s.map_source_locations(&loc.0, &[loc.1]);473 eprint!(472 eprint!(476 locs[0].line475 locs[0].line477 );476 );478 });477 });478 }479 eprintln!(" {}", str);479 eprintln!(" {}", str);480 Ok(rest) as Result<Any>480 Ok(rest) as Result<Any>481}481}574#[jrsonnet_macros::builtin]574#[jrsonnet_macros::builtin]575fn builtin_manifest_yaml_doc(575fn builtin_manifest_yaml_doc(576 value: Any,576 value: Any,577 indent_array_in_object: bool,577 indent_array_in_object: Option<bool>,578 quote_keys: bool,578 quote_keys: Option<bool>,579) -> Result<String> {579) -> Result<String> {580 manifest_yaml_ex(580 manifest_yaml_ex(581 &value.0,581 &value.0,582 &ManifestYamlOptions {582 &ManifestYamlOptions {583 padding: " ",583 padding: " ",584 arr_element_padding: if indent_array_in_object { " " } else { "" },584 arr_element_padding: if indent_array_in_object.unwrap_or(false) {585 " "586 } else {587 ""588 },585 quote_keys,589 quote_keys: quote_keys.unwrap_or(true),586 },590 },587 )591 )588}592}650 Ok(count)654 Ok(count)651}655}652653pub fn call_builtin(654 context: Context,655 loc: &ExprLocation,656 name: &str,657 args: &ArgsDesc,658) -> Result<Val> {659 BUILTINS660 .with(|builtins| builtins.get(name).copied())661 .ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)662}663656crates/jrsonnet-evaluator/src/builtin/sort.rsdiffbeforeafterboth1use crate::{1use crate::{2 error::{Error, LocError, Result},2 error::{Error, LocError, Result},3 throw, FuncVal, Val,3 throw,4 typed::Any,5 FuncVal, Val,4};6};5use gcmodule::{Cc, Trace};7use gcmodule::{Cc, Trace};59 Ok(sort_type)61 Ok(sort_type)60}62}616362pub fn sort(values: Cc<Vec<Val>>, key_getter: &FuncVal) -> Result<Cc<Vec<Val>>> {64pub fn sort(values: Cc<Vec<Val>>, key_getter: Option<&FuncVal>) -> Result<Cc<Vec<Val>>> {63 if values.len() <= 1 {65 if values.len() <= 1 {64 return Ok(values);66 return Ok(values);65 }67 }66 if key_getter.is_ident() {68 if let Some(key_getter) = key_getter {69 // Slow path, user provided key getter67 let mut mvalues = (*values).clone();70 let mut vk = Vec::with_capacity(values.len());71 for value in values.iter() {72 vk.push((73 value.clone(),74 key_getter.evaluate_simple(&[Any(value.clone())].as_slice())?,75 ));76 }68 let sort_type = get_sort_type(&mut mvalues, |k| k)?;77 let sort_type = get_sort_type(&mut vk, |v| &mut v.1)?;69 match sort_type {78 match sort_type {70 SortKeyType::Number => mvalues.sort_by_key(|v| match v {79 SortKeyType::Number => vk.sort_by_key(|v| match v.1 {71 Val::Num(n) => NonNaNf64(*n),80 Val::Num(n) => NonNaNf64(n),72 _ => unreachable!(),81 _ => unreachable!(),73 }),82 }),74 SortKeyType::String => mvalues.sort_by_key(|v| match v {83 SortKeyType::String => vk.sort_by_key(|v| match &v.1 {75 Val::Str(s) => s.clone(),84 Val::Str(s) => s.clone(),76 _ => unreachable!(),85 _ => unreachable!(),77 }),86 }),78 SortKeyType::Unknown => unreachable!(),87 SortKeyType::Unknown => unreachable!(),79 };88 };80 Ok(Cc::new(mvalues))89 Ok(Cc::new(vk.into_iter().map(|v| v.0).collect()))81 } else {90 } else {91 // Fast path, identity key getter82 let mut vk = Vec::with_capacity(values.len());92 let mut mvalues = (*values).clone();83 for value in values.iter() {84 vk.push((value.clone(), key_getter.evaluate_values(&[value.clone()])?));85 }86 let sort_type = get_sort_type(&mut vk, |v| &mut v.1)?;93 let sort_type = get_sort_type(&mut mvalues, |k| k)?;87 match sort_type {94 match sort_type {88 SortKeyType::Number => vk.sort_by_key(|v| match v.1 {95 SortKeyType::Number => mvalues.sort_unstable_by_key(|v| match v {89 Val::Num(n) => NonNaNf64(n),96 Val::Num(n) => NonNaNf64(*n),90 _ => unreachable!(),97 _ => unreachable!(),91 }),98 }),92 SortKeyType::String => vk.sort_by_key(|v| match &v.1 {99 SortKeyType::String => mvalues.sort_unstable_by_key(|v| match v {93 Val::Str(s) => s.clone(),100 Val::Str(s) => s.clone(),94 _ => unreachable!(),101 _ => unreachable!(),95 }),102 }),96 SortKeyType::Unknown => unreachable!(),103 SortKeyType::Unknown => unreachable!(),97 };104 };98 Ok(Cc::new(vk.into_iter().map(|v| v.0).collect()))105 Ok(Cc::new(mvalues))99 }106 }100}107}101108crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth1use std::convert::TryFrom;1use std::convert::TryFrom;223use crate::{3use crate::{4 builtin::std_slice,4 builtin::{std_slice, BUILTINS},5 error::Error::*,5 error::Error::*,6 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},6 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},7 gc::TraceBox,7 gc::TraceBox,192 Ok(match field_name {192 Ok(match field_name {193 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),193 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),194 jrsonnet_parser::FieldName::Dyn(expr) => push_frame(194 jrsonnet_parser::FieldName::Dyn(expr) => push_frame(195 &expr.1,195 Some(&expr.1),196 || "evaluating field name".to_string(),196 || "evaluating field name".to_string(),197 || {197 || {198 let value = evaluate(context, expr)?;198 let value = evaluate(context, expr)?;442 context: Context,442 context: Context,443 value: &LocExpr,443 value: &LocExpr,444 args: &ArgsDesc,444 args: &ArgsDesc,445 loc: &ExprLocation,445 loc: Option<&ExprLocation>,446 tailstrict: bool,446 tailstrict: bool,447) -> Result<Val> {447) -> Result<Val> {448 let value = evaluate(context.clone(), value)?;448 let value = evaluate(context.clone(), value)?;463 let value = &assertion.0;463 let value = &assertion.0;464 let msg = &assertion.1;464 let msg = &assertion.1;465 let assertion_result = push_frame(465 let assertion_result = push_frame(466 &value.1,466 Some(&value.1),467 || "assertion condition".to_owned(),467 || "assertion condition".to_owned(),468 || bool::try_from(evaluate(context.clone(), value)?),468 || bool::try_from(evaluate(context.clone(), value)?),469 )?;469 )?;470 if !assertion_result {470 if !assertion_result {471 push_frame(471 push_frame(472 &value.1,472 Some(&value.1),473 || "assertion failure".to_owned(),473 || "assertion failure".to_owned(),474 || {474 || {475 if let Some(msg) = msg {475 if let Some(msg) = msg {519 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,519 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,520 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,520 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,521 Var(name) => push_frame(521 Var(name) => push_frame(522 loc,522 Some(loc),523 || format!("variable <{}> access", name),523 || format!("variable <{}> access", name),524 || context.binding(name.clone())?.evaluate(),524 || context.binding(name.clone())?.evaluate(),525 )?,525 )?,528 (Val::Obj(v), Val::Str(s)) => {528 (Val::Obj(v), Val::Str(s)) => {529 let sn = s.clone();529 let sn = s.clone();530 push_frame(530 push_frame(531 loc,531 Some(loc),532 || format!("field <{}> access", sn),532 || format!("field <{}> access", sn),533 || {533 || {534 if let Some(v) = v.get(s.clone())? {534 if let Some(v) = v.get(s.clone())? {624 &evaluate(context.clone(), s)?,624 &evaluate(context.clone(), s)?,625 &Val::Obj(evaluate_object(context, t)?),625 &Val::Obj(evaluate_object(context, t)?),626 )?,626 )?,627 Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,627 Apply(value, args, tailstrict) => {628 evaluate_apply(context, value, args, Some(loc), *tailstrict)?629 }628 Function(params, body) => {630 Function(params, body) => {629 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())631 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())630 }632 }631 Intrinsic(name) => Val::Func(Cc::new(FuncVal::Intrinsic(name.clone()))),633 Intrinsic(name) => Val::Func(Cc::new(FuncVal::StaticBuiltin(634 BUILTINS635 .with(|b| b.get(name).copied())636 .ok_or_else(|| IntrinsicNotFound(name.clone()))?,637 ))),632 AssertExpr(assert, returned) => {638 AssertExpr(assert, returned) => {633 evaluate_assert(context.clone(), assert)?;639 evaluate_assert(context.clone(), assert)?;634 evaluate(context, returned)?640 evaluate(context, returned)?635 }641 }636 ErrorStmt(e) => push_frame(642 ErrorStmt(e) => push_frame(637 loc,643 Some(loc),638 || "error statement".to_owned(),644 || "error statement".to_owned(),639 || throw!(RuntimeError(IStr::try_from(evaluate(context, e)?)?,)),645 || throw!(RuntimeError(IStr::try_from(evaluate(context, e)?)?,)),640 )?,646 )?,644 cond_else,650 cond_else,645 } => {651 } => {646 if push_frame(652 if push_frame(647 loc,653 Some(loc),648 || "if condition".to_owned(),654 || "if condition".to_owned(),649 || bool::try_from(evaluate(context.clone(), &cond.0)?),655 || bool::try_from(evaluate(context.clone(), &cond.0)?),650 )? {656 )? {683 let mut import_location = tmp.to_path_buf();689 let mut import_location = tmp.to_path_buf();684 import_location.pop();690 import_location.pop();685 push_frame(691 push_frame(686 loc,692 Some(loc),687 || format!("import {:?}", path),693 || format!("import {:?}", path),688 || with_state(|s| s.import_file(&import_location, path)),694 || with_state(|s| s.import_file(&import_location, path)),689 )?695 )?crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth1use crate::{1use crate::{2 error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw, Context, FutureWrapper,2 error::{Error::*, LocError},3 evaluate, evaluate_named,4 gc::TraceBox,5 throw,6 typed::Typed,3 GcHashMap, LazyVal, LazyValValue, Result, Val,7 Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val,4};8};5use gcmodule::Trace;9use gcmodule::Trace;6use jrsonnet_interner::IStr;10use jrsonnet_interner::IStr;7use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};11use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};8use std::collections::HashMap;12use std::{borrow::Cow, collections::HashMap, convert::TryFrom};91310const NO_DEFAULT_CONTEXT: &str =11 "no default context set for call with defined default parameter value";1213#[derive(Trace)]14#[derive(Trace)]14struct EvaluateLazyVal {15struct EvaluateLazyVal {15 context: Context,16 context: Context,21 }22 }22}23}232425pub trait ArgLike {26 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;27}28impl ArgLike for &LocExpr {29 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {30 Ok(if tailstrict {31 LazyVal::new_resolved(evaluate(ctx, self)?)32 } else {33 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {34 context: ctx,35 expr: (*self).clone(),36 })))37 })38 }39}40impl<T> ArgLike for T41where42 T: Typed + Clone,43 Val: TryFrom<T, Error = LocError>,44{45 fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {46 let val: Val = Val::try_from(self.clone())?;47 Ok(LazyVal::new_resolved(val))48 }49}50pub enum TlaArg {51 String(IStr),52 Code(LocExpr),53 Val(Val),54}55impl ArgLike for TlaArg {56 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {57 match self {58 TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),59 TlaArg::Code(code) => Ok(if tailstrict {60 LazyVal::new_resolved(evaluate(ctx, code)?)61 } else {62 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {63 context: ctx,64 expr: code.clone(),65 })))66 }),67 TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),68 }69 }70}7172pub trait ArgsLike {73 fn unnamed_len(&self) -> usize;74 fn unnamed_iter(75 &self,76 ctx: Context,77 tailstrict: bool,78 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,79 ) -> Result<()>;80 fn named_iter(81 &self,82 ctx: Context,83 tailstrict: bool,84 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,85 ) -> Result<()>;86 fn named_names(&self, handler: &mut dyn FnMut(&IStr));87}8889impl ArgsLike for ArgsDesc {90 fn unnamed_len(&self) -> usize {91 self.unnamed.len()92 }9394 fn unnamed_iter(95 &self,96 ctx: Context,97 tailstrict: bool,98 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,99 ) -> Result<()> {100 for (id, arg) in self.unnamed.iter().enumerate() {101 handler(102 id,103 if tailstrict {104 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)105 } else {106 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {107 context: ctx.clone(),108 expr: arg.clone(),109 })))110 },111 )?;112 }113 Ok(())114 }115116 fn named_iter(117 &self,118 ctx: Context,119 tailstrict: bool,120 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,121 ) -> Result<()> {122 for (name, arg) in self.named.iter() {123 handler(124 name,125 if tailstrict {126 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)127 } else {128 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {129 context: ctx.clone(),130 expr: arg.clone(),131 })))132 },133 )?;134 }135 Ok(())136 }137138 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {139 for (name, _) in self.named.iter() {140 handler(name)141 }142 }143}144145impl<A: ArgLike> ArgsLike for [(IStr, A)] {146 fn unnamed_len(&self) -> usize {147 0148 }149150 fn unnamed_iter(151 &self,152 _ctx: Context,153 _tailstrict: bool,154 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,155 ) -> Result<()> {156 Ok(())157 }158159 fn named_iter(160 &self,161 ctx: Context,162 tailstrict: bool,163 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,164 ) -> Result<()> {165 for (name, val) in self.iter() {166 handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;167 }168 Ok(())169 }170171 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {172 for (name, _) in self.iter() {173 handler(name);174 }175 }176}177178impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {179 fn unnamed_len(&self) -> usize {180 0181 }182183 fn unnamed_iter(184 &self,185 _ctx: Context,186 _tailstrict: bool,187 _handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,188 ) -> Result<()> {189 Ok(())190 }191192 fn named_iter(193 &self,194 ctx: Context,195 tailstrict: bool,196 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,197 ) -> Result<()> {198 for (name, value) in self.iter() {199 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;200 }201 Ok(())202 }203204 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {205 for (name, _) in self.iter() {206 handler(name);207 }208 }209}210211impl<A: ArgLike> ArgsLike for [A] {212 fn unnamed_len(&self) -> usize {213 self.len()214 }215216 fn unnamed_iter(217 &self,218 ctx: Context,219 tailstrict: bool,220 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,221 ) -> Result<()> {222 for (i, arg) in self.iter().enumerate() {223 handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;224 }225 Ok(())226 }227228 fn named_iter(229 &self,230 _ctx: Context,231 _tailstrict: bool,232 _handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,233 ) -> Result<()> {234 Ok(())235 }236237 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}238}239impl<A: ArgLike> ArgsLike for &[A] {240 fn unnamed_len(&self) -> usize {241 (*self).unnamed_len()242 }243244 fn unnamed_iter(245 &self,246 ctx: Context,247 tailstrict: bool,248 handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,249 ) -> Result<()> {250 (*self).unnamed_iter(ctx, tailstrict, handler)251 }252253 fn named_iter(254 &self,255 ctx: Context,256 tailstrict: bool,257 handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,258 ) -> Result<()> {259 (*self).named_iter(ctx, tailstrict, handler)260 }261262 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {263 (*self).named_names(handler)264 }265}26624/// Creates correct [context](Context) for function body evaluation returning error on invalid call.267/// Creates correct [context](Context) for function body evaluation returning error on invalid call.25///268///33 ctx: Context,276 ctx: Context,34 body_ctx: Context,277 body_ctx: Context,35 params: &ParamsDesc,278 params: &ParamsDesc,36 args: &ArgsDesc,279 args: &dyn ArgsLike,37 tailstrict: bool,280 tailstrict: bool,38) -> Result<Context> {281) -> Result<Context> {39 let mut passed_args = GcHashMap::with_capacity(params.len());282 let mut passed_args = GcHashMap::with_capacity(params.len());40 if args.unnamed.len() > params.len() {283 if args.unnamed_len() > params.len() {41 throw!(TooManyArgsFunctionHas(params.len()))284 throw!(TooManyArgsFunctionHas(params.len()))42 }285 }4328644 let mut filled_args = 0;287 let mut filled_args = 0;4528846 for (id, arg) in args.unnamed.iter().enumerate() {289 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {47 let name = params[id].0.clone();290 let name = params[id].0.clone();48 passed_args.insert(291 passed_args.insert(name, arg);49 name,50 if tailstrict {51 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)52 } else {53 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {54 context: ctx.clone(),55 expr: arg.clone(),56 })))57 },58 );59 filled_args += 1;292 filled_args += 1;60 }293 Ok(())294 })?;6129562 for (name, value) in args.named.iter() {296 args.named_iter(ctx, tailstrict, &mut |name, value| {63 // FIXME: O(n) for arg existence check297 // FIXME: O(n) for arg existence check64 if !params.iter().any(|p| &p.0 == name) {298 if !params.iter().any(|p| &p.0 == name) {65 throw!(UnknownFunctionParameter((name as &str).to_owned()));299 throw!(UnknownFunctionParameter((name as &str).to_owned()));66 }300 }67 if passed_args301 if passed_args.insert(name.clone(), value).is_some() {68 .insert(69 name.clone(),70 if tailstrict {71 LazyVal::new_resolved(evaluate(ctx.clone(), value)?)72 } else {73 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {74 context: ctx.clone(),75 expr: value.clone(),76 })))77 },78 )79 .is_some()80 {81 throw!(BindingParameterASecondTime(name.clone()));302 throw!(BindingParameterASecondTime(name.clone()));82 }303 }83 filled_args += 1;304 filled_args += 1;84 }305 Ok(())306 })?;8530786 if filled_args < params.len() {308 if filled_args < params.len() {87 // Some args are unset, but maybe we have defaults for them309 // Some args are unset, but maybe we have defaults for them123345124 // Some args still wasn't filled346 // Some args still wasn't filled125 if filled_args != params.len() {347 if filled_args != params.len() {126 for param in params.iter().skip(args.unnamed.len()) {348 for param in params.iter().skip(args.unnamed_len()) {127 if !args.named.iter().any(|a| a.0 == param.0) {349 let mut found = false;350 args.named_names(&mut |name| {351 if name == ¶m.0 {352 found = true;353 }354 });355 if !found {128 throw!(FunctionParameterNotBoundInCall(param.0.clone()));356 throw!(FunctionParameterNotBoundInCall(param.0.clone()));129 }357 }130 }358 }141 }369 }142}370}143371372type BuiltinParamName = Cow<'static, str>;373144#[derive(Clone, Copy)]374#[derive(Clone)]145pub struct BuiltinParam {375pub struct BuiltinParam {146 pub name: &'static str,376 pub name: BuiltinParamName,147 pub has_default: bool,377 pub has_default: bool,148}378}149379380pub trait Builtin: Trace {381 fn name(&self) -> &str;382 fn params(&self) -> &[BuiltinParam];383 fn call(384 &self,385 context: Context,386 loc: Option<&ExprLocation>,387 args: &dyn ArgsLike,388 ) -> Result<Val>;389}390391pub trait StaticBuiltin: Builtin + Send + Sync392where393 Self: 'static,394{395 // In impl, to make it object safe:396 // const INST: &'static Self;397}398150/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead399/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead151///400///152/// ## Parameters401/// ## Parameters153/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)402/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)154/// * `params`: function parameters' definition403/// * `params`: function parameters' definition155/// * `args`: passed function arguments404/// * `args`: passed function arguments156/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily405/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily157pub fn parse_builtin_call<'k>(406pub fn parse_builtin_call(158 ctx: Context,407 ctx: Context,159 params: &'static [BuiltinParam],408 params: &[BuiltinParam],160 args: &'k ArgsDesc,409 args: &dyn ArgsLike,161 tailstrict: bool,410 tailstrict: bool,162) -> Result<GcHashMap<&'k str, LazyVal>> {411) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {163 let mut passed_args = GcHashMap::with_capacity(params.len());412 let mut passed_args = GcHashMap::with_capacity(params.len());164 if args.unnamed.len() > params.len() {413 if args.unnamed_len() > params.len() {165 throw!(TooManyArgsFunctionHas(params.len()))414 throw!(TooManyArgsFunctionHas(params.len()))166 }415 }167416168 let mut filled_args = 0;417 let mut filled_args = 0;169418170 for (id, arg) in args.unnamed.iter().enumerate() {419 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {171 let name = params[id].name;420 let name = params[id].name.clone();172 passed_args.insert(421 passed_args.insert(name, arg);173 name,174 if tailstrict {175 LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)176 } else {177 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {178 context: ctx.clone(),179 expr: arg.clone(),180 })))181 },182 );183 filled_args += 1;422 filled_args += 1;184 }423 Ok(())424 })?;185425186 for (name, value) in args.named.iter() {426 args.named_iter(ctx, tailstrict, &mut |name, arg| {187 // FIXME: O(n) for arg existence check427 // FIXME: O(n) for arg existence check188 if !params.iter().any(|p| p.name == name as &str) {428 let p = params429 .iter()430 .find(|p| p.name == name as &str)189 throw!(UnknownFunctionParameter((name as &str).to_owned()));431 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;190 }432 if passed_args.insert(p.name.clone(), arg).is_some() {191 if passed_args192 .insert(193 name,194 if tailstrict {195 LazyVal::new_resolved(evaluate(ctx.clone(), value)?)196 } else {197 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {198 context: ctx.clone(),199 expr: value.clone(),200 })))201 },202 )203 .is_some()204 {205 throw!(BindingParameterASecondTime(name.clone()));433 throw!(BindingParameterASecondTime(name.clone()));206 }434 }207 filled_args += 1;435 filled_args += 1;208 }436 Ok(())437 })?;209438210 if filled_args < params.len() {439 if filled_args < params.len() {211 for param in params.iter().filter(|p| p.has_default) {440 for param in params.iter().filter(|p| p.has_default) {217446218 // Some args still wasn't filled447 // Some args still wasn't filled219 if filled_args != params.len() {448 if filled_args != params.len() {220 for param in params.iter().skip(args.unnamed.len()) {449 for param in params.iter().skip(args.unnamed_len()) {221 if !args.named.iter().any(|a| &a.0 as &str == param.name) {450 let mut found = false;451 args.named_names(&mut |name| {452 if name as &str == ¶m.name as &str {453 found = true;454 }455 });456 if !found {222 throw!(FunctionParameterNotBoundInCall(param.name.into()));457 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));223 }458 }224 }459 }225 unreachable!();460 unreachable!();226 }461 }227 }462 }228 Ok(passed_args)463 Ok(passed_args)229}230231pub fn parse_function_call_map(232 ctx: Context,233 body_ctx: Option<Context>,234 params: &ParamsDesc,235 args: &HashMap<IStr, Val>,236 tailstrict: bool,237) -> Result<Context> {238 let mut out = GcHashMap::with_capacity(params.len());239 let mut positioned_args = vec![None; params.0.len()];240 for (name, val) in args.iter() {241 let idx = params242 .iter()243 .position(|p| *p.0 == **name)244 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;245246 if idx >= params.len() {247 throw!(TooManyArgsFunctionHas(params.len()));248 }249 if positioned_args[idx].is_some() {250 throw!(BindingParameterASecondTime(params[idx].0.clone()));251 }252 positioned_args[idx] = Some(val.clone());253 }254 // Fill defaults255 for (id, p) in params.iter().enumerate() {256 let val = if let Some(arg) = positioned_args[id].take() {257 LazyVal::new_resolved(arg)258 } else if let Some(default) = &p.1 {259 if tailstrict {260 LazyVal::new_resolved(evaluate(261 body_ctx.clone().expect(NO_DEFAULT_CONTEXT),262 default,263 )?)264 } else {265 let body_ctx = body_ctx.clone();266 let default = default.clone();267 #[derive(Trace)]268 struct EvaluateLazyVal {269 body_ctx: Option<Context>,270 default: LocExpr,271 }272 impl LazyValValue for EvaluateLazyVal {273 fn get(self: Box<Self>) -> Result<Val> {274 evaluate(275 self.body_ctx.clone().expect(NO_DEFAULT_CONTEXT),276 &self.default,277 )278 }279 }280 LazyVal::new(TraceBox(Box::new(EvaluateLazyVal { body_ctx, default })))281 }282 } else {283 throw!(FunctionParameterNotBoundInCall(p.0.clone()));284 };285 out.insert(p.0.clone(), val);286 }287288 Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None))289}290291pub fn place_args(body_ctx: Context, params: &ParamsDesc, args: &[Val]) -> Result<Context> {292 let mut out = GcHashMap::with_capacity(params.len());293 let mut positioned_args = vec![None; params.0.len()];294 for (id, arg) in args.iter().enumerate() {295 if id >= params.len() {296 throw!(TooManyArgsFunctionHas(params.len()));297 }298 positioned_args[id] = Some(arg);299 }300 // Fill defaults301 for (id, p) in params.iter().enumerate() {302 let val = if let Some(arg) = &positioned_args[id] {303 (*arg).clone()304 } else if let Some(default) = &p.1 {305 evaluate(body_ctx.clone(), default)?306 } else {307 throw!(FunctionParameterNotBoundInCall(p.0.clone()));308 };309 out.insert(p.0.clone(), LazyVal::new_resolved(val));310 }311312 Ok(body_ctx.extend(out, None, None, None))313}464}314465crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth25use error::{Error::*, LocError, Result, StackTraceElement};25use error::{Error::*, LocError, Result, StackTraceElement};26pub use evaluate::*;26pub use evaluate::*;27pub use function::parse_function_call;27pub use function::parse_function_call;28use function::TlaArg;28use gc::{GcHashMap, TraceBox};29use gc::{GcHashMap, TraceBox};29use gcmodule::{Cc, Trace};30use gcmodule::{Cc, Trace};30pub use import::*;31pub use import::*;77 /// Used for ext.native78 /// Used for ext.native78 pub ext_natives: HashMap<IStr, Cc<NativeCallback>>,79 pub ext_natives: HashMap<IStr, Cc<NativeCallback>>,79 /// TLA vars80 /// TLA vars80 pub tla_vars: HashMap<IStr, Val>,81 pub tla_vars: HashMap<IStr, TlaArg>,81 /// Global variables are inserted in default context82 /// Global variables are inserted in default context82 pub globals: HashMap<IStr, Val>,83 pub globals: HashMap<IStr, Val>,83 /// Used to resolve file locations/contents84 /// Used to resolve file locations/contents174 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))175 EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))175}176}176pub(crate) fn push_frame<T>(177pub(crate) fn push_frame<T>(177 e: &ExprLocation,178 e: Option<&ExprLocation>,178 frame_desc: impl FnOnce() -> String,179 frame_desc: impl FnOnce() -> String,179 f: impl FnOnce() -> Result<T>,180 f: impl FnOnce() -> Result<T>,180) -> Result<T> {181) -> Result<T> {203204204impl EvaluationState {205impl EvaluationState {205 /// Parses and adds file as loaded206 /// Parses and adds file as loaded206 pub fn add_file(&self, path: Rc<Path>, source_code: IStr) -> Result<()> {207 pub fn add_file(&self, path: Rc<Path>, source_code: IStr) -> Result<LocExpr> {207 self.add_parsed_file(208 path.clone(),209 source_code.clone(),210 parse(208 let parsed = parse(211 &source_code,209 &source_code,212 &ParserSettings {210 &ParserSettings {213 file_name: path.clone(),211 file_name: path.clone(),216 .map_err(|error| ImportSyntaxError {214 .map_err(|error| ImportSyntaxError {217 error: Box::new(error),215 error: Box::new(error),218 path: path.to_owned(),216 path: path.to_owned(),219 source_code,217 source_code: source_code.clone(),220 })?,218 })?;221 )?;219 self.add_parsed_file(path, source_code, parsed.clone())?;222220223 Ok(())221 Ok(parsed)224 }222 }225223226 pub fn reset_evaluation_state(&self, name: &Path) {224 pub fn reset_evaluation_state(&self, name: &Path) {341 /// Executes code creating a new stack frame339 /// Executes code creating a new stack frame342 pub fn push<T>(340 pub fn push<T>(343 &self,341 &self,344 e: &ExprLocation,342 e: Option<&ExprLocation>,345 frame_desc: impl FnOnce() -> String,343 frame_desc: impl FnOnce() -> String,346 f: impl FnOnce() -> Result<T>,344 f: impl FnOnce() -> Result<T>,347 ) -> Result<T> {345 ) -> Result<T> {364 }362 }365 if let Err(mut err) = result {363 if let Err(mut err) = result {366 err.trace_mut().0.push(StackTraceElement {364 err.trace_mut().0.push(StackTraceElement {367 location: Some(e.clone()),365 location: e.cloned(),368 desc: frame_desc(),366 desc: frame_desc(),369 });367 });370 return Err(err);368 return Err(err);506 Val::Func(func) => push_description_frame(504 Val::Func(func) => push_description_frame(507 || "during TLA call".to_owned(),505 || "during TLA call".to_owned(),508 || {506 || {509 func.evaluate_map(507 func.evaluate(510 self.create_default_context(),508 self.create_default_context(),509 None,511 &self.settings().tla_vars,510 &self.settings().tla_vars,512 true,511 true,513 )512 )583 pub fn add_tla(&self, name: IStr, value: Val) {582 pub fn add_tla(&self, name: IStr, value: Val) {584 self.settings_mut().tla_vars.insert(name, value);583 self.settings_mut()584 .tla_vars585 .insert(name, TlaArg::Val(value));585 }586 }586 pub fn add_tla_str(&self, name: IStr, value: IStr) {587 pub fn add_tla_str(&self, name: IStr, value: IStr) {587 self.add_tla(name, Val::Str(value));588 self.settings_mut()589 .tla_vars590 .insert(name, TlaArg::String(value));588 }591 }589 pub fn add_tla_code(&self, name: IStr, code: IStr) -> Result<()> {592 pub fn add_tla_code(&self, name: IStr, code: IStr) -> Result<()> {590 let value =593 let parsed = self.add_file(PathBuf::from(format!("tla_code {}", name)).into(), code)?;591 self.evaluate_snippet_raw(PathBuf::from(format!("tla_code {}", name)).into(), code)?;592 self.add_tla(name, value);594 self.settings_mut()595 .tla_vars596 .insert(name, TlaArg::Code(parsed));593 Ok(())597 Ok(())594 }598 }595599668 state.run_in_state(|| {672 state.run_in_state(|| {669 state673 state670 .push(674 .push(671 &ExprLocation(PathBuf::from("test1.jsonnet").into(), 10, 20),675 Some(&ExprLocation(PathBuf::from("test1.jsonnet").into(), 10, 20)),672 || "outer".to_owned(),676 || "outer".to_owned(),673 || {677 || {674 state.push(678 state.push(675 &ExprLocation(PathBuf::from("test2.jsonnet").into(), 30, 40),679 Some(&ExprLocation(PathBuf::from("test2.jsonnet").into(), 30, 40)),676 || "inner".to_owned(),680 || "inner".to_owned(),677 || Err(RuntimeError("".into()).into()),681 || Err(RuntimeError("".into()).into()),678 )?;682 )?;977 r#"std.parseJson('{"a": -1,"b": 1,"c": 3.141,"d": []}')"#,981 r#"std.parseJson('{"a": -1,"b": 1,"c": 3.141,"d": []}')"#,978 r#"{"a": -1,"b": 1,"c": 3.141,"d": []}"#982 r#"{"a": -1,"b": 1,"c": 3.141,"d": []}"#979 );983 );980 // TODO: this should in fact fail as is no proper JSON syntax981 assert_json!(982 r#"std.parseJson("{a:-1, b:1, c:3.141, d:[]}")"#,983 r#"{"a": -1,"b": 1,"c": 3.141,"d": []}"#984 );985 // TODO: this is also no valid JSON986 assert_json!(r#"std.parseJson('local x = 2; x * x')"#, r#"4"#);987 }984 }988985989 #[test]986 #[test]crates/jrsonnet-evaluator/src/native.rsdiffbeforeafterboth8use std::path::Path;8use std::path::Path;9use std::rc::Rc;9use std::rc::Rc;101011#[deprecated(note = "Use builtins instead")]11pub trait NativeCallbackHandler: Trace {12pub trait NativeCallbackHandler: Trace {12 fn call(&self, from: Rc<Path>, args: &[Val]) -> Result<Val>;13 fn call(&self, from: Rc<Path>, args: &[Val]) -> Result<Val>;13}14}crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth37 .into()37 .into()38 ))38 ))39 }39 }40 Ok(n as $ty)40 Ok(n as Self)41 }41 }42 _ => unreachable!(),42 _ => unreachable!(),43 }43 }249249250/// To be used in Vec<Any>250/// To be used in Vec<Any>251/// Regular Val can't be used here, because it has wrong TryFrom::Error type251/// Regular Val can't be used here, because it has wrong TryFrom::Error type252#[derive(Clone)]252pub struct Any(pub Val);253pub struct Any(pub Val);253254254impl Typed for Any {255impl Typed for Any {crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use crate::{1use crate::{2 builtin::{2 builtin::manifest::{3 call_builtin,4 manifest::{5 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType,3 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,6 ManifestYamlOptions,7 },4 },8 },9 cc_ptr_eq,5 cc_ptr_eq,10 error::{Error::*, LocError},6 error::{Error::*, LocError},11 evaluate,7 evaluate,12 function::{parse_function_call, parse_function_call_map, place_args},8 function::{parse_function_call, ArgsLike, Builtin, StaticBuiltin},13 gc::TraceBox,9 gc::TraceBox,14 native::NativeCallback,10 native::NativeCallback,15 throw, Context, ObjValue, Result,11 throw, Context, ObjValue, Result,16};12};17use gcmodule::{Cc, Trace};13use gcmodule::{Cc, Trace};18use jrsonnet_interner::IStr;14use jrsonnet_interner::IStr;19use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};15use jrsonnet_parser::{ExprLocation, LocExpr, ParamsDesc};20use jrsonnet_types::ValType;16use jrsonnet_types::ValType;21use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};17use std::{cell::RefCell, fmt::Debug, rc::Rc};221823pub trait LazyValValue: Trace {19pub trait LazyValValue: Trace {24 fn get(self: Box<Self>) -> Result<Val>;20 fn get(self: Box<Self>) -> Result<Val>;41 pub fn new_resolved(val: Val) -> Self {37 pub fn new_resolved(val: Val) -> Self {42 Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))38 Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))43 }39 }40 pub fn force(&self) -> Result<()> {41 self.evaluate()?;42 Ok(())43 }44 pub fn evaluate(&self) -> Result<Val> {44 pub fn evaluate(&self) -> Result<Val> {45 match &*self.0.borrow() {45 match &*self.0.borrow() {46 LazyValInternals::Computed(v) => return Ok(v.clone()),46 LazyValInternals::Computed(v) => return Ok(v.clone()),86 pub body: LocExpr,86 pub body: LocExpr,87}87}888889#[derive(Debug, Trace)]89#[derive(Trace)]90pub enum FuncVal {90pub enum FuncVal {91 /// Plain function implemented in jsonnet91 /// Plain function implemented in jsonnet92 Normal(FuncDesc),92 Normal(FuncDesc),93 /// Standard library function93 /// Standard library function94 Intrinsic(IStr),94 StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),9596 Builtin(TraceBox<dyn Builtin>),95 /// Library functions implemented in native97 /// Library functions implemented in native96 NativeExt(IStr, Cc<NativeCallback>),98 NativeExt(IStr, Cc<NativeCallback>),97}99}100101impl Debug for FuncVal {102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {103 match self {104 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),105 Self::StaticBuiltin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),106 Self::Builtin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),107 Self::NativeExt(arg0, arg1) => {108 f.debug_tuple("NativeExt").field(arg0).field(arg1).finish()109 }110 }111 }112}9811399impl PartialEq for FuncVal {114impl PartialEq for FuncVal {100 fn eq(&self, other: &Self) -> bool {115 fn eq(&self, other: &Self) -> bool {101 match (self, other) {116 match (self, other) {102 (Self::Normal(a), Self::Normal(b)) => a == b,117 (Self::Normal(a), Self::Normal(b)) => a == b,103 (Self::Intrinsic(an), Self::Intrinsic(bn)) => an == bn,118 (Self::StaticBuiltin(an), Self::StaticBuiltin(bn)) => std::ptr::eq(*an, *bn),104 (Self::NativeExt(an, _), Self::NativeExt(bn, _)) => an == bn,119 (Self::NativeExt(an, _), Self::NativeExt(bn, _)) => an == bn,105 (..) => false,120 (..) => false,106 }121 }107 }122 }108}123}109impl FuncVal {124impl FuncVal {110 pub fn is_ident(&self) -> bool {125 pub fn args_len(&self) -> usize {111 matches!(&self, Self::Intrinsic(n) if n as &str == "id")126 match self {127 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),128 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),129 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),130 Self::NativeExt(_, n) => n.params.iter().filter(|p| p.1.is_none()).count(),131 }112 }132 }113 pub fn name(&self) -> IStr {133 pub fn name(&self) -> IStr {114 match self {134 match self {115 Self::Normal(normal) => normal.name.clone(),135 Self::Normal(normal) => normal.name.clone(),116 Self::Intrinsic(name) => format!("std.{}", name).into(),136 Self::StaticBuiltin(builtin) => builtin.name().into(),137 Self::Builtin(builtin) => builtin.name().into(),117 Self::NativeExt(n, _) => format!("native.{}", n).into(),138 Self::NativeExt(n, _) => format!("native.{}", n).into(),118 }139 }119 }140 }120 pub fn evaluate(141 pub fn evaluate(121 &self,142 &self,122 call_ctx: Context,143 call_ctx: Context,123 loc: &ExprLocation,144 loc: Option<&ExprLocation>,124 args: &ArgsDesc,145 args: &dyn ArgsLike,125 tailstrict: bool,146 tailstrict: bool,126 ) -> Result<Val> {147 ) -> Result<Val> {127 match self {148 match self {135 )?;156 )?;136 evaluate(ctx, &func.body)157 evaluate(ctx, &func.body)137 }158 }138 Self::Intrinsic(name) => call_builtin(call_ctx, loc, name, args),159 Self::StaticBuiltin(name) => name.call(call_ctx, loc, args),160 Self::Builtin(b) => b.call(call_ctx, loc, args),139 Self::NativeExt(_name, handler) => {161 Self::NativeExt(_name, handler) => {140 let args =162 let args =141 parse_function_call(call_ctx, Context::new(), &handler.params, args, true)?;163 parse_function_call(call_ctx, Context::new(), &handler.params, args, true)?;142 let mut out_args = Vec::with_capacity(handler.params.len());164 let mut out_args = Vec::with_capacity(handler.params.len());143 for p in handler.params.0.iter() {165 for p in handler.params.0.iter() {144 out_args.push(args.binding(p.0.clone())?.evaluate()?);166 out_args.push(args.binding(p.0.clone())?.evaluate()?);145 }167 }146 Ok(handler.call(loc.0.clone(), &out_args)?)168 Ok(handler.call(loc.expect("todo").0.clone(), &out_args)?)147 }169 }148 }170 }149 }171 }150151 pub fn evaluate_map(172 pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result<Val> {152 &self,153 call_ctx: Context,154 args: &HashMap<IStr, Val>,155 tailstrict: bool,156 ) -> Result<Val> {157 match self {173 self.evaluate(Context::default(), None, args, true)158 Self::Normal(func) => {159 let ctx = parse_function_call_map(160 call_ctx,161 Some(func.ctx.clone()),162 &func.params,163 args,164 tailstrict,165 )?;166 evaluate(ctx, &func.body)167 }168 Self::Intrinsic(_) => todo!(),169 Self::NativeExt(_, _) => todo!(),170 }171 }174 }172173 pub fn evaluate_values(&self, args: &[Val]) -> Result<Val> {174 match self {175 Self::Normal(func) => {176 let ctx = place_args(func.ctx.clone(), &func.params, args)?;177 evaluate(ctx, &func.body)178 }179 Self::Intrinsic(_) => todo!(),180 Self::NativeExt(_, _) => todo!(),181 }182 }183}175}184176185#[derive(Clone)]177#[derive(Clone)]crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth2use rustc_hash::FxHashMap;2use rustc_hash::FxHashMap;3use serde::{Deserialize, Serialize};3use serde::{Deserialize, Serialize};4use std::{4use std::{5 borrow::Cow,5 cell::RefCell,6 cell::RefCell,6 fmt::{self, Display},7 fmt::{self, Display},7 hash::{BuildHasherDefault, Hash, Hasher},8 hash::{BuildHasherDefault, Hash, Hasher},90 }91 }91}92}9394impl<'i> From<Cow<'i, str>> for IStr {95 fn from(c: Cow<'i, str>) -> Self {96 (&c as &str).into()97 }98}929993impl Serialize for IStr {100impl Serialize for IStr {94 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>101 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth1use proc_macro2::Span;2use quote::quote;1use quote::quote;3use syn::{parse_macro_input, FnArg, Ident, ItemFn, Pat, PatType};2use syn::{3 parse_macro_input, FnArg, GenericArgument, ItemFn, Pat, PatType, Path, PathArguments, Type,4};455fn is_location_arg(t: &PatType) -> bool {6fn is_location_arg(t: &PatType) -> bool {6 t.attrs.iter().any(|a| a.path.is_ident("location"))7 t.attrs.iter().any(|a| a.path.is_ident("location"))7}8}910trait RetainHad<T> {11 fn retain_had(&mut self, h: impl FnMut(&T) -> bool) -> bool;12}13impl<T> RetainHad<T> for Vec<T> {14 fn retain_had(&mut self, h: impl FnMut(&T) -> bool) -> bool {15 let before = self.len();16 self.retain(h);17 let after = self.len();18 before != after19 }20}2122fn extract_type_from_option(ty: &Type) -> Option<&Type> {23 fn path_is_option(path: &Path) -> bool {24 path.leading_colon.is_none()25 && path.segments.len() == 126 && path.segments.iter().next().unwrap().ident == "Option"27 }2829 match ty {30 Type::Path(typepath) if typepath.qself.is_none() && path_is_option(&typepath.path) => {31 // Get the first segment of the path (there is only one, in fact: "Option"):32 let type_params = &typepath.path.segments.iter().next().unwrap().arguments;33 // It should have only on angle-bracketed param ("<String>"):34 let generic_arg = match type_params {35 PathArguments::AngleBracketed(params) => params.args.iter().next().unwrap(),36 _ => panic!("missing option generic"),37 };38 // This argument must be a type:39 match generic_arg {40 GenericArgument::Type(ty) => Some(ty),41 _ => panic!("option generic should be a type"),42 }43 }44 _ => None,45 }46}8479#[proc_macro_attribute]48#[proc_macro_attribute]10pub fn builtin(49pub fn builtin(33 Pat::Ident(i) => i.ident.to_string(),72 Pat::Ident(i) => i.ident.to_string(),34 _ => panic!("only idents supported yet"),73 _ => panic!("only idents supported yet"),35 };74 };36 // TODO: Check if ty == Option<_>37 let optional = false;75 let optional = extract_type_from_option(&t.ty).is_some();38 quote! {76 quote! {39 BuiltinParam {77 BuiltinParam {40 name: #ident,78 name: std::borrow::Cow::Borrowed(#ident),41 has_default: #optional,79 has_default: #optional,42 }80 }43 }81 }53 FnArg::Typed(t) => t,91 FnArg::Typed(t) => t,54 })92 })55 .map(|t| {93 .map(|t| {56 let count_before = t.attrs.len();94 let is_location = t.attrs.retain_had(|a| !a.path.is_ident("location"));57 t.attrs.retain(|a| !a.path.is_ident("location"));58 let count_after = t.attrs.len();59 let is_location = count_before != count_after;60 if is_location {95 if is_location {61 quote! {{96 quote! {{62 loc97 loc67 _ => panic!("only idents supported yet"),102 _ => panic!("only idents supported yet"),68 };103 };69 let ty = &t.ty;104 let ty = &t.ty;105 if let Some(opt_ty) = extract_type_from_option(&t.ty) {106 quote! {{107 if let Some(value) = parsed.get(#ident) {108 Some(jrsonnet_evaluator::push_description_frame(109 || format!("argument <{}> evaluation", #ident),110 || <#opt_ty>::try_from(value.evaluate()?),111 )?)112 } else {113 None114 }115 }}116 } else {70 quote! {{117 quote! {{71 let value = parsed.get(#ident).unwrap();118 let value = parsed.get(#ident).unwrap();7211975 || <#ty>::try_from(value.evaluate()?),122 || <#ty>::try_from(value.evaluate()?),76 )?123 )?77 }}124 }}125 }78 }126 }79 }).collect::<Vec<_>>();127 })80 128 .collect::<Vec<_>>();81 let inner_name = Ident::new("inner", Span::call_site());12982 let mut inner_fun = fun.clone();83 inner_fun.sig.ident = inner_name.clone();8485 let attrs = &fun.attrs;86 let vis = &fun.vis;87 let name = &fun.sig.ident;130 let name = &fun.sig.ident;131 let vis = &fun.vis;88 (quote! {132 (quote! {89 #(#attrs)*133 #fun134 #[doc(hidden)]135 #[allow(non_camel_case_types)]136 #[derive(Clone, Copy, gcmodule::Trace)]90 #vis fn #name(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {137 #vis struct #name {}91 #inner_fun138 const _: () = {92 use jrsonnet_evaluator::function::BuiltinParam;139 use jrsonnet_evaluator::function::{Builtin, StaticBuiltin, BuiltinParam, ArgsLike};93 const PARAMS: &'static [BuiltinParam] = &[140 const PARAMS: &'static [BuiltinParam] = &[94 #(#params),*141 #(#params),*95 ];142 ];143144 impl #name {145 pub const INST: &'static dyn StaticBuiltin = &#name {};146 }147 impl StaticBuiltin for #name {}148 impl Builtin for #name149 where150 Self: 'static151 {152 fn name(&self) -> &str {153 stringify!(#name)154 }155 fn params(&self) -> &[BuiltinParam] {156 PARAMS157 }158 fn call(&self, context: Context, loc: Option<&ExprLocation>, args: &dyn ArgsLike) -> Result<Val> {96 let parsed = jrsonnet_evaluator::function::parse_builtin_call(context, &PARAMS, args, false)?;159 let parsed = jrsonnet_evaluator::function::parse_builtin_call(context, &PARAMS, args, false)?;9716098 let result: #result = #inner_name(#(#args),*);161 let result: #result = #name(#(#args),*);99 let result = result?;162 let result = result?;100 result.try_into()163 result.try_into()164 }165 }101 }166 };102 })167 })103 .into()168 .into()104}169}crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth40 / "#" (!eol()[_])* eol()40 / "#" (!eol()[_])* eol()414142 rule single_whitespace() = quiet!{([' ' | '\r' | '\n' | '\t'] / comment())} / expected!("<whitespace>")42 rule single_whitespace() = quiet!{([' ' | '\r' | '\n' | '\t'] / comment())} / expected!("<whitespace>")43 rule _() = single_whitespace()*43 rule _() = quiet!{([' ' | '\r' | '\n' | '\t']+) / comment()}* / expected!("<whitespace>")444445 /// For comma-delimited elements45 /// For comma-delimited elements46 rule comma() = quiet!{_ "," _} / expected!("<comma>")46 rule comma() = quiet!{_ "," _} / expected!("<comma>")305pub fn parse(str: &str, settings: &ParserSettings) -> Result<LocExpr, ParseError> {305pub fn parse(str: &str, settings: &ParserSettings) -> Result<LocExpr, ParseError> {306 jsonnet_parser::jsonnet(str, settings)306 jsonnet_parser::jsonnet(str, settings)307}307}308/// Used for importstr values309pub fn string_to_expr(str: IStr, settings: &ParserSettings) -> LocExpr {310 let len = str.len();311 LocExpr(312 Rc::new(Expr::Str(str)),313 ExprLocation(settings.file_name.clone(), 0, len),314 )315}308316309#[cfg(test)]317#[cfg(test)]310pub mod tests {318pub mod tests {crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth375375376 manifestJsonEx:: $intrinsic(manifestJsonEx),376 manifestJsonEx:: $intrinsic(manifestJsonEx),377377378 manifestYamlDocImpl:: $intrinsic(manifestYamlDocImpl),378 manifestYamlDoc:: $intrinsic(manifestYamlDoc),379379380 manifestYamlDoc(value, indent_array_in_object=false, quote_keys=true):: std.manifestYamlDocImpl(value, indent_array_in_object, quote_keys),381382 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::380 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::383 if !std.isArray(value) then381 if !std.isArray(value) then384 error 'manifestYamlStream only takes arrays, got ' + std.type(value)382 error 'manifestYamlStream only takes arrays, got ' + std.type(value)442 base64Decode:: $intrinsic(base64Decode),440 base64Decode:: $intrinsic(base64Decode),443441444 reverse:: $intrinsic(reverse),442 reverse:: $intrinsic(reverse),445446 sortImpl:: $intrinsic(sortImpl),447443448 sort(arr, keyF=id)::444 sort:: $intrinsic(sort),449 std.sortImpl(arr, keyF),450445451 uniq(arr, keyF=id)::446 uniq(arr, keyF=id)::452 local f(a, b) =447 local f(a, b) =