git.delta.rocks / jrsonnet / refs/commits / b915f23c48a1

difftreelog

refactor prepared signatures in IR

zvuulvvlYaroslav Bolyukin2026-03-21parent: #df5053d.patch.diff
in: master

15 files changed

modifiedcrates/jrsonnet-evaluator/src/async_import.rsdiffbeforeafterboth
33
4use 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 }
modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
180 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 self
182 }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 self
188 }
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)
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
1use 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};
66
7use jrsonnet_gcmodule::{Acyclic, Trace};7use jrsonnet_gcmodule::{Acyclic, Trace};
11use thiserror::Error;11use thiserror::Error;
1212
13use 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 out
48}48}
49
50fn 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 out
78}
7949
80const 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}
106
107type FunctionSignature = Vec<(Option<IStr>, ParamDefault)>;
10876
109/// Possible errors77/// Possible errors
110#[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),
158126
159 #[error("external variable is not defined: {0}")]127 #[error("external variable is not defined: {0}")]
160 UndefinedExternalVariable(IStr),128 UndefinedExternalVariable(IStr),
modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
170 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 ))
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
3use 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;
1112
12use 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: params
81 .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);
131134
178 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}
381
382pub 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}
378388
379pub 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)?;
modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
1use std::any::Any;1use std::any::Any;
2use std::fmt;
23
3use 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};
57
6use 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};
8
9#[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.0
26 .as_ref()
27 .map_or(false, |s| s.as_bytes() == other.as_bytes())
28 }
29}
30
31#[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::Exists
41 } else {
42 Self::None
43 }
44 }
45}
4610
47#[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}
59
60#[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 parsing
70 pub fn name(&self) -> &ParamName {
71 &self.name
72 }
73 pub fn default(&self) -> ParamDefault {
74 self.default
75 }
76 pub fn has_default(&self) -> bool {
77 !matches!(self.default, ParamDefault::None)
78 }
79}
8023
81cc_dyn!(24cc_dyn!(
82 #[derive(Clone)]25 #[derive(Clone)]
89 self.0.name()32 self.0.name()
90 }33 }
9134
92 fn params(&self) -> &[ParamParse] {35 fn params(&self) -> FunctionSignature {
93 self.0.params()36 self.0.params()
94 }37 }
9538
109 /// Function name to be used in stack traces52 /// Function name to be used in stack traces
110 fn name(&self) -> &str;53 fn name(&self) -> &str;
111 /// Parameter names for named calls54 /// Parameter names for named calls
112 fn params(&self) -> &[ParamParse];55 fn params(&self) -> FunctionSignature;
113 /// Call the builtin56 /// Call the builtin
114 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>;
11558
12669
127#[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 params
137 .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 }
15496
155 fn params(&self) -> &[ParamParse] {97 fn params(&self) -> FunctionSignature {
156 &self.params98 self.params.clone()
157 }99 }
158100
159 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 = args
162 .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"))
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
5use 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};
99
10use 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};
2020
21pub 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;
26
27pub use jrsonnet_parser::function::*;
2528
26/// 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,
6770
68 /// Function parameter definition71 /// Function parameter definition
69 pub params: ParamsDesc,72 pub params: ExprParams,
70 /// Function body73 /// Function body
71 pub body: Rc<Spanned<Expr>>,74 pub body: Rc<Spanned<Expr>>,
72
73 #[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 error
139 Self::StaticBuiltin(static_builtin)139 Self::StaticBuiltin(static_builtin)
140 }140 }
141141
142 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 arguments
152 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 }
224
230 #[allow(clippy::infallible_destructuring_match)]225 #[allow(clippy::infallible_destructuring_match)]
231 let id = match &param.0 {226 let id = match &param.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,
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
1use std::mem::replace;1use std::mem::replace;
22
3use 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;
66
7use 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 }
4439
45 let mut filled_named = 0;40 let mut filled_named = 0;
46 let mut filled_positionals = 0;41 let mut filled_positionals = 0;
4742
48 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 &params.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,
5953
60 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 check
62 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 context
75 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_named
79 - filled_positionals,
80 );
8172
82 for (idx, param) in params.iter().enumerate().filter(|p| p.1 .1.is_some()) {73 for (idx, into, default) in params
74 .exprs
75 .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 }
9086
91 destruct(87 destruct(
92 &param.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;
108104
109 // Some args still weren't filled105 // Some args still weren't filled
110 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 &param.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 - lazily
148pub 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 params
159 .iter()
160 .map(|p| (p.name().as_str().map(IStr::from), p.default()))
161 .collect()
162 ))
163 }150 }
164151
175 let id = params162 let id = params
176 .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 }
218202
219/// Creates Context, which has all argument default values applied203/// Creates Context, which has all argument default values applied
220/// and with unbound values causing error to be returned204/// and with unbound values causing error to be returned
221pub 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();
223207
224 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());
225209
226 for param in params.iter() {210 for param in params.exprs.iter() {
227 if let Some(v) = &param.1 {211 if let Some(v) = &param.default {
228 destruct(212 destruct(
229 &param.0.clone(),213 &param.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 &param.0,225 &param.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 },
addedcrates/jrsonnet-evaluator/src/function/prepared.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-evaluator/src/stack.rsdiffbeforeafterboth
20type NightlyLocalKey<T> = std::thread::LocalKey<T>;20type NightlyLocalKey<T> = std::thread::LocalKey<T>;
2121
22#[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! {
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
364 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 Sync
385 /// The result of this transmute can not outlive the thread, thus 'static here is equivalent to the
386 /// nightly-only 'thread
387 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> {
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
7use jrsonnet_gcmodule::Acyclic;7use jrsonnet_gcmodule::Acyclic;
8use jrsonnet_interner::IStr;8use jrsonnet_interner::IStr;
99
10use crate::source::Source;10use crate::{
11 function::{FunctionSignature, ParamDefault, ParamName, ParamParse},
12 source::Source,
13};
1114
12#[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}
147150
148/// name, default value151/// name, default value
149#[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}
151157
152/// Defined function parameters158/// Defined function parameters
153#[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_len
171 }
172 pub fn new(exprs: Vec<ExprParam>) -> Self {
173 Self {
174 signature: FunctionSignature::new(
175 exprs
176 .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}
162190
198}226}
199impl Destruct {227impl Destruct {
200 /// Name of destructure, used for function parameter names228 /// Name of destructure, used for function parameter names
201 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) x
399 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 2
401 IfElse(Box<IfElse>),429 IfElse(Box<IfElse>),
402 Slice(Box<Slice>),430 Slice(Box<Slice>),
addedcrates/jrsonnet-parser/src/function.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
7pub 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()
7072
71 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::ExprParams
73 = 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()) }
7577
76 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))}
modifiedcrates/jrsonnet-parser/src/snapshots/jrsonnet_parser__tests__default_param_before_nondefault.snapdiffbeforeafterboth
6 [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,