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

difftreelog

refactor fancier builtin param type

Yaroslav Bolyukin2023-08-06parent: #494be65.patch.diff
in: master

5 files changed

modifiedbindings/jsonnet/src/native.rsdiffbeforeafterboth
1use std::{1use std::{
2 borrow::Cow,
3 ffi::{c_void, CStr},2 ffi::{c_void, CStr},
4 os::raw::{c_char, c_int},3 os::raw::{c_char, c_int},
5};4};
82 let param = CStr::from_ptr(*raw_params)81 let param = CStr::from_ptr(*raw_params)
83 .to_str()82 .to_str()
84 .expect("param name is not utf-8");83 .expect("param name is not utf-8");
85 params.push(Cow::Owned(param.into()));84 params.push(param.into());
86 raw_params = raw_params.offset(1);85 raw_params = raw_params.offset(1);
87 }86 }
8887
modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
1use std::{any::Any, borrow::Cow};1use std::{any::Any, borrow::Cow};
22
3use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;
4use jrsonnet_interner::IStr;
45
5use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};6use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};
6use crate::{error::Result, gc::TraceBox, tb, Context, Val};7use crate::{error::Result, gc::TraceBox, tb, Context, Val};
78
9/// Can't have str | IStr, because constant BuiltinParam causes
10/// E0492: constant functions cannot refer to interior mutable data
11#[derive(Clone, Trace)]
8pub type BuiltinParamName = Cow<'static, str>;12pub struct ParamName(Option<Cow<'static, str>>);
13impl ParamName {
14 pub const ANONYMOUS: Self = Self(None);
15 pub const fn new_static(name: &'static str) -> Self {
16 Self(Some(Cow::Borrowed(name)))
17 }
18 pub fn new_dynamic(name: String) -> Self {
19 Self(Some(Cow::Owned(name)))
20 }
21 pub fn as_str(&self) -> Option<&str> {
22 self.0.as_deref()
23 }
24 pub fn is_anonymous(&self) -> bool {
25 self.0.is_none()
26 }
27}
28impl PartialEq<IStr> for ParamName {
29 fn eq(&self, other: &IStr) -> bool {
30 match &self.0 {
31 Some(s) => s.as_bytes() == other.as_bytes(),
32 None => false,
33 }
34 }
35}
936
10#[derive(Clone, Trace)]37#[derive(Clone, Trace)]
11pub struct BuiltinParam {38pub struct BuiltinParam {
12 /// Parameter name for named call parsing
13 pub name: Option<BuiltinParamName>,39 name: ParamName,
14 /// Is implementation allowed to return empty value
15 pub has_default: bool,40 has_default: bool,
16}41}
42impl BuiltinParam {
43 pub const fn new(name: ParamName, has_default: bool) -> Self {
44 Self { name, has_default }
45 }
46 /// Parameter name for named call parsing
47 pub fn name(&self) -> &ParamName {
48 &self.name
49 }
50 /// Is implementation allowed to return empty value
51 pub fn has_default(&self) -> bool {
52 self.has_default
53 }
54}
1755
18/// Description of function defined by native code56/// Description of function defined by native code
19///57///
44}82}
45impl NativeCallback {83impl NativeCallback {
46 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]84 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]
47 pub fn new(params: Vec<Cow<'static, str>>, handler: impl NativeCallbackHandler) -> Self {85 pub fn new(params: Vec<String>, handler: impl NativeCallbackHandler) -> Self {
48 Self {86 Self {
49 params: params87 params: params
50 .into_iter()88 .into_iter()
51 .map(|n| BuiltinParam {89 .map(|n| BuiltinParam {
52 name: Some(n),90 name: ParamName::new_dynamic(n.to_string()),
53 has_default: false,91 has_default: false,
54 })92 })
55 .collect(),93 .collect(),
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
88
9use self::{9use self::{
10 arglike::OptionalContext,10 arglike::OptionalContext,
11 builtin::{Builtin, StaticBuiltin},11 builtin::{Builtin, BuiltinParam, ParamName, StaticBuiltin},
12 native::NativeDesc,12 native::NativeDesc,
13 parse::{parse_default_function_call, parse_function_call},13 parse::{parse_default_function_call, parse_function_call},
14};14};
113 }113 }
114}114}
115
116#[allow(clippy::unnecessary_wraps)]
117#[builtin]
118const fn builtin_id(x: Val) -> Val {
119 x
120}
121static ID: &builtin_id = &builtin_id {};
115122
116impl FuncVal {123impl FuncVal {
117 pub fn builtin(builtin: impl Builtin) -> Self {124 pub fn builtin(builtin: impl Builtin) -> Self {
118 Self::Builtin(Cc::new(tb!(builtin)))125 Self::Builtin(Cc::new(tb!(builtin)))
119 }126 }
127
128 pub fn params(&self) -> Vec<BuiltinParam> {
129 match self {
130 Self::Id => ID.params().to_vec(),
131 Self::StaticBuiltin(i) => i.params().to_vec(),
132 Self::Builtin(i) => i.params().to_vec(),
133 Self::Normal(p) => p
134 .params
135 .iter()
136 .map(|p| {
137 BuiltinParam::new(
138 p.0.name()
139 .as_ref()
140 .map(IStr::to_string)
141 .map_or(ParamName::ANONYMOUS, ParamName::new_dynamic),
142 p.1.is_some(),
143 )
144 })
145 .collect(),
146 }
147 }
120 /// Amount of non-default required arguments148 /// Amount of non-default required arguments
121 pub fn params_len(&self) -> usize {149 pub fn params_len(&self) -> usize {
122 match self {150 match self {
123 Self::Id => 1,151 Self::Id => 1,
124 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),152 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),
125 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),153 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default()).count(),
126 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),154 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default()).count(),
127 }155 }
128 }156 }
129 /// Function name, as defined in code.157 /// Function name, as defined in code.
146 tailstrict: bool,174 tailstrict: bool,
147 ) -> Result<Val> {175 ) -> Result<Val> {
148 match self {176 match self {
149 Self::Id => {177 Self::Id => ID.call(call_ctx, loc, args),
150 #[allow(clippy::unnecessary_wraps)]
151 #[builtin]
152 const fn builtin_id(x: Val) -> Val {
153 x
154 }
155 static ID: &builtin_id = &builtin_id {};
156
157 ID.call(call_ctx, loc, args)
158 }
159 Self::Normal(func) => {178 Self::Normal(func) => {
160 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;179 let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;
161 evaluate(body_ctx, &func.body)180 evaluate(body_ctx, &func.body)
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
163 params.len(),163 params.len(),
164 params164 params
165 .iter()165 .iter()
166 .map(|p| (p.name.as_ref().map(|v| v.as_ref().into()), p.has_default))166 .map(|p| (p.name().as_str().map(IStr::from), p.has_default()))
167 .collect()167 .collect()
168 ))168 ))
169 }169 }
180 // FIXME: O(n) for arg existence check180 // FIXME: O(n) for arg existence check
181 let id = params181 let id = params
182 .iter()182 .iter()
183 .position(|p| p.name.as_ref().map_or(false, |v| v as &str == name as &str))183 .position(|p| p.name() == name)
184 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;184 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;
185 if replace(&mut passed_args[id], Some(arg)).is_some() {185 if replace(&mut passed_args[id], Some(arg)).is_some() {
186 throw!(BindingParameterASecondTime(name.clone()));186 throw!(BindingParameterASecondTime(name.clone()));
190 })?;190 })?;
191191
192 if filled_args < params.len() {192 if filled_args < params.len() {
193 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default) {193 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default()) {
194 if passed_args[id].is_some() {194 if passed_args[id].is_some() {
195 continue;195 continue;
196 }196 }
204 args.named_names(&mut |name| {204 args.named_names(&mut |name| {
205 if param205 if param.name() == name {
206 .name
207 .as_ref()
208 .map_or(false, |v| v as &str == name as &str)
209 {
210 found = true;206 found = true;
211 }207 }
212 });208 });
213 if !found {209 if !found {
214 throw!(FunctionParameterNotBoundInCall(210 throw!(FunctionParameterNotBoundInCall(
215 param.name.as_ref().map(|v| v.as_ref().into()),211 param.name().as_str().map(IStr::from),
216 params212 params
217 .iter()213 .iter()
218 .map(|p| (p.name.as_ref().map(|p| p.as_ref().into()), p.has_default))214 .map(|p| (p.name().as_str().map(IStr::from), p.has_default()))
219 .collect()215 .collect()
220 ));216 ));
221 }217 }
modifiedtests/tests/common.rsdiffbeforeafterboth
1use std::borrow::Cow;
2
3use jrsonnet_evaluator::{1use jrsonnet_evaluator::{
4 error::Result,2 error::Result,
66 FuncVal::StaticBuiltin(b) => b64 FuncVal::StaticBuiltin(b) => b
67 .params()65 .params()
68 .iter()66 .iter()
69 .map(|p| {67 .map(|p| p.name().as_str().unwrap_or(&"<unnamed>").to_string())
70 p.name
71 .as_ref()
72 .unwrap_or(&Cow::Borrowed("<unnamed>"))
73 .to_string()
74 })
75 .collect(),68 .collect(),
76 FuncVal::Builtin(b) => b69 FuncVal::Builtin(b) => b
77 .params()70 .params()
78 .iter()71 .iter()
79 .map(|p| {72 .map(|p| p.name().as_str().unwrap_or(&"<unnamed>").to_string())
80 p.name
81 .as_ref()
82 .unwrap_or(&Cow::Borrowed("<unnamed>"))
83 .to_string()
84 })
85 .collect(),73 .collect(),
86 }74 }
87}75}