difftreelog
refactor prepared signatures in IR
in: master
15 files changed
crates/jrsonnet-evaluator/src/async_import.rsdiffbeforeafterboth334use jrsonnet_gcmodule::Acyclic;4use jrsonnet_gcmodule::Acyclic;5use jrsonnet_parser::{5use jrsonnet_parser::{6 ArgsDesc, AssertExpr, AssertStmt, BindSpec, CompSpec, Destruct, Expr, FieldMember, FieldName,6 ArgsDesc, AssertExpr, AssertStmt, BindSpec, CompSpec, Destruct, Expr, ExprParam, ExprParams,7 ForSpecData, IfElse, IfSpecData, ImportKind, ObjBody, Param, ParamsDesc,7 FieldMember, FieldName, ForSpecData, IfElse, IfSpecData, ImportKind, ObjBody, ParserSettings,8 ParserSettings, Slice, SliceDesc, Source, SourcePath, Spanned,8 Slice, SliceDesc, Source, SourcePath, Spanned,9};9};10use rustc_hash::FxHashMap;10use rustc_hash::FxHashMap;63 }63 }64 }64 }65 }65 }66 fn in_params(params: &ParamsDesc, out: &mut FoundImports) {66 fn in_params(params: &ExprParams, out: &mut FoundImports) {67 for Param(dest, default) in &*params.0 {67 for ExprParam { destruct, default } in &*params.exprs {68 in_destruct(dest, out);68 in_destruct(destruct, out);69 if let Some(expr) = default {69 if let Some(expr) = default {70 find_imports(expr, out);70 find_imports(expr, out);71 }71 }crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth180 assert!(old.is_none(), "variable bound twice in single context call");180 assert!(old.is_none(), "variable bound twice in single context call");181 self181 self182 }182 }183 pub fn binds(&mut self, bindings: FxHashMap<IStr, Thunk<Val>>) -> &mut Self {184 for (k, v) in bindings {185 self.bind(k, v);186 }187 self188 }183 pub fn build(self) -> Context {189 pub fn build(self) -> Context {184 if let Some(parent) = self.extend {190 if let Some(parent) = self.extend {185 parent.extend_bindings(self.bindings)191 parent.extend_bindings(self.bindings)crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth1use std::{1use std::{2 cmp::Ordering,2 cmp::Ordering,3 convert::Infallible,3 convert::Infallible,4 fmt::{Debug, Display},4 fmt::{self, Debug, Display},5};5};667use jrsonnet_gcmodule::{Acyclic, Trace};7use jrsonnet_gcmodule::{Acyclic, Trace};11use thiserror::Error;11use thiserror::Error;121213use crate::{13use crate::{14 function::{builtin::ParamDefault, CallLocation},14 function::{CallLocation, FunctionSignature, ParamDefault, ParamName},15 stdlib::format::FormatError,15 stdlib::format::FormatError,16 typed::TypeLocError,16 typed::TypeLocError,17 val::ConvertNumValueError,17 val::ConvertNumValueError,47 out47 out48}48}4950fn format_signature(sig: &FunctionSignature) -> String {51 let mut out = String::new();52 out.push_str("\nFunction has the following signature: ");53 out.push('(');54 if sig.is_empty() {55 out.push_str("/*no arguments*/");56 } else {57 for (i, (name, default)) in sig.iter().enumerate() {58 if i != 0 {59 out.push_str(", ");60 }61 if let Some(name) = name {62 out.push_str(name);63 } else {64 out.push_str("<unnamed>");65 }66 match default {67 ParamDefault::None => {}68 ParamDefault::Exists => out.push_str(" = <default>"),69 ParamDefault::Literal(lit) => {70 out.push_str(" = ");71 out.push_str(lit);72 }73 }74 }75 }76 out.push(')');77 out78}794980const fn format_empty_str(str: &str) -> &str {50const fn format_empty_str(str: &str) -> &str {81 if str.is_empty() {51 if str.is_empty() {104 heap.into_iter().map(|v| v.1).collect()74 heap.into_iter().map(|v| v.1).collect()105}75}106107type FunctionSignature = Vec<(Option<IStr>, ParamDefault)>;10876109/// Possible errors77/// Possible errors110#[allow(missing_docs)]78#[allow(missing_docs)]148 #[error("only functions can be called, got {0}")]116 #[error("only functions can be called, got {0}")]149 OnlyFunctionsCanBeCalledGot(ValType),117 OnlyFunctionsCanBeCalledGot(ValType),150 #[error("parameter {0} is not defined")]118 #[error("parameter {0} is not defined")]151 UnknownFunctionParameter(String),119 UnknownFunctionParameter(IStr),152 #[error("argument {0} is already bound")]120 #[error("argument {0} is already bound")]153 BindingParameterASecondTime(IStr),121 BindingParameterASecondTime(IStr),154 #[error("too many args, function has {0}{sig}", sig = format_signature(.1))]122 #[error("too many args, function has {0}\nFunction has the following signature: {1}")]155 TooManyArgsFunctionHas(usize, FunctionSignature),123 TooManyArgsFunctionHas(usize, FunctionSignature),156 #[error("function argument is not passed: {}{}", .0.as_ref().map_or("<unnamed>", IStr::as_str), format_signature(.1))]124 #[error("function argument is not passed: {0}\nFunction has the following signature: {1}")]157 FunctionParameterNotBoundInCall(Option<IStr>, FunctionSignature),125 FunctionParameterNotBoundInCall(ParamName, FunctionSignature),158126159 #[error("external variable is not defined: {0}")]127 #[error("external variable is not defined: {0}")]160 UndefinedExternalVariable(IStr),128 UndefinedExternalVariable(IStr),crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth170 let value = value.clone();170 let value = value.clone();171 let data = {171 let data = {172 let fctx = fctx.clone();172 let fctx = fctx.clone();173 Thunk!(move || name.map_or_else(173 Thunk!(move || name.0.map_or_else(174 || evaluate(fctx.unwrap(), &value),174 || evaluate(fctx.unwrap(), &value),175 |name| evaluate_named(fctx.unwrap(), &value, name),175 |name| evaluate_named(fctx.unwrap(), &value, name),176 ))176 ))crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth3use jrsonnet_gcmodule::{Cc, Trace};3use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{5use jrsonnet_parser::{6 ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember, FieldName,6 function::ParamName, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprParams,7 ForSpecData, IfSpecData, ImportKind, LiteralType, ObjBody, ObjMembers, ParamsDesc, Spanned,7 FieldMember, FieldName, ForSpecData, IfSpecData, ImportKind, LiteralType, ObjBody, ObjMembers,8 Spanned,8};9};9use jrsonnet_types::ValType;10use jrsonnet_types::ValType;10use rustc_hash::FxHashMap;11use rustc_hash::FxHashMap;111212use self::destructure::destruct;13use self::destructure::destruct;13use crate::{14use crate::{14 Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, ResultExt, SupThis, Unbound, Val, arr::ArrValue, bail, destructure::evaluate_dest, error::{ErrorKind::*, suggest_object_fields}, evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op}, function::{CallLocation, FuncDesc, FuncVal, builtin::{ParamDefault, ParamName, ParamParse}}, gc::WithCapacityExt as _, in_frame, typed::Typed, val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk}, with_state15 arr::ArrValue,16 bail,17 destructure::evaluate_dest,18 error::{suggest_object_fields, ErrorKind::*},19 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},20 function::{CallLocation, FuncDesc, FuncVal},21 gc::WithCapacityExt as _,22 in_frame,23 typed::Typed,24 val::{CachedUnbound, IndexableVal, NumValue, StrValue, Thunk},25 with_state, Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result,26 ResultExt, SupThis, Unbound, Val,15};27};16pub mod destructure;28pub mod destructure;17pub mod operator;29pub mod operator;71pub fn evaluate_method(83pub fn evaluate_method(72 ctx: Context,84 ctx: Context,73 name: IStr,85 name: IStr,74 params: ParamsDesc,86 params: ExprParams,75 body: Rc<Spanned<Expr>>,87 body: Rc<Spanned<Expr>>,76) -> Val {88) -> Val {77 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {89 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {78 name,90 name,79 ctx,91 ctx,80 params_parse: params81 .iter()82 .map(|p| {83 ParamParse::new(84 p.0.name().map_or(ParamName::ANONYMOUS, ParamName::new),85 ParamDefault::exists(p.1.is_some()),86 )87 })88 .collect(),89 params,92 params,90 body,93 body,91 })))94 })))125 Val::Arr(list) => {128 Val::Arr(list) => {126 for item in list.iter_lazy() {129 for item in list.iter_lazy() {127 let fctx = Pending::new();130 let fctx = Pending::new();128 let mut new_bindings = FxHashMap::with_capacity(var.capacity_hint());131 let mut new_bindings = FxHashMap::with_capacity(var.binds_len());129 destruct(var, item, fctx.clone(), &mut new_bindings)?;132 destruct(var, item, fctx.clone(), &mut new_bindings)?;130 let ctx = ctx.clone().extend_bindings(new_bindings).into_future(fctx);133 let ctx = ctx.clone().extend_bindings(new_bindings).into_future(fctx);131134178 fn bind(&self, sup_this: SupThis) -> Result<Context> {181 fn bind(&self, sup_this: SupThis) -> Result<Context> {179 let fctx = Context::new_future();182 let fctx = Context::new_future();180 let mut new_bindings =183 let mut new_bindings =181 FxHashMap::with_capacity(self.locals.iter().map(BindSpec::capacity_hint).sum());184 FxHashMap::with_capacity(self.locals.iter().map(BindSpec::binds_len).sum());182 for b in self.locals.iter() {185 for b in self.locals.iter() {183 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;186 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;184 }187 }249 struct UnboundMethod<B: Trace> {252 struct UnboundMethod<B: Trace> {250 uctx: B,253 uctx: B,251 value: Rc<Spanned<Expr>>,254 value: Rc<Spanned<Expr>>,252 params: ParamsDesc,255 params: ExprParams,253 name: IStr,256 name: IStr,254 }257 }255 impl<B: Unbound<Bound = Context>> Unbound for UnboundMethod<B> {258 impl<B: Unbound<Bound = Context>> Unbound for UnboundMethod<B> {376 Ok(())379 Ok(())377}380}381382pub fn evaluate_named_param(ctx: Context, expr: &Spanned<Expr>, name: ParamName) -> Result<Val> {383 match name.0 {384 Some(name) => evaluate_named(ctx, expr, name),385 None => evaluate(ctx, expr),386 }387}378388379pub fn evaluate_named(ctx: Context, expr: &Spanned<Expr>, name: IStr) -> Result<Val> {389pub fn evaluate_named(ctx: Context, expr: &Spanned<Expr>, name: IStr) -> Result<Val> {380 use Expr::*;390 use Expr::*;551 })?,561 })?,552 LocalExpr(bindings, returned) => {562 LocalExpr(bindings, returned) => {553 let mut new_bindings: FxHashMap<IStr, Thunk<Val>> =563 let mut new_bindings: FxHashMap<IStr, Thunk<Val>> =554 FxHashMap::with_capacity(bindings.iter().map(BindSpec::capacity_hint).sum());564 FxHashMap::with_capacity(bindings.iter().map(BindSpec::binds_len).sum());555 let fctx = Context::new_future();565 let fctx = Context::new_future();556 for b in bindings {566 for b in bindings {557 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;567 evaluate_dest(b, fctx.clone(), &mut new_bindings)?;crates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth1use std::any::Any;1use std::any::Any;2use std::fmt;233use jrsonnet_gcmodule::{cc_dyn, Acyclic, Trace, TraceBox};4use jrsonnet_gcmodule::{cc_dyn, Acyclic, Trace, TraceBox};4use jrsonnet_interner::IStr;5use jrsonnet_interner::IStr;6use jrsonnet_parser::function::{FunctionSignature, ParamDefault, ParamName, ParamParse};576use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};8use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};7use crate::{Context, Result, Val};9use crate::{Context, Result, Val};89#[derive(Clone, Acyclic)]10pub struct ParamName(Option<IStr>);11impl ParamName {12 pub const ANONYMOUS: Self = Self(None);13 pub fn new(name: IStr) -> Self {14 Self(Some(name))15 }16 pub fn as_str(&self) -> Option<&str> {17 self.0.as_deref()18 }19 pub fn is_anonymous(&self) -> bool {20 self.0.is_none()21 }22}23impl PartialEq<IStr> for ParamName {24 fn eq(&self, other: &IStr) -> bool {25 self.026 .as_ref()27 .map_or(false, |s| s.as_bytes() == other.as_bytes())28 }29}3031#[derive(Clone, Copy, Debug, Acyclic)]32pub enum ParamDefault {33 None,34 Exists,35 Literal(&'static str),36}37impl ParamDefault {38 pub const fn exists(is_exists: bool) -> Self {39 if is_exists {40 Self::Exists41 } else {42 Self::None43 }44 }45}461047#[macro_export]11#[macro_export]48macro_rules! params {12macro_rules! params {49 (@name unnamed) => { ParamName::ANONYMOUS };13 (@name unnamed) => { ParamName::ANONYMOUS };50 (@name named $name:literal) => { ParamName::new($crate::IStr::from($name)) };14 (@name named $name:literal) => { ParamName::new($crate::IStr::from($name)) };51 ($($(#[$meta:meta])* [$kind:ident $(($lit:literal))? => $default:expr]),* $(,)?) => {15 ($($(#[$meta:meta])* [$kind:ident $(($lit:literal))? => $default:expr]),* $(,)?) => {52 thread_local! {16 thread_local! {53 static PARAMS: [ParamParse; { const N: usize = <[u8]>::len(&[$($(#[$meta])* 0u8),*]); N }] = [17 static PARAMS: FunctionSignature = FunctionSignature::new([54 $($(#[$meta])* ParamParse::new(params!(@name $kind $($lit)?), $default)),*18 $($(#[$meta])* ParamParse::new(params!(@name $kind $($lit)?), $default)),*55 ];19 ].into());56 }20 }57 };21 };58}22}5960#[derive(Clone, Acyclic)]61pub struct ParamParse {62 name: ParamName,63 default: ParamDefault,64}65impl ParamParse {66 pub fn new(name: ParamName, default: ParamDefault) -> Self {67 Self { name, default }68 }69 /// Parameter name for named call parsing70 pub fn name(&self) -> &ParamName {71 &self.name72 }73 pub fn default(&self) -> ParamDefault {74 self.default75 }76 pub fn has_default(&self) -> bool {77 !matches!(self.default, ParamDefault::None)78 }79}802381cc_dyn!(24cc_dyn!(82 #[derive(Clone)]25 #[derive(Clone)]89 self.0.name()32 self.0.name()90 }33 }913492 fn params(&self) -> &[ParamParse] {35 fn params(&self) -> FunctionSignature {93 self.0.params()36 self.0.params()94 }37 }9538109 /// Function name to be used in stack traces52 /// Function name to be used in stack traces110 fn name(&self) -> &str;53 fn name(&self) -> &str;111 /// Parameter names for named calls54 /// Parameter names for named calls112 fn params(&self) -> &[ParamParse];55 fn params(&self) -> FunctionSignature;113 /// Call the builtin56 /// Call the builtin114 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;57 fn call(&self, ctx: Context, loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val>;1155812669127#[derive(Trace)]70#[derive(Trace)]128pub struct NativeCallback {71pub struct NativeCallback {129 pub(crate) params: Vec<ParamParse>,72 pub(crate) params: FunctionSignature,130 handler: TraceBox<dyn NativeCallbackHandler>,73 handler: TraceBox<dyn NativeCallbackHandler>,131}74}132impl NativeCallback {75impl NativeCallback {133 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]76 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]134 pub fn new(params: Vec<String>, handler: impl NativeCallbackHandler) -> Self {77 pub fn new(params: Vec<String>, handler: impl NativeCallbackHandler) -> Self {135 Self {78 Self {136 params: params79 params: FunctionSignature::new(80 params137 .into_iter()81 .into_iter()138 .map(|n| ParamParse {82 .map(|n| ParamParse::new(ParamName::new(n.into()), ParamDefault::None))139 name: ParamName::new(n.into()),140 default: ParamDefault::None,141 })142 .collect(),83 .collect(),84 ),143 handler: TraceBox(Box::new(handler)),85 handler: TraceBox(Box::new(handler)),144 }86 }145 }87 }152 "<native>"94 "<native>"153 }95 }15496155 fn params(&self) -> &[ParamParse] {97 fn params(&self) -> FunctionSignature {156 &self.params98 self.params.clone()157 }99 }158100159 fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {101 fn call(&self, ctx: Context, _loc: CallLocation<'_>, args: &dyn ArgsLike) -> Result<Val> {160 let args = parse_builtin_call(ctx, &self.params, args, true)?;102 let args = parse_builtin_call(ctx, self.params.clone(), args, true)?;161 let args = args103 let args = args162 .into_iter()104 .into_iter()163 .map(|a| a.expect("legacy natives have no default params"))105 .map(|a| a.expect("legacy natives have no default params"))crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth5use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_gcmodule::{Cc, Trace};6use jrsonnet_interner::IStr;6use jrsonnet_interner::IStr;7pub use jrsonnet_macros::builtin;7pub use jrsonnet_macros::builtin;8use jrsonnet_parser::{Destruct, Expr, ParamsDesc, Span, Spanned};8use jrsonnet_parser::{Destruct, Expr, ExprParams, Span, Spanned};9910use self::{10use self::{11 arglike::OptionalContext,11 arglike::OptionalContext,12 builtin::{Builtin, ParamParse, StaticBuiltin},12 builtin::{Builtin, StaticBuiltin},13 native::NativeDesc,13 native::NativeDesc,14 parse::{parse_default_function_call, parse_function_call},14 parse::{parse_default_function_call, parse_function_call},15};15};16use crate::{16use crate::{17 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, Context,17 bail, error::ErrorKind::*, evaluate, evaluate_trivial, function::builtin::BuiltinFunc, params,18 ContextBuilder, Result, Thunk, Val,18 Context, ContextBuilder, Result, Thunk, Val,19};19};202021pub mod arglike;21pub mod arglike;22pub mod builtin;22pub mod builtin;23pub mod native;23pub mod native;24pub mod parse;24pub mod parse;25pub mod prepared;2627pub use jrsonnet_parser::function::*;252826/// Function callsite location.29/// Function callsite location.27/// Either from other jsonnet code, specified by expression location, or from native (without location).30/// Either from other jsonnet code, specified by expression location, or from native (without location).66 pub ctx: Context,69 pub ctx: Context,677068 /// Function parameter definition71 /// Function parameter definition69 pub params: ParamsDesc,72 pub params: ExprParams,70 /// Function body73 /// Function body71 pub body: Rc<Spanned<Expr>>,74 pub body: Rc<Spanned<Expr>>,7273 #[educe(PartialEq = false, Debug = false)]74 pub(crate) params_parse: Vec<ParamParse>,75}75}76impl FuncDesc {76impl FuncDesc {77 /// Create body context, but fill arguments without defaults with lazy error77 /// Create body context, but fill arguments without defaults with lazy error139 Self::StaticBuiltin(static_builtin)139 Self::StaticBuiltin(static_builtin)140 }140 }141141142 pub fn params(&self) -> &[ParamParse] {142 pub fn params(&self) -> FunctionSignature {143 match self {143 match self {144 Self::Id => ID.params(),144 Self::Id => ID.params(),145 Self::StaticBuiltin(i) => i.params(),145 Self::StaticBuiltin(i) => i.params(),146 Self::Builtin(i) => i.params(),146 Self::Builtin(i) => i.params(),147 Self::Normal(p) => &p.params_parse,147 Self::Normal(p) => p.params.signature.clone(),148 Self::Thunk(_) => &[],148 Self::Thunk(_) => FunctionSignature::empty(),149 }149 }150 }150 }151 /// Amount of non-default required arguments151 /// Amount of non-default required arguments152 pub fn params_len(&self) -> usize {152 pub fn params_len(&self) -> usize {153 match self {154 Self::Id => 1,155 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),156 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(),153 self.params().iter().filter(|p| !p.has_default()).count()157 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(),158 Self::Thunk(_) => 0,159 }160 }154 }161 /// Function name, as defined in code.155 /// Function name, as defined in code.162 pub fn name(&self) -> IStr {156 pub fn name(&self) -> IStr {185 evaluate(body_ctx, &func.body)179 evaluate(body_ctx, &func.body)186 }180 }187 Self::Thunk(thunk) => {181 Self::Thunk(thunk) => {188 if args.is_empty() {182 if !args.is_empty() {189 bail!(TooManyArgsFunctionHas(0, vec![],))183 bail!(TooManyArgsFunctionHas(0, FunctionSignature::empty()))190 }184 }191 thunk.evaluate()185 thunk.evaluate()192 }186 }223 if desc.params.len() != 1 {217 if desc.params.len() != 1 {224 return false;218 return false;225 }219 }226 let param = &desc.params[0];220 let param = &desc.params.exprs[0];227 if param.1.is_some() {221 if param.default.is_some() {228 return false;222 return false;229 }223 }224230 #[allow(clippy::infallible_destructuring_match)]225 #[allow(clippy::infallible_destructuring_match)]231 let id = match ¶m.0 {226 let id = match ¶m.destruct {232 Destruct::Full(id) => id,227 Destruct::Full(id) => id,233 #[cfg(feature = "exp-destruct")]228 #[cfg(feature = "exp-destruct")]234 _ => return false,229 _ => return false,crates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth1use std::mem::replace;1use std::mem::replace;223use jrsonnet_interner::IStr;3use jrsonnet_interner::IStr;4use jrsonnet_parser::ParamsDesc;4use jrsonnet_parser::{function::FunctionSignature, ExprParams};5use rustc_hash::FxHashMap;5use rustc_hash::FxHashMap;667use super::{arglike::ArgsLike, builtin::ParamParse};7use super::arglike::ArgsLike;8use crate::{8use crate::{9 bail,9 bail,10 destructure::destruct,10 destructure::destruct,11 error::{ErrorKind::*, Result},11 error::{ErrorKind::*, Result},12 evaluate_named,12 evaluate_named, evaluate_named_param,13 function::builtin::ParamDefault,14 gc::WithCapacityExt as _,13 gc::WithCapacityExt as _,15 Context, Pending, Thunk, Val,14 Context, Pending, Thunk, Val,16};15};26pub fn parse_function_call(25pub fn parse_function_call(27 ctx: Context,26 ctx: Context,28 body_ctx: Context,27 body_ctx: Context,29 params: &ParamsDesc,28 params: &ExprParams,30 args: &dyn ArgsLike,29 args: &dyn ArgsLike,31 tailstrict: bool,30 tailstrict: bool,32) -> Result<Context> {31) -> Result<Context> {33 let mut passed_args =32 let mut passed_args = FxHashMap::with_capacity(params.binds_len());34 FxHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());35 if args.unnamed_len() > params.len() {33 if args.unnamed_len() > params.signature.len() {36 bail!(TooManyArgsFunctionHas(34 bail!(TooManyArgsFunctionHas(37 params.len(),35 params.signature.len(),38 params36 params.signature.clone(),39 .iter()40 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))41 .collect()42 ))37 ))43 }38 }443945 let mut filled_named = 0;40 let mut filled_named = 0;46 let mut filled_positionals = 0;41 let mut filled_positionals = 0;474248 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {43 args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {49 let name = params[id].0.clone();50 destruct(44 destruct(51 &name,45 ¶ms.exprs[id].destruct,52 arg,46 arg,53 Pending::new_filled(ctx.clone()),47 Pending::new_filled(ctx.clone()),54 &mut passed_args,48 &mut passed_args,595360 args.named_iter(ctx, tailstrict, &mut |name, value| {54 args.named_iter(ctx, tailstrict, &mut |name, value| {61 // FIXME: O(n) for arg existence check55 // FIXME: O(n) for arg existence check62 if !params.iter().any(|p| p.0.name().as_ref() == Some(name)) {56 if !params.exprs.iter().any(|p| &p.destruct.name() == name) {63 bail!(UnknownFunctionParameter((name as &str).to_owned()));57 bail!(UnknownFunctionParameter(name.clone()));64 }58 }65 if passed_args.insert(name.clone(), value).is_some() {59 if passed_args.insert(name.clone(), value).is_some() {66 bail!(BindingParameterASecondTime(name.clone()));60 bail!(BindingParameterASecondTime(name.clone()));74 // Default values should be created in newly created context68 // Default values should be created in newly created context75 let fctx = Context::new_future();69 let fctx = Context::new_future();76 let mut defaults = FxHashMap::with_capacity(70 let mut defaults =77 params.iter().map(|p| p.0.capacity_hint()).sum::<usize>()71 FxHashMap::with_capacity(params.binds_len() - filled_named - filled_positionals);78 - filled_named79 - filled_positionals,80 );817282 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {73 for (idx, into, default) in params74 .exprs75 .iter()76 .enumerate()77 .filter_map(|(i, p)| Some((i, &p.destruct, p.default.as_ref()?)))78 {83 if let Some(name) = param.0.name() {79 if let Some(name) = into.name().0 {84 if passed_args.contains_key(&name) {80 if passed_args.contains_key(&name) {85 continue;81 continue;86 }82 }89 }85 }908691 destruct(87 destruct(92 ¶m.0,88 &into,93 {89 {94 let ctx = fctx.clone();90 let ctx = fctx.clone();95 let name = param.0.name().unwrap_or_else(|| "<destruct>".into());91 let name = into.name();96 let value = param.1.clone().expect("default exists");92 let value = default.clone();97 Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))93 Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))98 },94 },99 fctx.clone(),95 fctx.clone(),100 &mut defaults,96 &mut defaults,101 )?;97 )?;102 if param.0.name().is_some() {98 if !into.name().is_anonymous() {103 filled_named += 1;99 filled_named += 1;104 } else {100 } else {105 filled_positionals += 1;101 filled_positionals += 1;108104109 // Some args still weren't filled105 // Some args still weren't filled110 if filled_named + filled_positionals != params.len() {106 if filled_named + filled_positionals != params.len() {111 for param in params.iter().skip(args.unnamed_len()) {107 for param in params.exprs.iter().skip(args.unnamed_len()) {112 let mut found = false;108 let mut found = false;113 args.named_names(&mut |name| {109 args.named_names(&mut |name| {114 if Some(name) == param.0.name().as_ref() {110 if ¶m.destruct.name() == name {115 found = true;111 found = true;116 }112 }117 });113 });118 if !found {114 if !found {119 bail!(FunctionParameterNotBoundInCall(115 bail!(FunctionParameterNotBoundInCall(120 param.0.clone().name(),116 param.destruct.name(),121 params117 params.signature.clone()122 .iter()123 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))124 .collect()125 ));118 ));126 }119 }127 }120 }147/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily140/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily148pub fn parse_builtin_call(141pub fn parse_builtin_call(149 ctx: Context,142 ctx: Context,150 params: &[ParamParse],143 params: FunctionSignature,151 args: &dyn ArgsLike,144 args: &dyn ArgsLike,152 tailstrict: bool,145 tailstrict: bool,153) -> Result<Vec<Option<Thunk<Val>>>> {146) -> Result<Vec<Option<Thunk<Val>>>> {154 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];147 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];155 if args.unnamed_len() > params.len() {148 if args.unnamed_len() > params.len() {156 bail!(TooManyArgsFunctionHas(149 bail!(TooManyArgsFunctionHas(params.len(), params,))157 params.len(),158 params159 .iter()160 .map(|p| (p.name().as_str().map(IStr::from), p.default()))161 .collect()162 ))163 }150 }164151175 let id = params162 let id = params176 .iter()163 .iter()177 .position(|p| p.name() == name)164 .position(|p| p.name() == name)178 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;165 .ok_or_else(|| UnknownFunctionParameter(name.clone()))?;179 if replace(&mut passed_args[id], Some(arg)).is_some() {166 if replace(&mut passed_args[id], Some(arg)).is_some() {180 bail!(BindingParameterASecondTime(name.clone()));167 bail!(BindingParameterASecondTime(name.clone()));181 }168 }202 });189 });203 if !found {190 if !found {204 bail!(FunctionParameterNotBoundInCall(191 bail!(FunctionParameterNotBoundInCall(205 param.name().as_str().map(IStr::from),192 param.name().clone(),206 params193 params,207 .iter()208 .map(|p| (p.name().as_str().map(IStr::from), p.default()))209 .collect()210 ));194 ));211 }195 }212 }196 }218202219/// Creates Context, which has all argument default values applied203/// Creates Context, which has all argument default values applied220/// and with unbound values causing error to be returned204/// and with unbound values causing error to be returned221pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {205pub fn parse_default_function_call(body_ctx: Context, params: &ExprParams) -> Result<Context> {222 let fctx = Context::new_future();206 let fctx = Context::new_future();223207224 let mut bindings = FxHashMap::with_capacity(params.iter().map(|p| p.0.capacity_hint()).sum());208 let mut bindings = FxHashMap::with_capacity(params.binds_len());225209226 for param in params.iter() {210 for param in params.exprs.iter() {227 if let Some(v) = ¶m.1 {211 if let Some(v) = ¶m.default {228 destruct(212 destruct(229 ¶m.0.clone(),213 ¶m.destruct.clone(),230 {214 {231 let ctx = fctx.clone();215 let ctx = fctx.clone();232 let name = param.0.name().unwrap_or_else(|| "<destruct>".into());216 let name = param.destruct.name();233 let value = v.clone();217 let value = v.clone();234 Thunk!(move || evaluate_named(ctx.unwrap(), &value, name))218 Thunk!(move || evaluate_named_param(ctx.unwrap(), &value, name))235 },219 },236 fctx.clone(),220 fctx.clone(),237 &mut bindings,221 &mut bindings,238 )?;222 )?;239 } else {223 } else {240 destruct(224 destruct(241 ¶m.0,225 ¶m.destruct,242 {226 {243 let param_name = param.0.name().unwrap_or_else(|| "<destruct>".into());227 let param_name = param.destruct.name();244 let params = params.clone();228 let params = params.clone();245 Thunk!(move || Err(FunctionParameterNotBoundInCall(229 Thunk!(move || Err(FunctionParameterNotBoundInCall(246 Some(param_name),230 param_name,247 params231 params.signature.clone()248 .iter()249 .map(|p| (p.0.name(), ParamDefault::exists(p.1.is_some())))250 .collect(),251 )232 )252 .into()))233 .into()))253 },234 },crates/jrsonnet-evaluator/src/function/prepared.rsdiffbeforeafterbothno changes
crates/jrsonnet-evaluator/src/stack.rsdiffbeforeafterboth20type NightlyLocalKey<T> = std::thread::LocalKey<T>;20type NightlyLocalKey<T> = std::thread::LocalKey<T>;212122#[cfg(nightly)]22#[cfg(nightly)]23#[macro_export]23macro_rules! const_tls {24macro_rules! const_tls {24 (const $name:ident: $t:ty = $expr:expr;) => {25 (const $name:ident: $t:ty = $expr:expr;) => {25 #[thread_local]26 #[thread_local]26 static $name: NightlyLocalKey<$t> = NightlyLocalKey($expr);27 static $name: NightlyLocalKey<$t> = NightlyLocalKey($expr);27 };28 };28}29}29#[cfg(not(nightly))]30#[cfg(not(nightly))]31#[macro_export]30macro_rules! const_tls {32macro_rules! const_tls {31 (const $name:ident: $t:ty = $expr:expr;) => {33 (const $name:ident: $t:ty = $expr:expr;) => {32 thread_local! {34 thread_local! {crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth364 const _: () = {368 const _: () = {365 use ::jrsonnet_evaluator::{369 use ::jrsonnet_evaluator::{366 State, Val,370 State, Val,367 function::{builtin::{Builtin, StaticBuiltin, ParamParse, ParamName, ParamDefault}, CallLocation, ArgsLike, parse::parse_builtin_call},371 function::{builtin::{Builtin, StaticBuiltin}, FunctionSignature, ParamParse, ParamName, ParamDefault, CallLocation, ArgsLike, parse::parse_builtin_call},368 Result, Context, typed::Typed,372 Result, Context, typed::Typed,369 parser::Span, params,373 parser::Span, params,370 };374 };380 fn name(&self) -> &str {384 fn name(&self) -> &str {381 stringify!(#name)385 stringify!(#name)382 }386 }383 fn params(&self) -> &[ParamParse] {387 fn params(&self) -> FunctionSignature {384 /// Safety: ParamParse contains IStr, which is thread-local, thus neither Send or Sync385 /// The result of this transmute can not outlive the thread, thus 'static here is equivalent to the386 /// nightly-only 'thread387 PARAMS.with(|p| unsafe { std::mem::transmute::<&[ParamParse], &'static [ParamParse]>(p.as_slice()) })388 PARAMS.with(|p| p.clone())388 }389 }389 #[allow(unused_variables)]390 #[allow(unused_variables)]390 fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {391 fn call(&self, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {crates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth7use jrsonnet_gcmodule::Acyclic;7use jrsonnet_gcmodule::Acyclic;8use jrsonnet_interner::IStr;8use jrsonnet_interner::IStr;9910use crate::source::Source;10use crate::{11 function::{FunctionSignature, ParamDefault, ParamName, ParamParse},12 source::Source,13};111412#[derive(Debug, PartialEq, Acyclic)]15#[derive(Debug, PartialEq, Acyclic)]13pub enum FieldName {16pub enum FieldName {41pub struct FieldMember {44pub struct FieldMember {42 pub name: FieldName,45 pub name: FieldName,43 pub plus: bool,46 pub plus: bool,44 pub params: Option<ParamsDesc>,47 pub params: Option<ExprParams>,45 pub visibility: Visibility,48 pub visibility: Visibility,46 pub value: Rc<Spanned<Expr>>,49 pub value: Rc<Spanned<Expr>>,47}50}147150148/// name, default value151/// name, default value149#[derive(Debug, PartialEq, Acyclic)]152#[derive(Debug, PartialEq, Acyclic)]150pub struct Param(pub Destruct, pub Option<Rc<Spanned<Expr>>>);153pub struct ExprParam {154 pub destruct: Destruct,155 pub default: Option<Rc<Spanned<Expr>>>,156}151157152/// Defined function parameters158/// Defined function parameters153#[derive(Debug, Clone, PartialEq, Acyclic)]159#[derive(Debug, Clone, PartialEq, Acyclic)]154pub struct ParamsDesc(pub Rc<Vec<Param>>);160pub struct ExprParams {155161 pub exprs: Rc<Vec<ExprParam>>,162 pub signature: FunctionSignature,163 binds_len: usize,164}156impl Deref for ParamsDesc {165impl ExprParams {157 type Target = Vec<Param>;166 pub fn len(&self) -> usize {167 self.exprs.len()168 }158 fn deref(&self) -> &Self::Target {169 pub fn binds_len(&self) -> usize {170 self.binds_len171 }172 pub fn new(exprs: Vec<ExprParam>) -> Self {173 Self {174 signature: FunctionSignature::new(175 exprs176 .iter()177 .map(|p| {178 ParamParse::new(179 p.destruct.name(),180 ParamDefault::exists(p.default.is_some()),181 )182 })183 .collect(),184 ),159 &self.0185 binds_len: exprs.iter().map(|v| v.destruct.binds_len()).sum(),186 exprs: Rc::new(exprs),187 }160 }188 }161}189}162190198}226}199impl Destruct {227impl Destruct {200 /// Name of destructure, used for function parameter names228 /// Name of destructure, used for function parameter names201 pub fn name(&self) -> Option<IStr> {229 pub fn name(&self) -> ParamName {202 match self {230 ParamName(match self {203 Self::Full(name) => Some(name.clone()),231 Self::Full(name) => Some(name.clone()),204 #[cfg(feature = "exp-destruct")]232 #[cfg(feature = "exp-destruct")]205 _ => None,233 _ => None,206 }234 })207 }235 }208 pub fn capacity_hint(&self) -> usize {236 pub fn binds_len(&self) -> usize {209 #[cfg(feature = "exp-destruct")]237 #[cfg(feature = "exp-destruct")]210 fn cap_rest(rest: &Option<DestructRest>) -> usize {238 fn cap_rest(rest: &Option<DestructRest>) -> usize {211 match rest {239 match rest {220 Self::Skip => 0,248 Self::Skip => 0,221 #[cfg(feature = "exp-destruct")]249 #[cfg(feature = "exp-destruct")]222 Self::Array { start, rest, end } => {250 Self::Array { start, rest, end } => {223 start.iter().map(Destruct::capacity_hint).sum::<usize>()251 start.iter().map(Destruct::binds_len).sum::<usize>()224 + end.iter().map(Destruct::capacity_hint).sum::<usize>()252 + end.iter().map(Destruct::binds_len).sum::<usize>()225 + cap_rest(rest)253 + cap_rest(rest)226 }254 }227 #[cfg(feature = "exp-destruct")]255 #[cfg(feature = "exp-destruct")]248 },276 },249 Function {277 Function {250 name: IStr,278 name: IStr,251 params: ParamsDesc,279 params: ExprParams,252 value: Rc<Spanned<Expr>>,280 value: Rc<Spanned<Expr>>,253 },281 },254}282}255impl BindSpec {283impl BindSpec {256 pub fn capacity_hint(&self) -> usize {284 pub fn binds_len(&self) -> usize {257 match self {285 match self {258 BindSpec::Field { into, .. } => into.capacity_hint(),286 BindSpec::Field { into, .. } => into.binds_len(),259 BindSpec::Function { .. } => 1,287 BindSpec::Function { .. } => 1,260 }288 }261 }289 }396 parts: Vec<IndexPart>,424 parts: Vec<IndexPart>,397 },425 },398 /// function(x) x426 /// function(x) x399 Function(ParamsDesc, Rc<Spanned<Expr>>),427 Function(ExprParams, Rc<Spanned<Expr>>),400 /// if true == false then 1 else 2428 /// if true == false then 1 else 2401 IfElse(Box<IfElse>),429 IfElse(Box<IfElse>),402 Slice(Box<Slice>),430 Slice(Box<Slice>),crates/jrsonnet-parser/src/function.rsdiffbeforeafterbothno changes
crates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth7pub use expr::*;7pub use expr::*;8pub use jrsonnet_interner::IStr;8pub use jrsonnet_interner::IStr;9pub use peg;9pub use peg;10pub mod function;10mod location;11mod location;11mod source;12mod source;12mod unescape;13mod unescape;68 rule keyword(id: &'static str) -> ()70 rule keyword(id: &'static str) -> ()69 = ##parse_string_literal(id) end_of_ident()71 = ##parse_string_literal(id) end_of_ident()707271 pub rule param(s: &ParserSettings) -> expr::Param = name:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name, expr.map(Rc::new)) }73 pub rule param(s: &ParserSettings) -> expr::ExprParam = destruct:destruct(s) expr:(_ "=" _ expr:expr(s){expr})? { expr::ExprParam { destruct, default: expr.map(Rc::new) } }72 pub rule params(s: &ParserSettings) -> expr::ParamsDesc74 pub rule params(s: &ParserSettings) -> expr::ExprParams73 = params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) }75 = params:param(s) ** comma() comma()? { expr::ExprParams::new(params) }74 / { expr::ParamsDesc(Rc::new(Vec::new())) }76 / { expr::ExprParams::new(Vec::new()) }757776 pub rule arg(s: &ParserSettings) -> (Option<IStr>, Rc<Spanned<Expr>>)78 pub rule arg(s: &ParserSettings) -> (Option<IStr>, Rc<Spanned<Expr>>)77 = name:(quiet! { (s:id() _ "=" !['='] _ {s})? } / expected!("<argument name>")) expr:expr(s) {(name, Rc::new(expr))}79 = name:(quiet! { (s:id() _ "=" !['='] _ {s})? } / expected!("<argument name>")) expr:expr(s) {(name, Rc::new(expr))}crates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__default_param_before_nondefault.snapdiffbeforeafterboth6 [6 [7 Function {7 Function {8 name: "x",8 name: "x",9 params: ParamsDesc(9 params: ExprParams {10 [10 exprs: [11 Param(11 ExprParam {12 Full(12 destruct: Full(13 "foo",13 "foo",14 ),14 ),15 Some(15 default: Some(16 Str(16 Str(17 "foo",17 "foo",18 ) from virtual:<test>:14-19,18 ) from virtual:<test>:14-19,19 ),19 ),20 ),20 },21 Param(21 ExprParam {22 Full(22 destruct: Full(23 "bar",23 "bar",24 ),24 ),25 None,25 default: None,26 ),26 },27 ],27 ],28 ),28 signature: FunctionSignature(29 [30 ParamParse {31 name: ParamName(32 Some(33 "foo",34 ),35 ),36 default: Exists,37 },38 ParamParse {39 name: ParamName(40 Some(41 "bar",42 ),43 ),44 default: None,45 },46 ],47 ),48 binds_len: 2,49 },29 value: Literal(50 value: Literal(30 Null,51 Null,31 ) from virtual:<test>:28-32,52 ) from virtual:<test>:28-32,