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

difftreelog

refactor move builtins to HashMap

Yaroslav Bolyukin2021-01-06parent: #a7f8b8f.patch.diff
in: master

1 file changed

modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
8use format::{format_arr, format_obj};8use format::{format_arr, format_obj};
9use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};9use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};
10use jrsonnet_types::ty;10use jrsonnet_types::ty;
11use std::{path::PathBuf, rc::Rc};11use std::{collections::HashMap, path::PathBuf, rc::Rc};
1212
13pub mod stdlib;13pub mod stdlib;
14pub use stdlib::*;14pub use stdlib::*;
33 )33 )
34}34}
35
36type Builtin = fn(context: Context, loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val>;
37
38type BuiltinsType = HashMap<Box<str>, Builtin>;
39
40thread_local! {
41 static BUILTINS: BuiltinsType = {
42 [
43 ("length".into(), builtin_length as Builtin),
44 ("type".into(), builtin_type),
45 ("makeArray".into(), builtin_make_array),
46 ("codepoint".into(), builtin_codepoint),
47 ("objectFieldsEx".into(), builtin_object_fields_ex),
48 ("objectHasEx".into(), builtin_object_has_ex),
49 ("slice".into(), builtin_slice),
50 ("primitiveEquals".into(), builtin_primitive_equals),
51 ("equals".into(), builtin_equals),
52 ("modulo".into(), builtin_modulo),
53 ("mod".into(), builtin_mod),
54 ("floor".into(), builtin_floor),
55 ("log".into(), builtin_log),
56 ("pow".into(), builtin_pow),
57 ("extVar".into(), builtin_ext_var),
58 ("native".into(), builtin_native),
59 ("filter".into(), builtin_filter),
60 ("foldl".into(), builtin_foldl),
61 ("foldr".into(), builtin_foldr),
62 ("sortImpl".into(), builtin_sort_impl),
63 ("format".into(), builtin_format),
64 ("range".into(), builtin_range),
65 ("char".into(), builtin_char),
66 ("encodeUTF8".into(), builtin_encode_utf8),
67 ("md5".into(), builtin_md5),
68 ("base64".into(), builtin_base64),
69 ("trace".into(), builtin_trace),
70 ("join".into(), builtin_join),
71 ("escapeStringJson".into(), builtin_escape_string_json),
72 ("manifestJsonEx".into(), builtin_manifest_json_ex),
73 ("reverse".into(), builtin_reverse),
74 ("id".into(), builtin_id),
75 ].iter().cloned().collect()
76 };
77}
78
79fn builtin_length(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
80 parse_args!(context, "length", args, 1, [
81 0, x: ty!((str | obj | [any]));
82 ], {
83 Ok(match x {
84 Val::Str(n) => Val::Num(n.chars().count() as f64),
85 Val::Arr(a) => Val::Num(a.len() as f64),
86 Val::Obj(o) => Val::Num(
87 o.fields_visibility()
88 .into_iter()
89 .filter(|(_k, v)| *v)
90 .count() as f64,
91 ),
92 _ => unreachable!(),
93 })
94 })
95}
96
97fn builtin_type(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
98 parse_args!(context, "type", args, 1, [
99 0, x: ty!(any);
100 ], {
101 Ok(Val::Str(x.value_type().name().into()))
102 })
103}
104
105fn builtin_make_array(
106 context: Context,
107 _loc: &Option<ExprLocation>,
108 args: &ArgsDesc,
109) -> Result<Val> {
110 parse_args!(context, "makeArray", args, 2, [
111 0, sz: ty!(number((Some(0.0))..(None))) => Val::Num;
112 1, func: ty!(fn.any) => Val::Func;
113 ], {
114 let mut out = Vec::with_capacity(sz as usize);
115 for i in 0..sz as usize {
116 out.push(LazyVal::new_resolved(func.evaluate_values(
117 Context::new(),
118 &[Val::Num(i as f64)]
119 )?))
120 }
121 Ok(Val::Arr(out.into()))
122 })
123}
124
125fn builtin_codepoint(
126 context: Context,
127 _loc: &Option<ExprLocation>,
128 args: &ArgsDesc,
129) -> Result<Val> {
130 parse_args!(context, "codepoint", args, 1, [
131 0, str: ty!(char) => Val::Str;
132 ], {
133 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))
134 })
135}
136
137fn builtin_object_fields_ex(
138 context: Context,
139 _loc: &Option<ExprLocation>,
140 args: &ArgsDesc,
141) -> Result<Val> {
142 parse_args!(context, "objectFieldsEx", args, 2, [
143 0, obj: ty!(obj) => Val::Obj;
144 1, inc_hidden: ty!(bool) => Val::Bool;
145 ], {
146 let mut out = obj.fields_visibility()
147 .into_iter()
148 .filter(|(_k, v)| *v || inc_hidden)
149 .map(|(k, _v)|k)
150 .collect::<Vec<_>>();
151 out.sort();
152 Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))
153 })
154}
155
156fn builtin_object_has_ex(
157 context: Context,
158 _loc: &Option<ExprLocation>,
159 args: &ArgsDesc,
160) -> Result<Val> {
161 parse_args!(context, "objectHasEx", args, 3, [
162 0, obj: ty!(obj) => Val::Obj;
163 1, f: ty!(str) => Val::Str;
164 2, inc_hidden: ty!(bool) => Val::Bool;
165 ], {
166 Ok(Val::Bool(
167 obj.fields_visibility()
168 .into_iter()
169 .filter(|(_k, v)| *v || inc_hidden)
170 .any(|(k, _v)| *k == *f),
171 ))
172 })
173}
174
175// faster
176fn builtin_slice(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
177 parse_args!(context, "slice", args, 4, [
178 0, indexable: ty!((str | [any]));
179 1, index: ty!((num | null));
180 2, end: ty!((num | null));
181 3, step: ty!((num | null));
182 ], {
183 let index = match index {
184 Val::Num(v) => v as usize,
185 Val::Null => 0,
186 _ => unreachable!(),
187 };
188 let end = match end {
189 Val::Num(v) => v as usize,
190 Val::Null => match &indexable {
191 Val::Str(s) => s.chars().count(),
192 Val::Arr(v) => v.len(),
193 _ => unreachable!()
194 },
195 _ => unreachable!()
196 };
197 let step = match step {
198 Val::Num(v) => v as usize,
199 Val::Null => 1,
200 _ => unreachable!()
201 };
202 match &indexable {
203 Val::Str(s) => {
204 Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))
205 }
206 Val::Arr(arr) => {
207 Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))
208 }
209 _ => unreachable!()
210 }
211 })
212}
213
214// faster
215fn builtin_primitive_equals(
216 context: Context,
217 _loc: &Option<ExprLocation>,
218 args: &ArgsDesc,
219) -> Result<Val> {
220 parse_args!(context, "primitiveEquals", args, 2, [
221 0, a: ty!(any);
222 1, b: ty!(any);
223 ], {
224 Ok(Val::Bool(primitive_equals(&a, &b)?))
225 })
226}
227
228// faster
229fn builtin_equals(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
230 parse_args!(context, "equals", args, 2, [
231 0, a: ty!(any);
232 1, b: ty!(any);
233 ], {
234 Ok(Val::Bool(equals(&a, &b)?))
235 })
236}
237
238fn builtin_modulo(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
239 parse_args!(context, "modulo", args, 2, [
240 0, a: ty!(num) => Val::Num;
241 1, b: ty!(num) => Val::Num;
242 ], {
243 Ok(Val::Num(a % b))
244 })
245}
246
247fn builtin_mod(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
248 parse_args!(context, "mod", args, 2, [
249 0, a: ty!((num | str));
250 1, b: ty!(any);
251 ], {
252 match (a, b) {
253 (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),
254 (Val::Str(str), vals) => std_format(str, vals),
255 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))
256 }
257 })
258}
259
260fn builtin_floor(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
261 parse_args!(context, "floor", args, 1, [
262 0, x: ty!(num) => Val::Num;
263 ], {
264 Ok(Val::Num(x.floor()))
265 })
266}
267
268fn builtin_log(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
269 parse_args!(context, "log", args, 1, [
270 0, n: ty!(num) => Val::Num;
271 ], {
272 Ok(Val::Num(n.ln()))
273 })
274}
275
276fn builtin_pow(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
277 parse_args!(context, "pow", args, 2, [
278 0, x: ty!(num) => Val::Num;
279 1, n: ty!(num) => Val::Num;
280 ], {
281 Ok(Val::Num(x.powf(n)))
282 })
283}
284
285fn builtin_ext_var(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
286 parse_args!(context, "extVar", args, 1, [
287 0, x: ty!(str) => Val::Str;
288 ], {
289 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)
290 })
291}
292
293fn builtin_native(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
294 parse_args!(context, "native", args, 1, [
295 0, x: ty!(str) => Val::Str;
296 ], {
297 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)
298 })
299}
300
301fn builtin_filter(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
302 parse_args!(context, "filter", args, 2, [
303 0, func: ty!(fn.any) => Val::Func;
304 1, arr: ty!([any]) => Val::Arr;
305 ], {
306 let mut out = Vec::new();
307 for item in arr.iter() {
308 let item = item?;
309 if func
310 .evaluate_values(context.clone(), &[item.clone()])?
311 .try_cast_bool("filter predicate")? {
312 out.push(item);
313 }
314 }
315 Ok(Val::Arr(out.into()))
316 })
317}
318
319fn builtin_foldl(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
320 parse_args!(context, "foldl", args, 3, [
321 0, func: ty!(fn.any) => Val::Func;
322 1, arr: ty!([any]) => Val::Arr;
323 2, init: ty!(any);
324 ], {
325 let mut acc = init;
326 for i in arr.iter() {
327 acc = func.evaluate_values(context.clone(), &[acc, i?])?;
328 }
329 Ok(acc)
330 })
331}
332
333fn builtin_foldr(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
334 parse_args!(context, "foldr", args, 3, [
335 0, func: ty!(fn.any) => Val::Func;
336 1, arr: ty!([any]) => Val::Arr;
337 2, init: ty!(any);
338 ], {
339 let mut acc = init;
340 for i in arr.iter().rev() {
341 acc = func.evaluate_values(context.clone(), &[acc, i?])?;
342 }
343 Ok(acc)
344 })
345}
346
347#[allow(non_snake_case)]
348fn builtin_sort_impl(
349 context: Context,
350 _loc: &Option<ExprLocation>,
351 args: &ArgsDesc,
352) -> Result<Val> {
353 parse_args!(context, "sort", args, 2, [
354 0, arr: ty!([any]) => Val::Arr;
355 1, keyF: ty!(fn.any) => Val::Func;
356 ], {
357 if arr.len() <= 1 {
358 return Ok(Val::Arr(arr))
359 }
360 Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))
361 })
362}
363
364// faster
365fn builtin_format(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
366 parse_args!(context, "format", args, 2, [
367 0, str: ty!(str) => Val::Str;
368 1, vals: ty!(any)
369 ], {
370 std_format(str, vals)
371 })
372}
373
374fn builtin_range(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
375 parse_args!(context, "range", args, 2, [
376 0, from: ty!(num) => Val::Num;
377 1, to: ty!(num) => Val::Num;
378 ], {
379 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));
380 for i in from as usize..=to as usize {
381 out.push(Val::Num(i as f64));
382 }
383 Ok(Val::Arr(out.into()))
384 })
385}
386
387fn builtin_char(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
388 parse_args!(context, "char", args, 1, [
389 0, n: ty!(num) => Val::Num;
390 ], {
391 let mut out = String::new();
392 out.push(std::char::from_u32(n as u32).ok_or_else(||
393 InvalidUnicodeCodepointGot(n as u32)
394 )?);
395 Ok(Val::Str(out.into()))
396 })
397}
398
399fn builtin_encode_utf8(
400 context: Context,
401 _loc: &Option<ExprLocation>,
402 args: &ArgsDesc,
403) -> Result<Val> {
404 parse_args!(context, "encodeUTF8", args, 1, [
405 0, str: ty!(str) => Val::Str;
406 ], {
407 Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))
408 })
409}
410
411fn builtin_md5(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
412 parse_args!(context, "md5", args, 1, [
413 0, str: ty!(str) => Val::Str;
414 ], {
415 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))
416 })
417}
418
419fn builtin_trace(context: Context, loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
420 parse_args!(context, "trace", args, 2, [
421 0, str: ty!(str) => Val::Str;
422 1, rest: ty!(any);
423 ], {
424 eprint!("TRACE:");
425 if let Some(loc) = loc {
426 with_state(|s|{
427 let locs = s.map_source_locations(&loc.0, &[loc.1]);
428 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);
429 });
430 }
431 eprintln!(" {}", str);
432 Ok(rest)
433 })
434}
435
436fn builtin_base64(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
437 parse_args!(context, "base64", args, 1, [
438 0, input: ty!((str | [num]));
439 ], {
440 Ok(Val::Str(match input {
441 Val::Str(s) => {
442 base64::encode(s.bytes().collect::<Vec<_>>()).into()
443 },
444 Val::Arr(a) => {
445 base64::encode(a.iter().map(|v| {
446 Ok(v?.clone().unwrap_num()? as u8)
447 }).collect::<Result<Vec<_>>>()?).into()
448 },
449 _ => unreachable!()
450 }))
451 })
452}
453
454fn builtin_join(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
455 parse_args!(context, "join", args, 2, [
456 0, sep: ty!((str | [any]));
457 1, arr: ty!([any]) => Val::Arr;
458 ], {
459 Ok(match sep {
460 Val::Arr(joiner_items) => {
461 let mut out = Vec::new();
462
463 let mut first = true;
464 for item in arr.iter() {
465 let item = item?.clone();
466 if let Val::Arr(items) = item {
467 if !first {
468 out.reserve(joiner_items.len());
469 // TODO: extend
470 for item in joiner_items.iter() {
471 out.push(item?);
472 }
473 }
474 first = false;
475 out.reserve(items.len());
476 // TODO: extend
477 for item in items.iter() {
478 out.push(item?);
479 }
480 } else {
481 throw!(RuntimeError("in std.join all items should be arrays".into()));
482 }
483 }
484
485 Val::Arr(out.into())
486 },
487 Val::Str(sep) => {
488 let mut out = String::new();
489
490 let mut first = true;
491 for item in arr.iter() {
492 let item = item?.clone();
493 if let Val::Str(item) = item {
494 if !first {
495 out += &sep;
496 }
497 first = false;
498 out += &item;
499 } else {
500 throw!(RuntimeError("in std.join all items should be strings".into()));
501 }
502 }
503
504 Val::Str(out.into())
505 },
506 _ => unreachable!()
507 })
508 })
509}
510
511// faster
512fn builtin_escape_string_json(
513 context: Context,
514 _loc: &Option<ExprLocation>,
515 args: &ArgsDesc,
516) -> Result<Val> {
517 parse_args!(context, "escapeStringJson", args, 1, [
518 0, str_: ty!(str) => Val::Str;
519 ], {
520 Ok(Val::Str(escape_string_json(&str_).into()))
521 })
522}
523
524// faster
525fn builtin_manifest_json_ex(
526 context: Context,
527 _loc: &Option<ExprLocation>,
528 args: &ArgsDesc,
529) -> Result<Val> {
530 parse_args!(context, "manifestJsonEx", args, 2, [
531 0, value: ty!(any);
532 1, indent: ty!(str) => Val::Str;
533 ], {
534 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {
535 padding: &indent,
536 mtype: ManifestType::Std,
537 })?.into()))
538 })
539}
540
541// faster
542fn builtin_reverse(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
543 parse_args!(context, "reverse", args, 1, [
544 0, value: ty!([any]) => Val::Arr;
545 ], {
546 Ok(Val::Arr(value.reversed()))
547 })
548}
549
550fn builtin_id(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
551 parse_args!(context, "id", args, 1, [
552 0, v: ty!(any);
553 ], {
554 Ok(v)
555 })
556}
35557
36#[allow(clippy::cognitive_complexity)]558#[allow(clippy::cognitive_complexity)]
37pub fn call_builtin(559pub fn call_builtin(
40 name: &str,562 name: &str,
41 args: &ArgsDesc,563 args: &ArgsDesc,
42) -> Result<Val> {564) -> Result<Val> {
43 Ok(match name as &str {
44 "length" => parse_args!(context, "length", args, 1, [
45 0, x: ty!((str | obj | [any]));
46 ], {
47 Ok(match x {
48 Val::Str(n) => Val::Num(n.chars().count() as f64),
49 Val::Arr(a) => Val::Num(a.len() as f64),
50 Val::Obj(o) => Val::Num(
51 o.fields_visibility()
52 .into_iter()
53 .filter(|(_k, v)| *v)
54 .count() as f64,
55 ),
56 _ => unreachable!(),
57 })
58 })?,
59 "type" => parse_args!(context, "type", args, 1, [
60 0, x: ty!(any);
61 ], {
62 Ok(Val::Str(x.value_type().name().into()))
63 })?,
64 "makeArray" => parse_args!(context, "makeArray", args, 2, [
65 0, sz: ty!(number((Some(0.0))..(None))) => Val::Num;
66 1, func: ty!(fn.any) => Val::Func;
67 ], {
68 let mut out = Vec::with_capacity(sz as usize);
69 for i in 0..sz as usize {
70 out.push(LazyVal::new_resolved(func.evaluate_values(
71 Context::new(),
72 &[Val::Num(i as f64)]
73 )?))
74 }
75 Ok(Val::Arr(out.into()))
76 })?,
77 "codepoint" => parse_args!(context, "codepoint", args, 1, [
78 0, str: ty!(char) => Val::Str;
79 ], {
80 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))
81 })?,
82 "objectFieldsEx" => parse_args!(context, "objectFieldsEx", args, 2, [
83 0, obj: ty!(obj) => Val::Obj;
84 1, inc_hidden: ty!(bool) => Val::Bool;
85 ], {
86 let mut out = obj.fields_visibility()
87 .into_iter()
88 .filter(|(_k, v)| *v || inc_hidden)
89 .map(|(k, _v)|k)
90 .collect::<Vec<_>>();
91 out.sort();
92 Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))
93 })?,
94 "objectHasEx" => parse_args!(context, "objectHasEx", args, 3, [
95 0, obj: ty!(obj) => Val::Obj;
96 1, f: ty!(str) => Val::Str;
97 2, inc_hidden: ty!(bool) => Val::Bool;
98 ], {
99 Ok(Val::Bool(
100 obj.fields_visibility()
101 .into_iter()
102 .filter(|(_k, v)| *v || inc_hidden)
103 .any(|(k, _v)| *k == *f),
104 ))
105 })?,
106 // faster
107 "slice" => parse_args!(context, "slice", args, 4, [
108 0, indexable: ty!((str | [any]));
109 1, index: ty!((num | null));
110 2, end: ty!((num | null));
111 3, step: ty!((num | null));
112 ], {
113 let index = match index {
114 Val::Num(v) => v as usize,
115 Val::Null => 0,
116 _ => unreachable!(),
117 };
118 let end = match end {
119 Val::Num(v) => v as usize,
120 Val::Null => match &indexable {
121 Val::Str(s) => s.chars().count(),
122 Val::Arr(v) => v.len(),
123 _ => unreachable!()
124 },
125 _ => unreachable!()
126 };
127 let step = match step {
128 Val::Num(v) => v as usize,
129 Val::Null => 1,
130 _ => unreachable!()
131 };
132 match &indexable {
133 Val::Str(s) => {
134 Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))
135 }
136 Val::Arr(arr) => {
137 Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))
138 }
139 _ => unreachable!()
140 }
141 })?,
142 "primitiveEquals" => parse_args!(context, "primitiveEquals", args, 2, [
143 0, a: ty!(any);
144 1, b: ty!(any);
145 ], {
146 Ok(Val::Bool(primitive_equals(&a, &b)?))
147 })?,
148 // faster
149 "equals" => parse_args!(context, "equals", args, 2, [
150 0, a: ty!(any);
151 1, b: ty!(any);
152 ], {
153 Ok(Val::Bool(equals(&a, &b)?))
154 })?,
155 "modulo" => parse_args!(context, "modulo", args, 2, [
156 0, a: ty!(num) => Val::Num;
157 1, b: ty!(num) => Val::Num;
158 ], {
159 Ok(Val::Num(a % b))
160 })?,
161 "mod" => parse_args!(context, "mod", args, 2, [
162 0, a: ty!((num | str));
163 1, b: ty!(any);
164 ], {
165 match (a, b) {
166 (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),
167 (Val::Str(str), vals) => std_format(str, vals),
168 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))
169 }
170 })?,
171 "floor" => parse_args!(context, "floor", args, 1, [
172 0, x: ty!(num) => Val::Num;
173 ], {
174 Ok(Val::Num(x.floor()))
175 })?,
176 "log" => parse_args!(context, "log", args, 1, [
177 0, n: ty!(num) => Val::Num;
178 ], {
179 Ok(Val::Num(n.ln()))
180 })?,
181 "trace" => parse_args!(context, "trace", args, 2, [
182 0, str: ty!(str) => Val::Str;
183 1, rest: ty!(any);
184 ], {
185 eprint!("TRACE:");
186 if let Some(loc) = loc {565 if let Some(f) = BUILTINS.with(|builtins| builtins.get(name).map(|f| *f)) {
187 with_state(|s|{
188 let locs = s.map_source_locations(&loc.0, &[loc.1]);
189 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);
190 });
191 }
192 eprintln!(" {}", str);
193 Ok(rest)
194 })?,
195 "pow" => parse_args!(context, "pow", args, 2, [
196 0, x: ty!(num) => Val::Num;
197 1, n: ty!(num) => Val::Num;
198 ], {
199 Ok(Val::Num(x.powf(n)))
200 })?,
201 "extVar" => parse_args!(context, "extVar", args, 1, [
202 0, x: ty!(str) => Val::Str;
203 ], {
204 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)
205 })?,
206 "native" => parse_args!(context, "native", args, 1, [
207 0, x: ty!(str) => Val::Str;
208 ], {
209 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)
210 })?,
211 "filter" => parse_args!(context, "filter", args, 2, [
212 0, func: ty!(fn.any) => Val::Func;
213 1, arr: ty!([any]) => Val::Arr;
214 ], {
215 let mut out = Vec::new();
216 for item in arr.iter() {
217 let item = item?;
218 if func
219 .evaluate_values(context.clone(), &[item.clone()])?
220 .try_cast_bool("filter predicate")? {
221 out.push(item);
222 }
223 }
224 Ok(Val::Arr(out.into()))
225 })?,
226 "foldl" => parse_args!(context, "foldl", args, 3, [
227 0, func: ty!(fn.any) => Val::Func;
228 1, arr: ty!([any]) => Val::Arr;
229 2, init: ty!(any);
230 ], {
231 let mut acc = init;
232 for i in arr.iter() {
233 acc = func.evaluate_values(context.clone(), &[acc, i?])?;
234 }
235 Ok(acc)
236 })?,
237 "foldr" => parse_args!(context, "foldr", args, 3, [
238 0, func: ty!(fn.any) => Val::Func;
239 1, arr: ty!([any]) => Val::Arr;
240 2, init: ty!(any);
241 ], {
242 let mut acc = init;
243 for i in arr.iter().rev() {
244 acc = func.evaluate_values(context.clone(), &[acc, i?])?;
245 }
246 Ok(acc)
247 })?,
248 #[allow(non_snake_case)]
249 "sortImpl" => parse_args!(context, "sort", args, 2, [
250 0, arr: ty!([any]) => Val::Arr;
251 1, keyF: ty!(fn.any) => Val::Func;
252 ], {
253 if arr.len() <= 1 {
254 return Ok(Val::Arr(arr))566 return Ok(f(context, loc, args)?);
255 }567 }
256 Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))568 throw!(IntrinsicNotFound(name.into()))
257 })?,
258 // faster
259 "format" => parse_args!(context, "format", args, 2, [
260 0, str: ty!(str) => Val::Str;
261 1, vals: ty!(any)
262 ], {
263 std_format(str, vals)
264 })?,
265 "range" => parse_args!(context, "range", args, 2, [
266 0, from: ty!(num) => Val::Num;
267 1, to: ty!(num) => Val::Num;
268 ], {
269 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));
270 for i in from as usize..=to as usize {
271 out.push(Val::Num(i as f64));
272 }
273 Ok(Val::Arr(out.into()))
274 })?,
275 "char" => parse_args!(context, "char", args, 1, [
276 0, n: ty!(num) => Val::Num;
277 ], {
278 let mut out = String::new();
279 out.push(std::char::from_u32(n as u32).ok_or_else(||
280 InvalidUnicodeCodepointGot(n as u32)
281 )?);
282 Ok(Val::Str(out.into()))
283 })?,
284 "encodeUTF8" => parse_args!(context, "encodeUTF8", args, 1, [
285 0, str: ty!(str) => Val::Str;
286 ], {
287 Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))
288 })?,
289 "md5" => parse_args!(context, "md5", args, 1, [
290 0, str: ty!(str) => Val::Str;
291 ], {
292 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))
293 })?,
294 "base64" => parse_args!(context, "base64", args, 1, [
295 0, input: ty!((str | [num]));
296 ], {
297 Ok(Val::Str(match input {
298 Val::Str(s) => {
299 base64::encode(s.bytes().collect::<Vec<_>>()).into()
300 },
301 Val::Arr(a) => {
302 base64::encode(a.iter().map(|v| {
303 Ok(v?.clone().unwrap_num()? as u8)
304 }).collect::<Result<Vec<_>>>()?).into()
305 },
306 _ => unreachable!()
307 }))
308 })?,
309 "join" => parse_args!(context, "join", args, 2, [
310 0, sep: ty!((str | [any]));
311 1, arr: ty!([any]) => Val::Arr;
312 ], {
313 Ok(match sep {
314 Val::Arr(joiner_items) => {
315 let mut out = Vec::new();
316
317 let mut first = true;
318 for item in arr.iter() {
319 let item = item?.clone();
320 if let Val::Arr(items) = item {
321 if !first {
322 out.reserve(joiner_items.len());
323 // TODO: extend
324 for item in joiner_items.iter() {
325 out.push(item?);
326 }
327 }
328 first = false;
329 out.reserve(items.len());
330 // TODO: extend
331 for item in items.iter() {
332 out.push(item?);
333 }
334 } else {
335 throw!(RuntimeError("in std.join all items should be arrays".into()));
336 }
337 }
338
339 Val::Arr(out.into())
340 },
341 Val::Str(sep) => {
342 let mut out = String::new();
343
344 let mut first = true;
345 for item in arr.iter() {
346 let item = item?.clone();
347 if let Val::Str(item) = item {
348 if !first {
349 out += &sep;
350 }
351 first = false;
352 out += &item;
353 } else {
354 throw!(RuntimeError("in std.join all items should be strings".into()));
355 }
356 }
357
358 Val::Str(out.into())
359 },
360 _ => unreachable!()
361 })
362 })?,
363 // faster
364 "escapeStringJson" => parse_args!(context, "escapeStringJson", args, 1, [
365 0, str_: ty!(str) => Val::Str;
366 ], {
367 Ok(Val::Str(escape_string_json(&str_).into()))
368 })?,
369 // faster
370 "manifestJsonEx" => parse_args!(context, "manifestJsonEx", args, 2, [
371 0, value: ty!(any);
372 1, indent: ty!(str) => Val::Str;
373 ], {
374 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {
375 padding: &indent,
376 mtype: ManifestType::Std,
377 })?.into()))
378 })?,
379 // faster
380 "reverse" => parse_args!(context, "reverse", args, 1, [
381 0, value: ty!([any]) => Val::Arr;
382 ], {
383 Ok(Val::Arr(value.reversed()))
384 })?,
385 "id" => parse_args!(context, "id", args, 1, [
386 0, v: ty!(any);
387 ], {
388 Ok(v)
389 })?,
390 name => throw!(IntrinsicNotFound(name.into())),
391 })
392}569}
393570