1use crate::function_library::FunctionLibrary;
13use crate::profile::{ExprProfile, ExprRevision, HostContext, HostKind, ProfileKey};
14use std::collections::HashMap;
15use std::sync::{Arc, LazyLock, Mutex};
16
17pub(crate) static DEFAULT_LIBRARY: LazyLock<FunctionLibrary> = LazyLock::new(build_default_library);
18
19static PROFILE_CACHE: LazyLock<Mutex<HashMap<ProfileKey, Arc<FunctionLibrary>>>> =
28 LazyLock::new(|| Mutex::new(HashMap::new()));
29
30fn build_library_skeleton(profile: &ExprProfile) -> FunctionLibrary {
45 let lib = match profile.revision() {
46 ExprRevision::V2026_02 => {
47 build_default_library()
50 } };
54
55 #[allow(clippy::never_loop)]
68 for ext in profile.extensions() {
69 match *ext {} }
73
74 lib
75}
76
77impl FunctionLibrary {
78 pub fn for_profile(profile: &ExprProfile) -> Arc<FunctionLibrary> {
105 let key = profile.cache_key();
108 let cached = {
109 let mut cache = PROFILE_CACHE.lock().expect("profile cache mutex poisoned");
110 Arc::clone(cache.entry(key.clone()).or_insert_with(|| {
111 let mut skeleton = build_library_skeleton(profile);
116 if matches!(key.host_kind, HostKind::Unresolved) {
117 skeleton.host_context_enabled = true;
118 register_unresolved_host_context_functions(&mut skeleton);
119 }
120 Arc::new(skeleton)
121 }))
122 };
123
124 if let HostContext::WithRules(rules) = profile.host_context() {
128 let base_key = ProfileKey {
132 host_kind: HostKind::None,
133 ..key
134 };
135 let base = {
136 let mut cache = PROFILE_CACHE.lock().expect("profile cache mutex poisoned");
137 Arc::clone(
138 cache
139 .entry(base_key)
140 .or_insert_with(|| Arc::new(build_library_skeleton(profile))),
141 )
142 };
143 let mut derived = (*base).clone();
147 derived.host_context_enabled = true;
148 register_host_context_functions(&mut derived, Arc::clone(rules));
149 return Arc::new(derived);
150 }
151
152 cached
153 }
154}
155
156fn build_default_library() -> FunctionLibrary {
158 let lib = FunctionLibrary::new();
159 lib.merge(arithmetic())
160 .merge(string_ops())
161 .merge(list_ops())
162 .merge(comparison())
163 .merge(math_ops())
164 .merge(string_functions())
165 .merge(list_functions())
166 .merge(conversion())
167 .merge(path_ops())
168 .merge(repr_ops())
169 .merge(regex_ops())
170 .merge(misc())
171}
172
173fn arithmetic() -> FunctionLibrary {
174 use crate::functions::arithmetic::*;
175 let mut lib = FunctionLibrary::new();
176 lib.register_sig("__add__", "(int, int) -> int", add_int)
178 .expect("bad builtin signature");
179 lib.register_sig("__sub__", "(int, int) -> int", sub_int)
180 .expect("bad builtin signature");
181 lib.register_sig("__mul__", "(int, int) -> int", mul_int)
182 .expect("bad builtin signature");
183 lib.register_sig("__truediv__", "(int, int) -> float", truediv_int)
184 .expect("bad builtin signature");
185 lib.register_sig("__floordiv__", "(int, int) -> int", floordiv_int)
186 .expect("bad builtin signature");
187 lib.register_sig("__mod__", "(int, int) -> int", mod_int)
188 .expect("bad builtin signature");
189 lib.register_sig("__pow__", "(int, int) -> float | int", pow_int)
190 .expect("bad builtin signature");
191 lib.register_sig("__neg__", "(int) -> int", neg_int)
192 .expect("bad builtin signature");
193 lib.register_sig("__pos__", "(int) -> int", pos_int)
194 .expect("bad builtin signature");
195 lib.register_sig("__add__", "(float, float) -> float", add_float)
197 .expect("bad builtin signature");
198 lib.register_sig("__sub__", "(float, float) -> float", sub_float)
199 .expect("bad builtin signature");
200 lib.register_sig("__mul__", "(float, float) -> float", mul_float)
201 .expect("bad builtin signature");
202 lib.register_sig("__truediv__", "(float, float) -> float", truediv_float)
203 .expect("bad builtin signature");
204 lib.register_sig("__floordiv__", "(float, float) -> int", floordiv_float)
205 .expect("bad builtin signature");
206 lib.register_sig("__mod__", "(float, float) -> float", mod_float)
207 .expect("bad builtin signature");
208 lib.register_sig("__pow__", "(float, float) -> float", pow_float)
209 .expect("bad builtin signature");
210 lib.register_sig("__neg__", "(float) -> float", neg_float)
211 .expect("bad builtin signature");
212 lib.register_sig("__pos__", "(float) -> float", pos_float)
213 .expect("bad builtin signature");
214 lib
215}
216
217fn string_ops() -> FunctionLibrary {
218 use crate::functions::arithmetic::*;
219 let mut lib = FunctionLibrary::new();
220 lib.register_sig("__add__", "(string, string) -> string", add_string)
221 .expect("bad builtin signature");
222 lib.register_sig(
223 "__add__",
224 "(string, range_expr) -> string",
225 add_string_range,
226 )
227 .expect("bad builtin signature");
228 lib.register_sig(
229 "__add__",
230 "(range_expr, string) -> string",
231 add_range_string,
232 )
233 .expect("bad builtin signature");
234 lib.register_sig("__mul__", "(string, int) -> string", mul_string)
235 .expect("bad builtin signature");
236 lib
237}
238
239fn list_ops() -> FunctionLibrary {
240 use crate::functions::arithmetic::*;
241 let mut lib = FunctionLibrary::new();
242 lib.register_sig("__add__", "(list[T1], list[T2]) -> list[T3]", add_list_list)
243 .expect("bad builtin signature");
244 lib.register_sig(
245 "__add__",
246 "(range_expr, list[T1]) -> list[T2]",
247 add_range_list,
248 )
249 .expect("bad builtin signature");
250 lib.register_sig(
251 "__add__",
252 "(list[T1], range_expr) -> list[T2]",
253 add_list_range,
254 )
255 .expect("bad builtin signature");
256 lib.register_sig(
257 "__add__",
258 "(range_expr, range_expr) -> list[int]",
259 add_range_range,
260 )
261 .expect("bad builtin signature");
262 lib.register_sig("__mul__", "(list[T1], int) -> list[T1]", mul_list)
263 .expect("bad builtin signature");
264 lib.register_sig(
265 "__getitem__",
266 "(list[T1], int) -> T1",
267 crate::functions::misc::getitem_list,
268 )
269 .expect("bad builtin signature");
270 lib.register_sig(
271 "__getitem__",
272 "(string, int) -> string",
273 crate::functions::misc::getitem_string,
274 )
275 .expect("bad builtin signature");
276 lib.register_sig(
277 "__getitem__",
278 "(range_expr, int) -> int",
279 crate::functions::misc::getitem_range,
280 )
281 .expect("bad builtin signature");
282 lib
283}
284
285fn comparison() -> FunctionLibrary {
286 use crate::functions::arithmetic::not_bool;
287 use crate::functions::comparison::*;
288 let mut lib = FunctionLibrary::new();
289 lib.register_sig("__not__", "(bool) -> bool", not_bool)
290 .expect("bad builtin signature");
291 lib.register_sig("__eq__", "(T1, T2) -> bool", eq_generic)
293 .expect("bad builtin signature");
294 lib.register_sig("__ne__", "(T1, T2) -> bool", ne_generic)
295 .expect("bad builtin signature");
296 lib.register_sig("__lt__", "(T1, T2) -> bool", lt_generic)
297 .expect("bad builtin signature");
298 lib.register_sig("__le__", "(T1, T2) -> bool", le_generic)
299 .expect("bad builtin signature");
300 lib.register_sig("__gt__", "(T1, T2) -> bool", gt_generic)
301 .expect("bad builtin signature");
302 lib.register_sig("__ge__", "(T1, T2) -> bool", ge_generic)
303 .expect("bad builtin signature");
304 lib.register_sig("__contains__", "(list[T1], T1) -> bool", contains_list)
306 .expect("bad builtin signature");
307 lib.register_sig("__contains__", "(range_expr, int) -> bool", contains_range)
308 .expect("bad builtin signature");
309 lib.register_sig("__contains__", "(string, string) -> bool", contains_string)
310 .expect("bad builtin signature");
311 lib.register_sig(
312 "__not_contains__",
313 "(list[T1], T1) -> bool",
314 not_contains_list,
315 )
316 .expect("bad builtin signature");
317 lib.register_sig(
318 "__not_contains__",
319 "(range_expr, int) -> bool",
320 not_contains_range,
321 )
322 .expect("bad builtin signature");
323 lib.register_sig(
324 "__not_contains__",
325 "(string, string) -> bool",
326 not_contains_string,
327 )
328 .expect("bad builtin signature");
329 lib.register_sig(
331 "__getitem__",
332 "(list[T1], int | nulltype, int | nulltype, int | nulltype) -> list[T1]",
333 slice_list,
334 )
335 .expect("bad builtin signature");
336 lib.register_sig(
337 "__getitem__",
338 "(range_expr, int | nulltype, int | nulltype, int | nulltype) -> range_expr | list[int]",
339 slice_range,
340 )
341 .expect("bad builtin signature");
342 lib.register_sig(
343 "__getitem__",
344 "(string, int | nulltype, int | nulltype, int | nulltype) -> string",
345 slice_string,
346 )
347 .expect("bad builtin signature");
348 lib
349}
350
351fn math_ops() -> FunctionLibrary {
352 use crate::functions::math::*;
353 let mut lib = FunctionLibrary::new();
354 lib.register_sig("min", "(int, int) -> int", min_fn)
355 .expect("bad builtin signature");
356 lib.register_sig("min", "(float, float) -> float", min_fn)
357 .expect("bad builtin signature");
358 lib.register_sig("min", "(int, int, int) -> int", min_fn)
359 .expect("bad builtin signature");
360 lib.register_sig("min", "(float, float, float) -> float", min_fn)
361 .expect("bad builtin signature");
362 lib.register_sig("min", "(list[int]) -> int", min_fn)
363 .expect("bad builtin signature");
364 lib.register_sig("min", "(list[float]) -> float", min_fn)
365 .expect("bad builtin signature");
366 lib.register_sig("min", "(range_expr) -> int", min_fn)
367 .expect("bad builtin signature");
368 lib.register_sig("min", "(list[nulltype]) -> noreturn", min_fn)
369 .expect("bad builtin signature");
370 lib.register_sig("max", "(int, int) -> int", max_fn)
371 .expect("bad builtin signature");
372 lib.register_sig("max", "(float, float) -> float", max_fn)
373 .expect("bad builtin signature");
374 lib.register_sig("max", "(int, int, int) -> int", max_fn)
375 .expect("bad builtin signature");
376 lib.register_sig("max", "(float, float, float) -> float", max_fn)
377 .expect("bad builtin signature");
378 lib.register_sig("max", "(list[int]) -> int", max_fn)
379 .expect("bad builtin signature");
380 lib.register_sig("max", "(list[float]) -> float", max_fn)
381 .expect("bad builtin signature");
382 lib.register_sig("max", "(range_expr) -> int", max_fn)
383 .expect("bad builtin signature");
384 lib.register_sig("max", "(list[nulltype]) -> noreturn", max_fn)
385 .expect("bad builtin signature");
386 lib.register_sig("floor", "(int) -> int", floor_int)
387 .expect("bad builtin signature");
388 lib.register_sig("floor", "(float) -> int", floor_float)
389 .expect("bad builtin signature");
390 lib.register_sig("ceil", "(int) -> int", ceil_int)
391 .expect("bad builtin signature");
392 lib.register_sig("ceil", "(float) -> int", ceil_float)
393 .expect("bad builtin signature");
394 lib.register_sig("round", "(float) -> int", round_fn)
395 .expect("bad builtin signature");
396 lib.register_sig("round", "(float, int) -> float | int", round_fn)
397 .expect("bad builtin signature");
398 lib.register_sig("round", "(int, int) -> int", round_fn)
399 .expect("bad builtin signature");
400 lib.register_sig("sum", "(list[int]) -> int", sum_list)
401 .expect("bad builtin signature");
402 lib.register_sig("sum", "(list[float]) -> float", sum_list)
403 .expect("bad builtin signature");
404 lib.register_sig("sum", "(list[nulltype]) -> int", sum_list)
405 .expect("bad builtin signature");
406 lib.register_sig("sum", "(range_expr) -> int", sum_list)
407 .expect("bad builtin signature");
408 lib
409}
410
411fn string_functions() -> FunctionLibrary {
412 use crate::functions::string::*;
413 let mut lib = FunctionLibrary::new();
414 lib.register_sig("upper", "(string) -> string", upper_fn)
415 .expect("bad builtin signature");
416 lib.register_sig("lower", "(string) -> string", lower_fn)
417 .expect("bad builtin signature");
418 lib.register_sig("strip", "(string) -> string", strip_fn)
419 .expect("bad builtin signature");
420 lib.register_sig("strip", "(string, string) -> string", strip_fn)
421 .expect("bad builtin signature");
422 lib.register_sig("lstrip", "(string) -> string", lstrip_fn)
423 .expect("bad builtin signature");
424 lib.register_sig("lstrip", "(string, string) -> string", lstrip_fn)
425 .expect("bad builtin signature");
426 lib.register_sig("rstrip", "(string) -> string", rstrip_fn)
427 .expect("bad builtin signature");
428 lib.register_sig("rstrip", "(string, string) -> string", rstrip_fn)
429 .expect("bad builtin signature");
430 lib.register_sig("startswith", "(string, string) -> bool", startswith_fn)
431 .expect("bad builtin signature");
432 lib.register_sig("endswith", "(string, string) -> bool", endswith_fn)
433 .expect("bad builtin signature");
434 lib.register_sig("replace", "(string, string, string) -> string", replace_fn)
435 .expect("bad builtin signature");
436 lib.register_sig("split", "(string) -> list[string]", split_fn)
437 .expect("bad builtin signature");
438 lib.register_sig("split", "(string, string) -> list[string]", split_fn)
439 .expect("bad builtin signature");
440 lib.register_sig("split", "(string, string, int) -> list[string]", split_fn)
441 .expect("bad builtin signature");
442 lib.register_sig("rsplit", "(string) -> list[string]", rsplit_fn)
443 .expect("bad builtin signature");
444 lib.register_sig("rsplit", "(string, string) -> list[string]", rsplit_fn)
445 .expect("bad builtin signature");
446 lib.register_sig("rsplit", "(string, string, int) -> list[string]", rsplit_fn)
447 .expect("bad builtin signature");
448 lib.register_sig("find", "(string, string) -> int", find_fn)
449 .expect("bad builtin signature");
450 lib.register_sig("rfind", "(string, string) -> int", rfind_fn)
451 .expect("bad builtin signature");
452 lib.register_sig("index", "(string, string) -> int", index_fn)
453 .expect("bad builtin signature");
454 lib.register_sig("rindex", "(string, string) -> int", rindex_fn)
455 .expect("bad builtin signature");
456 lib.register_sig("count", "(string, string) -> int", count_fn)
457 .expect("bad builtin signature");
458 lib.register_sig(
459 "removeprefix",
460 "(string, string) -> string",
461 removeprefix_fn,
462 )
463 .expect("bad builtin signature");
464 lib.register_sig(
465 "removesuffix",
466 "(string, string) -> string",
467 removesuffix_fn,
468 )
469 .expect("bad builtin signature");
470 lib.register_sig("isdigit", "(string) -> bool", isdigit_fn)
471 .expect("bad builtin signature");
472 lib.register_sig("isalpha", "(string) -> bool", isalpha_fn)
473 .expect("bad builtin signature");
474 lib.register_sig("isalnum", "(string) -> bool", isalnum_fn)
475 .expect("bad builtin signature");
476 lib.register_sig("isspace", "(string) -> bool", isspace_fn)
477 .expect("bad builtin signature");
478 lib.register_sig("isupper", "(string) -> bool", isupper_fn)
479 .expect("bad builtin signature");
480 lib.register_sig("islower", "(string) -> bool", islower_fn)
481 .expect("bad builtin signature");
482 lib.register_sig("isascii", "(string) -> bool", isascii_fn)
483 .expect("bad builtin signature");
484 lib.register_sig("title", "(string) -> string", title_fn)
485 .expect("bad builtin signature");
486 lib.register_sig("capitalize", "(string) -> string", capitalize_fn)
487 .expect("bad builtin signature");
488 lib.register_sig("center", "(string, int) -> string", center_fn)
489 .expect("bad builtin signature");
490 lib.register_sig("ljust", "(string, int) -> string", ljust_fn)
491 .expect("bad builtin signature");
492 lib.register_sig("rjust", "(string, int) -> string", rjust_fn)
493 .expect("bad builtin signature");
494 lib
495}
496
497fn list_functions() -> FunctionLibrary {
498 use crate::functions::list::*;
499 let mut lib = FunctionLibrary::new();
500 lib.register_sig("sorted", "(list[T1]) -> list[T1]", sorted_fn)
501 .expect("bad builtin signature");
502 lib.register_sig("reversed", "(list[T1]) -> list[T1]", reversed_fn)
503 .expect("bad builtin signature");
504 lib.register_sig("unique", "(list[T1]) -> list[T1]", unique_fn)
505 .expect("bad builtin signature");
506 lib.register_sig("flatten", "(list[list[T1]]) -> list[T1]", flatten_fn)
507 .expect("bad builtin signature");
508 lib.register_sig("flatten", "(list[T1]) -> list[T1]", flatten_fn)
509 .expect("bad builtin signature");
510 lib.register_sig("flatten", "(list[nulltype]) -> list[nulltype]", flatten_fn)
511 .expect("bad builtin signature");
512 lib.register_sig("join", "(list[string], string) -> string", join_fn)
513 .expect("bad builtin signature");
514 lib.register_sig("join", "(list[path], string) -> string", join_fn)
515 .expect("bad builtin signature");
516 lib.register_sig("join", "(list[nulltype], string) -> string", join_fn)
517 .expect("bad builtin signature");
518 lib.register_sig("range", "(int) -> list[int]", range_fn)
519 .expect("bad builtin signature");
520 lib.register_sig("range", "(int, int) -> list[int]", range_fn)
521 .expect("bad builtin signature");
522 lib.register_sig("range", "(int, int, int) -> list[int]", range_fn)
523 .expect("bad builtin signature");
524 lib
525}
526
527fn conversion() -> FunctionLibrary {
528 use crate::functions::conversion::*;
529 let mut lib = FunctionLibrary::new();
530 lib.register_sig("int", "(int) -> int", int_from_int)
531 .expect("bad builtin signature");
532 lib.register_sig("int", "(float) -> int", int_from_float)
533 .expect("bad builtin signature");
534 lib.register_sig("int", "(string) -> int", int_from_string)
535 .expect("bad builtin signature");
536 lib.register_sig("float", "(float) -> float", float_from_float)
537 .expect("bad builtin signature");
538 lib.register_sig("float", "(int) -> float", float_from_int)
539 .expect("bad builtin signature");
540 lib.register_sig("float", "(string) -> float", float_from_string)
541 .expect("bad builtin signature");
542 lib.register_sig("string", "(int) -> string", string_fn)
543 .expect("bad builtin signature");
544 lib.register_sig("string", "(float) -> string", string_fn)
545 .expect("bad builtin signature");
546 lib.register_sig("string", "(bool) -> string", string_fn)
547 .expect("bad builtin signature");
548 lib.register_sig("string", "(string) -> string", string_fn)
549 .expect("bad builtin signature");
550 lib.register_sig("string", "(path) -> string", string_fn)
551 .expect("bad builtin signature");
552 lib.register_sig("string", "(nulltype) -> string", string_fn)
553 .expect("bad builtin signature");
554 lib.register_sig("string", "(list[T1]) -> string", string_fn)
555 .expect("bad builtin signature");
556 lib.register_sig("string", "(range_expr) -> string", string_fn)
557 .expect("bad builtin signature");
558 lib.register_sig("bool", "(bool) -> bool", bool_from_bool)
559 .expect("bad builtin signature");
560 lib.register_sig("bool", "(int) -> bool", bool_from_int)
561 .expect("bad builtin signature");
562 lib.register_sig("bool", "(float) -> bool", bool_from_float)
563 .expect("bad builtin signature");
564 lib.register_sig("bool", "(string) -> bool", bool_from_string)
565 .expect("bad builtin signature");
566 lib.register_sig("bool", "(nulltype) -> bool", bool_from_null)
567 .expect("bad builtin signature");
568 lib.register_sig("bool", "(path) -> noreturn", bool_from_path)
569 .expect("bad builtin signature");
570 lib.register_sig("bool", "(list[T]) -> noreturn", bool_from_list)
571 .expect("bad builtin signature");
572 lib.register_sig(
573 "list",
574 "(range_expr) -> list[int]",
575 crate::functions::list::list_from_range,
576 )
577 .expect("bad builtin signature");
578 lib.register_sig(
579 "range_expr",
580 "(string) -> range_expr",
581 crate::functions::list::range_expr_from_string,
582 )
583 .expect("bad builtin signature");
584 lib.register_sig(
585 "range_expr",
586 "(list[int]) -> range_expr",
587 crate::functions::list::range_expr_from_list,
588 )
589 .expect("bad builtin signature");
590 lib.register_sig(
591 "range_expr",
592 "(list[nulltype]) -> noreturn",
593 crate::functions::list::range_expr_from_empty_list,
594 )
595 .expect("bad builtin signature");
596 lib
597}
598
599fn path_ops() -> FunctionLibrary {
600 use crate::functions::arithmetic::*;
601 use crate::functions::path::*;
602 let mut lib = FunctionLibrary::new();
603 lib.register_sig("path", "(string) -> path", crate::functions::misc::path_fn)
604 .expect("bad builtin signature");
605 lib.register_sig(
606 "path",
607 "(list[string]) -> path",
608 crate::functions::misc::path_fn,
609 )
610 .expect("bad builtin signature");
611 lib.register_sig("__truediv__", "(path, string) -> path", path_div)
612 .expect("bad builtin signature");
613 lib.register_sig("__truediv__", "(path, path) -> path", path_div)
614 .expect("bad builtin signature");
615 lib.register_sig("__add__", "(path, string) -> path", add_path_string)
616 .expect("bad builtin signature");
617 lib.register_sig("as_posix", "(path) -> string", as_posix_fn)
618 .expect("bad builtin signature");
619 lib.register_sig("with_name", "(path, string) -> path", with_name_fn)
620 .expect("bad builtin signature");
621 lib.register_sig("with_stem", "(path, string) -> path", with_stem_fn)
622 .expect("bad builtin signature");
623 lib.register_sig("with_suffix", "(path, string) -> path", with_suffix_fn)
624 .expect("bad builtin signature");
625 lib.register_sig("with_number", "(path, int) -> path", with_number_fn)
626 .expect("bad builtin signature");
627 lib.register_sig("with_number", "(string, int) -> string", with_number_fn)
628 .expect("bad builtin signature");
629 lib.register_sig("is_absolute", "(path) -> bool", is_absolute_fn)
630 .expect("bad builtin signature");
631 lib.register_sig("is_relative_to", "(path, path) -> bool", is_relative_to_fn)
632 .expect("bad builtin signature");
633 lib.register_sig(
634 "is_relative_to",
635 "(path, string) -> bool",
636 is_relative_to_fn,
637 )
638 .expect("bad builtin signature");
639 lib.register_sig("relative_to", "(path, path) -> path", relative_to_fn)
640 .expect("bad builtin signature");
641 lib.register_sig("relative_to", "(path, string) -> path", relative_to_fn)
642 .expect("bad builtin signature");
643 lib.register_sig("__property_name__", "(path) -> string", prop_name)
648 .expect("bad builtin signature");
649 lib.register_sig("__property_stem__", "(path) -> string", prop_stem)
650 .expect("bad builtin signature");
651 lib.register_sig("__property_suffix__", "(path) -> string", prop_suffix)
652 .expect("bad builtin signature");
653 lib.register_sig(
654 "__property_suffixes__",
655 "(path) -> list[string]",
656 prop_suffixes,
657 )
658 .expect("bad builtin signature");
659 lib.register_sig("__property_parent__", "(path) -> path", prop_parent)
660 .expect("bad builtin signature");
661 lib.register_sig("__property_parts__", "(path) -> list[string]", prop_parts)
662 .expect("bad builtin signature");
663 lib
664}
665
666fn repr_ops() -> FunctionLibrary {
667 use crate::functions::repr::*;
668 let mut lib = FunctionLibrary::new();
669 for f in [
670 (
671 "repr_py",
672 repr_py_fn
673 as fn(
674 &mut dyn crate::function_library::EvalContext,
675 &[crate::value::ExprValue],
676 )
677 -> Result<crate::value::ExprValue, crate::error::ExpressionError>,
678 ),
679 ("repr_json", repr_json_fn),
680 ("repr_sh", repr_sh_fn),
681 ("repr_cmd", repr_cmd_fn),
682 ("repr_pwsh", repr_pwsh_fn),
683 ] {
684 lib.register_sig(f.0, "(int) -> string", f.1)
685 .expect("bad builtin signature");
686 lib.register_sig(f.0, "(float) -> string", f.1)
687 .expect("bad builtin signature");
688 lib.register_sig(f.0, "(string) -> string", f.1)
689 .expect("bad builtin signature");
690 lib.register_sig(f.0, "(bool) -> string", f.1)
691 .expect("bad builtin signature");
692 lib.register_sig(f.0, "(path) -> string", f.1)
693 .expect("bad builtin signature");
694 lib.register_sig(f.0, "(nulltype) -> string", f.1)
695 .expect("bad builtin signature");
696 lib.register_sig(f.0, "(list[T1]) -> string", f.1)
697 .expect("bad builtin signature");
698 lib.register_sig(f.0, "(range_expr) -> string", f.1)
699 .expect("bad builtin signature");
700 }
701 lib
702}
703
704fn regex_ops() -> FunctionLibrary {
705 use crate::functions::regex::*;
706 let mut lib = FunctionLibrary::new();
707 lib.register_sig("re_match", "(string, string) -> list[string]?", re_match_fn)
708 .expect("bad builtin signature");
709 lib.register_sig(
710 "re_search",
711 "(string, string) -> list[string]?",
712 re_search_fn,
713 )
714 .expect("bad builtin signature");
715 lib.register_sig(
716 "re_findall",
717 "(string, string) -> list[string]",
718 re_findall_fn,
719 )
720 .expect("bad builtin signature");
721 lib.register_sig(
722 "re_findall",
723 "(string, string) -> list[list[string]]",
724 re_findall_fn,
725 )
726 .expect("bad builtin signature");
727 lib.register_sig(
728 "re_sub",
729 "(string, string, string) -> string",
730 re_replace_fn,
731 )
732 .expect("bad builtin signature");
733 lib.register_sig("re_split", "(string, string) -> list[string]", re_split_fn)
734 .expect("bad builtin signature");
735 lib.register_sig(
736 "re_split",
737 "(string, string, int) -> list[string]",
738 re_split_fn,
739 )
740 .expect("bad builtin signature");
741 lib.register_sig("re_escape", "(string) -> string", re_escape_fn)
742 .expect("bad builtin signature");
743 lib
744}
745
746pub fn register_host_context_functions(
751 lib: &mut FunctionLibrary,
752 rules: std::sync::Arc<Vec<crate::path_mapping::PathMappingRule>>,
753) {
754 lib.register_sig(
755 "apply_path_mapping",
756 "(string) -> path",
757 crate::functions::path::make_apply_path_mapping_fn(rules),
758 )
759 .expect("bad builtin signature");
760}
761
762pub fn register_unresolved_host_context_functions(lib: &mut FunctionLibrary) {
763 fn unresolved_apply_path_mapping(
764 _ctx: &mut dyn crate::function_library::EvalContext,
765 _a: &[crate::ExprValue],
766 ) -> Result<crate::ExprValue, crate::ExpressionError> {
767 Ok(crate::ExprValue::Unresolved(crate::ExprType::PATH))
768 }
769 lib.register_sig(
770 "apply_path_mapping",
771 "(string) -> path",
772 unresolved_apply_path_mapping,
773 )
774 .expect("bad builtin signature");
775}
776
777fn misc() -> FunctionLibrary {
778 use crate::functions::misc::*;
779 let mut lib = FunctionLibrary::new();
780 lib.register_sig("fail", "(string) -> noreturn", fail_fn)
781 .expect("bad builtin signature");
782 lib.register_sig("zfill", "(string, int) -> string", zfill_fn)
783 .expect("bad builtin signature");
784 lib.register_sig("zfill", "(int, int) -> string", zfill_fn)
785 .expect("bad builtin signature");
786 lib.register_sig("zfill", "(float, int) -> string", zfill_fn)
787 .expect("bad builtin signature");
788 lib.register_sig("any", "(list[bool]) -> bool", any_fn)
789 .expect("bad builtin signature");
790 lib.register_sig("any", "(list[nulltype]) -> bool", any_fn)
791 .expect("bad builtin signature");
792 lib.register_sig("all", "(list[bool]) -> bool", all_fn)
793 .expect("bad builtin signature");
794 lib.register_sig("all", "(list[nulltype]) -> bool", all_fn)
795 .expect("bad builtin signature");
796 lib.register_sig("abs", "(int) -> int", abs_int)
797 .expect("bad builtin signature");
798 lib.register_sig("abs", "(float) -> float", abs_float)
799 .expect("bad builtin signature");
800 lib.register_sig("len", "(string) -> int", len_string)
801 .expect("bad builtin signature");
802 lib.register_sig("len", "(path) -> int", len_path)
803 .expect("bad builtin signature");
804 lib.register_sig("len", "(list[T1]) -> int", len_list)
805 .expect("bad builtin signature");
806 lib.register_sig("len", "(range_expr) -> int", len_range)
807 .expect("bad builtin signature");
808 lib
809}
810
811#[cfg(test)]
812mod tests {
813 use super::*;
817 use crate::types::ExprType;
818
819 #[test]
820 fn default_library_has_all_categories() {
821 let lib = &*DEFAULT_LIBRARY;
822 assert!(!lib.get_signatures("__add__").is_empty(), "arithmetic");
824 assert!(!lib.get_signatures("upper").is_empty(), "string functions");
825 assert!(!lib.get_signatures("sorted").is_empty(), "list functions");
826 assert!(!lib.get_signatures("__not__").is_empty(), "not operator");
827 assert!(!lib.get_signatures("abs").is_empty(), "math");
828 assert!(!lib.get_signatures("int").is_empty(), "conversion");
829 assert!(!lib.get_signatures("path").is_empty(), "path");
830 assert!(!lib.get_signatures("repr_py").is_empty(), "repr");
831 assert!(!lib.get_signatures("re_match").is_empty(), "regex");
832 assert!(!lib.get_signatures("fail").is_empty(), "misc");
833 }
834
835 #[test]
836 fn derive_return_type_add_int() {
837 let lib = &*DEFAULT_LIBRARY;
838 assert_eq!(
839 lib.derive_return_type("__add__", &[ExprType::INT, ExprType::INT]),
840 Some(ExprType::INT)
841 );
842 }
843
844 #[test]
845 fn derive_return_type_add_float_coercion() {
846 let lib = &*DEFAULT_LIBRARY;
847 assert_eq!(
848 lib.derive_return_type("__add__", &[ExprType::INT, ExprType::FLOAT]),
849 Some(ExprType::FLOAT)
850 );
851 }
852
853 #[test]
854 fn derive_return_type_getitem_generic() {
855 let lib = &*DEFAULT_LIBRARY;
856 assert_eq!(
857 lib.derive_return_type(
858 "__getitem__",
859 &[ExprType::list(ExprType::STRING), ExprType::INT]
860 ),
861 Some(ExprType::STRING)
862 );
863 }
864
865 #[test]
866 fn derive_return_type_sorted_generic() {
867 let lib = &*DEFAULT_LIBRARY;
868 assert_eq!(
869 lib.derive_return_type("sorted", &[ExprType::list(ExprType::INT)]),
870 Some(ExprType::list(ExprType::INT))
871 );
872 }
873
874 #[test]
875 fn derive_return_type_comparison_operators() {
876 let lib = &*DEFAULT_LIBRARY;
877 assert_eq!(
878 lib.derive_return_type("__eq__", &[ExprType::INT, ExprType::INT]),
879 Some(ExprType::BOOL)
880 );
881 assert_eq!(
882 lib.derive_return_type("__ne__", &[ExprType::STRING, ExprType::STRING]),
883 Some(ExprType::BOOL)
884 );
885 assert_eq!(
886 lib.derive_return_type("__lt__", &[ExprType::INT, ExprType::FLOAT]),
887 Some(ExprType::BOOL)
888 );
889 assert_eq!(
890 lib.derive_return_type("__ge__", &[ExprType::FLOAT, ExprType::INT]),
891 Some(ExprType::BOOL)
892 );
893 }
894
895 #[test]
896 fn derive_return_type_contains_operators() {
897 let lib = &*DEFAULT_LIBRARY;
898 assert_eq!(
899 lib.derive_return_type(
900 "__contains__",
901 &[ExprType::list(ExprType::INT), ExprType::INT]
902 ),
903 Some(ExprType::BOOL)
904 );
905 assert_eq!(
906 lib.derive_return_type("__contains__", &[ExprType::STRING, ExprType::STRING]),
907 Some(ExprType::BOOL)
908 );
909 assert_eq!(
910 lib.derive_return_type(
911 "__not_contains__",
912 &[ExprType::list(ExprType::STRING), ExprType::STRING]
913 ),
914 Some(ExprType::BOOL)
915 );
916 }
917
918 #[test]
919 fn derive_return_type_slice_operators() {
920 let lib = &*DEFAULT_LIBRARY;
921 assert_eq!(
922 lib.derive_return_type(
923 "__getitem__",
924 &[
925 ExprType::list(ExprType::INT),
926 ExprType::NULLTYPE,
927 ExprType::INT,
928 ExprType::NULLTYPE
929 ]
930 ),
931 Some(ExprType::list(ExprType::INT))
932 );
933 assert_eq!(
934 lib.derive_return_type(
935 "__getitem__",
936 &[
937 ExprType::STRING,
938 ExprType::INT,
939 ExprType::NULLTYPE,
940 ExprType::NULLTYPE
941 ]
942 ),
943 Some(ExprType::STRING)
944 );
945 }
946
947 #[test]
948 fn get_property_type_path() {
949 let lib = &*DEFAULT_LIBRARY;
950 assert_eq!(
951 lib.get_property_type(&ExprType::PATH, "name"),
952 Some(ExprType::STRING)
953 );
954 assert_eq!(
955 lib.get_property_type(&ExprType::PATH, "parent"),
956 Some(ExprType::PATH)
957 );
958 assert_eq!(
959 lib.get_property_type(&ExprType::PATH, "suffixes"),
960 Some(ExprType::list(ExprType::STRING))
961 );
962 assert_eq!(lib.get_property_type(&ExprType::INT, "name"), None);
963 }
964
965 #[test]
966 fn signature_count() {
967 let lib = &*DEFAULT_LIBRARY;
968 let total: usize = lib
969 .function_names()
970 .map(|n| lib.get_signatures(n).len())
971 .sum();
972 assert!(total >= 190, "total signatures: {total}");
973 }
974
975 #[test]
976 fn all_signatures_have_real_implementations() {
977 let st = crate::SymbolTable::new();
979 assert!(crate::ParsedExpression::new("1 + 2")
981 .and_then(|p| p.evaluate(&st))
982 .is_ok()); assert!(crate::ParsedExpression::new("'hello'.upper()")
984 .and_then(|p| p.evaluate(&st))
985 .is_ok()); assert!(crate::ParsedExpression::new("len([1,2,3])")
987 .and_then(|p| p.evaluate(&st))
988 .is_ok()); assert!(crate::ParsedExpression::new("abs(-5)")
990 .and_then(|p| p.evaluate(&st))
991 .is_ok()); assert!(crate::ParsedExpression::new("int('42')")
993 .and_then(|p| p.evaluate(&st))
994 .is_ok()); assert!(crate::ParsedExpression::new("repr_py(42)")
996 .and_then(|p| p.evaluate(&st))
997 .is_ok()); assert!(crate::ParsedExpression::new("1 == 1")
999 .and_then(|p| p.evaluate(&st))
1000 .is_ok()); assert!(crate::ParsedExpression::new("2 in [1,2,3]")
1002 .and_then(|p| p.evaluate(&st))
1003 .is_ok()); assert!(crate::ParsedExpression::new("[1,2,3][0]")
1005 .and_then(|p| p.evaluate(&st))
1006 .is_ok()); assert!(crate::ParsedExpression::new("sorted([3,1,2])")
1008 .and_then(|p| p.evaluate(&st))
1009 .is_ok()); assert!(crate::ParsedExpression::new("range(5)")
1011 .and_then(|p| p.evaluate(&st))
1012 .is_ok()); }
1014
1015 #[test]
1016 fn python_function_names_present() {
1017 let lib = &*DEFAULT_LIBRARY;
1018 let expected = vec![
1020 "__add__",
1021 "__sub__",
1022 "__mul__",
1023 "__truediv__",
1024 "__floordiv__",
1025 "__mod__",
1026 "__pow__",
1027 "__neg__",
1028 "__pos__",
1029 "__not__",
1030 "__eq__",
1031 "__ne__",
1032 "__lt__",
1033 "__le__",
1034 "__gt__",
1035 "__ge__",
1036 "__contains__",
1037 "__not_contains__",
1038 "__getitem__",
1039 "__property_name__",
1040 "__property_stem__",
1041 "__property_suffix__",
1042 "__property_suffixes__",
1043 "__property_parent__",
1044 "__property_parts__",
1045 "abs",
1046 "all",
1047 "any",
1048 "as_posix",
1049 "bool",
1050 "capitalize",
1051 "ceil",
1052 "center",
1053 "count",
1054 "endswith",
1055 "fail",
1056 "find",
1057 "flatten",
1058 "float",
1059 "floor",
1060 "index",
1061 "int",
1062 "is_absolute",
1063 "is_relative_to",
1064 "isalnum",
1065 "isalpha",
1066 "isascii",
1067 "isdigit",
1068 "islower",
1069 "isspace",
1070 "isupper",
1071 "join",
1072 "len",
1073 "list",
1074 "ljust",
1075 "lower",
1076 "lstrip",
1077 "max",
1078 "min",
1079 "path",
1080 "range",
1081 "range_expr",
1082 "re_escape",
1083 "re_findall",
1084 "re_match",
1085 "re_search",
1086 "re_split",
1087 "re_sub",
1088 "relative_to",
1089 "removeprefix",
1090 "removesuffix",
1091 "replace",
1092 "repr_cmd",
1093 "repr_json",
1094 "repr_py",
1095 "repr_pwsh",
1096 "repr_sh",
1097 "reversed",
1098 "rfind",
1099 "rindex",
1100 "rjust",
1101 "round",
1102 "rsplit",
1103 "rstrip",
1104 "sorted",
1105 "split",
1106 "startswith",
1107 "string",
1108 "strip",
1109 "sum",
1110 "title",
1111 "unique",
1112 "upper",
1113 "with_name",
1114 "with_number",
1115 "with_stem",
1116 "with_suffix",
1117 "zfill",
1118 ];
1119 for name in &expected {
1120 assert!(
1121 !lib.get_signatures(name).is_empty(),
1122 "Missing function: {name}"
1123 );
1124 }
1125 assert!(
1127 lib.get_signatures("apply_path_mapping").is_empty(),
1128 "apply_path_mapping should only be available with host context"
1129 );
1130 }
1131
1132 #[test]
1133 fn host_context_has_apply_path_mapping() {
1134 let lib = FunctionLibrary::for_profile(&ExprProfile::current().with_host_context(
1135 HostContext::with_rules(Vec::<crate::path_mapping::PathMappingRule>::new()),
1136 ));
1137 assert!(!lib.get_signatures("apply_path_mapping").is_empty());
1138 assert!(lib.host_context_enabled);
1139 }
1140}
1141
1142#[cfg(test)]
1143mod for_profile_tests {
1144 use crate::path_mapping::{PathFormat, PathMappingRule};
1149 use crate::profile::{ExprProfile, HostContext};
1150 use crate::FunctionLibrary;
1151
1152 #[test]
1153 fn default_profile_has_builtins() {
1154 let lib = FunctionLibrary::for_profile(&ExprProfile::current());
1155 assert!(!lib.get_signatures("__add__").is_empty());
1156 assert!(!lib.get_signatures("upper").is_empty());
1157 assert!(lib.get_signatures("apply_path_mapping").is_empty());
1159 assert!(!lib.host_context_enabled);
1160 }
1161
1162 #[test]
1163 fn unresolved_host_context_registers_stub() {
1164 let profile = ExprProfile::current().with_host_context(HostContext::Unresolved);
1165 let lib = FunctionLibrary::for_profile(&profile);
1166 assert!(!lib.get_signatures("apply_path_mapping").is_empty());
1167 assert!(lib.host_context_enabled);
1168 }
1169
1170 #[test]
1171 fn with_rules_host_context_registers_real_impl() {
1172 let rule = PathMappingRule {
1173 source_path_format: PathFormat::Posix,
1174 source_path: "/src".into(),
1175 destination_path: "/dst".into(),
1176 };
1177 let profile = ExprProfile::current().with_host_context(HostContext::with_rules(vec![rule]));
1178 let lib = FunctionLibrary::for_profile(&profile);
1179 assert!(!lib.get_signatures("apply_path_mapping").is_empty());
1180 assert!(lib.host_context_enabled);
1181 }
1182
1183 #[test]
1184 fn cache_returns_same_arc_for_none_profile() {
1185 let profile = ExprProfile::current();
1188 let a = FunctionLibrary::for_profile(&profile);
1189 let b = FunctionLibrary::for_profile(&profile);
1190 assert!(std::sync::Arc::ptr_eq(&a, &b));
1191 }
1192
1193 #[test]
1194 fn cache_returns_same_arc_for_unresolved_profile() {
1195 let profile = ExprProfile::current().with_host_context(HostContext::Unresolved);
1196 let a = FunctionLibrary::for_profile(&profile);
1197 let b = FunctionLibrary::for_profile(&profile);
1198 assert!(std::sync::Arc::ptr_eq(&a, &b));
1199 }
1200
1201 #[test]
1202 fn with_rules_does_not_cache_rules_variant() {
1203 let r1 = vec![PathMappingRule {
1206 source_path_format: PathFormat::Posix,
1207 source_path: "/a".into(),
1208 destination_path: "/b".into(),
1209 }];
1210 let r2 = vec![PathMappingRule {
1211 source_path_format: PathFormat::Posix,
1212 source_path: "/c".into(),
1213 destination_path: "/d".into(),
1214 }];
1215 let p1 = ExprProfile::current().with_host_context(HostContext::with_rules(r1));
1216 let p2 = ExprProfile::current().with_host_context(HostContext::with_rules(r2));
1217 let a = FunctionLibrary::for_profile(&p1);
1218 let b = FunctionLibrary::for_profile(&p2);
1219 assert!(!std::sync::Arc::ptr_eq(&a, &b));
1220 }
1221
1222 #[test]
1223 fn evaluator_works_with_for_profile_library() {
1224 use crate::{ExprValue, ParsedExpression, SymbolTable};
1227 let lib = FunctionLibrary::for_profile(&ExprProfile::current());
1228 let parsed = ParsedExpression::new("1 + 2 * 3").unwrap();
1229 let st = SymbolTable::new();
1230 let v = parsed.with_library(&lib).evaluate(&[&st]).unwrap();
1231 assert_eq!(v, ExprValue::Int(7));
1232 }
1233}