@@ -490,6 +490,38 @@ ast_function_result safe_if_else(
490490 return evaluate (args[2 ], vars, funs);
491491 }
492492}
493+
494+ ast_function_result safe_object_access (
495+ std::span<const ast::node> args, const variable_registry& vars, const function_registry& funs) {
496+ if (args.size () != 2u ) {
497+ return unexpected (error{
498+ .message =
499+ " function takes 2 arguments, but " + std::to_string (args.size ()) + " provided" });
500+ }
501+
502+ // Evaluate object first.
503+ const auto lhs = evaluate (args[0 ], vars, funs);
504+ if (!lhs.has_value ()) {
505+ return unexpected (lhs.error ());
506+ }
507+
508+ if (!lhs.value ().is_object ()) {
509+ return unexpected (node_error (
510+ args[0 ], std::string (" expected object, got " ) +
511+ std::string (get_dynamic_type_name (lhs.value ()))));
512+ }
513+
514+ const auto & object = lhs.value ().get_ref <const object_t &>();
515+
516+ // Find specified field.
517+ const std::string field = std::string{std::get<ast::identifier>(args[1 ].content ).name };
518+ const auto iter = object.find (field);
519+ if (iter == object.end ()) {
520+ return unexpected (error{.message = " unknown field '" + field + " '" });
521+ }
522+
523+ return iter->second ;
524+ }
493525} // namespace
494526
495527void jsonexpr::impl::add_type (std::string& key, std::string_view type) {
@@ -627,6 +659,8 @@ function_registry make_default_functions() {
627659 register_function (freg, " [:]" , &safe_range_access<string_t , number_integer_t >);
628660 register_function (freg, " [:]" , &safe_range_access<array_t , number_integer_t >);
629661
662+ register_ast_function (freg, " ." , &safe_object_access);
663+
630664 register_function (freg, " in" , &safe_contains<true , number_integer_t , array_t >);
631665 register_function (freg, " in" , &safe_contains<true , number_float_t , array_t >);
632666 register_function (freg, " in" , &safe_contains<true , boolean_t , array_t >);
0 commit comments